Расчет sha256 дает разные результаты после добавления ломтиков в зависимости от того, распечатываю ли ломтик раньше или нет - PullRequest
1 голос
/ 22 января 2020

Я вычисляю sha256 из нескольких строк. Я конвертирую их в байтовые фрагменты определенным образом c и добавляю их все вместе, а затем вычисляю га sh, используя встроенную библиотеку. Однако, в зависимости от того, распечатываю ли я срез до вычисления sha256 или нет, я странно получаю разные результаты. При тестировании на детской площадке я не могу воспроизвести его.

Проверенный код можно увидеть и запустить на https://play.golang.org/p/z8XKx-p9huG, где он фактически дает одинаковый результат в обоих случаях.

func getHash(input1 string, input2hex string, input3hex string, input4 string) (string, error) {
    input1bytes := []byte(input1)
    firstHalfinput1Bytes := input1bytes[:8]
    secondHalfinput1Bytes := input1bytes[8:16]

    input4Bytes := []byte(input4)

    input3Bytes, err := hex.DecodeString(input3hex)
    if err != nil {
        fmt.Println("err " + err.Error())
    }

    input2Bytes, err := hex.DecodeString(input2hex)
    if err != nil {
        fmt.Println("err " + err.Error())
    }

    fullHashInputBytes := append(firstHalfinput1Bytes, input2Bytes...)
    // THIS IS THE OPTIONAL PRINT WHICH CHANGES OUTPUT LOCALLY:
    fmt.Println("fullHashInputBytes", fullHashInputBytes)
    fullHashInputBytes = append(fullHashInputBytes, secondHalfinput1Bytes...)
    fullHashInputBytes = append(fullHashInputBytes, input3Bytes...)
    fullHashInputBytes = append(fullHashInputBytes, input4Bytes...)
    sha256 := sha256.Sum256(fullHashInputBytes)
    for i := 0; i < 8; i++ {
        sha256[i+16] ^= sha256[i+24]
    }
    hash := hex.EncodeToString(sha256[:24])
    fmt.Println("hash", hash)
    return hash, nil
}

Журналы на игровой площадке:

Hello, playground
fullHashInputBytes [84 72 73 83 73 83 78 79 30 0 22 121 57 203 102 148 210 196 34 172 210 8 160 7]
hash 0161d9de8dd815ca9f4e1c7bb8684562542cc24b1026321c
hash 0161d9de8dd815ca9f4e1c7bb8684562542cc24b1026321c

, но если я запускаю ТОЧНО тот же код локально (просто скопируйте и вставьте его в основной файл. go и выполните go run main.go или go build . и ./test) Я получаю

Hello, playground
fullHashInputBytes [84 72 73 83 73 83 78 79 30 0 22 121 57 203 102 148 210 196 34 172 210 8 160 7]
hash 0161d9de8dd815ca9f4e1c7bb8684562542cc24b1026321c
hash d2de4ffb4e8790b8fd1ceeba726436fd97875a5740c27b47

Я использую go версию 1.13.4, но у меня была такая же проблема с 1.10.4. Я также получаю ту же проблему как на моем локальном компьютере, так и при развертывании на нашем сервере.

1 Ответ

9 голосов
/ 22 января 2020

Это потому, что вы создаете fullHashInputBytes, добавляя к firstHalfinput1Bytes first:

fullHashInputBytes := append(firstHalfinput1Bytes, input2Bytes...)

, который является частью input1bytes:

firstHalfinput1Bytes := input1bytes[:8]

Итак, первый append может перезаписать содержимое input1bytes с индексами выше 7, что на самом деле является содержимым secondHalfinput1Bytes:

secondHalfinput1Bytes := input1bytes[8:16]

Поэтому позже, когда вы также добавите secondHalfinput1Bytes к fullHashInputBytes, вы можете в итоге добавляем другое содержимое.

Скорее всего, это не то, что вам нужно.

Если вы сделаете это "чисто":

var fullHashInputBytes []byte
fullHashInputBytes = append(fullHashInputBytes, firstHalfinput1Bytes...)
fullHashInputBytes = append(fullHashInputBytes, input2Bytes...)
// OPTIONAL print doesn't change anything:
fmt.Println("fullHashInputBytes", fullHashInputBytes)
// ...rest of your appends...

Тогда вывод будет таким же если вы запускаете его локально или на Go Playground .

Почему девиантное поведение?

Зависит ли ваше первое добавление input1bytes от того, может ли добавление выполняется "на месте", без необходимости выделять новый резервный массив, который зависит от емкости firstHalfinput1Bytes, которая "наследуется" от input1bytes:

input1bytes := []byte(input1)
fmt.Println(cap(input1bytes))

(Вы можете прочитать Подробнее об этом здесь: Объединение двух фрагментов в Go).

Преобразование []byte(input) гарантирует только байты input1, но значение c не указывает, насколько большой должна быть емкость полученного среза. И это может зависеть от платформы / архитектуры. На игровой площадке Go приведенное выше преобразование приводит к capacity = 16, на моей локальной архитектуре amd64 я получаю capacity = 32.

Один последний кусок: емкость, используемая для среза результата []byte(input) преобразование может зависеть от того, что вы делаете с результирующим срезом. Компилятор может принять решение использовать меньшую емкость, если вы передадите ее fmt.Println(), так как это сигнализирует о том, что срез может исчезнуть. Опять же, решение, принятое компилятором, не в ваших руках.

Не полагайтесь на такие вещи, не пишите код, который опирается на емкость результирующего фрагмента преобразования. Сделайте это «чистым» способом: не добавляйте к firstHalfinput1Bytes, а к новому срезу.

...