Обработка данных порциями с помощью io.ReadFull приводит к повреждению файла?

#go #encryption #http-live-streaming

#Вперед #шифрование #http-прямая трансляция

Вопрос:

Я пытаюсь загружать и расшифровывать потоки HLS, используя io.ReadFull для обработки данных порциями для экономии памяти:

Нерелевантные части кода были опущены для простоты.

 func main() {
    f, _ := os.Create(out.ts)

    for _, v := range mediaPlaylist {
        resp, _ := http.Get(v.URI)
        for {
            r, err := decryptHLS(key, iv, resp.Body)
            if err != nil amp;amp; err == io.EOF {
                break
            else if err != nil amp;amp; err != io.ErrUnexpectedEOF {
                panic(err)
            }
            io.Copy(f, r)
        }
    }
}

func decryptHLS(key []byte, iv []byte, r io.Reader) (io.Reader, error) {
    block, _ := aes.NewCipher(key)

    buf := make([]byte, 8192)

    mode := cipher.NewCBCDecrypter(block, iv)

        n, err := io.ReadFull(r, buf)
        if err != nil amp;amp; err != io.ErrUnexpectedEOF {
                return nil, err
        }

    mode.CryptBlocks(buf, buf)

    return bytes.NewReader(buf[:n]), err
}
  

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

Если я изменю код для использования ioutil.ReadAll вместо этого, конечные видеофайлы больше не будут повреждены:

 func main() {
    f, _ := os.Create(out.ts)

    for _, v := range mediaPlaylist {
        resp, _ := http.Get(v.URI)
        segment, _ := ioutil.ReadAll(resp.Body)
        r, _ := decryptHLS(key, iv, amp;segment)
        io.Copy(f, r)
    }
}

func decryptHLS(key []byte, iv []byte, s *[]byte) io.Reader {
    block, _ := aes.NewCipher(key)

    mode := cipher.NewCBCDecrypter(block, iv)

    mode.CryptBlocks(*s, *s)

    return bytes.NewReader(*s)
}
  

Есть идеи, почему он работает правильно при чтении всего сегмента в память, а не при использовании io.ReadFull и обработке его порциями?

Комментарии:

1. Вы запускаете новый CBCDecrypter с начальным IV значением для каждого блока 8K. Это не так, как работает CBC , IV for block n — это зашифрованный текст from block n-1 . У вас должен быть только один BlockMode объект для всего файла. Вам также необходимо проверить, что у вас есть полные блоки.

2. Я бы сказал, что основной причиной может быть то, что прочитанные фрагменты не обязательно соответствуют размеру блока. Я не вижу кода, обрабатывающего это. . кроме того (как уже отмечалось), вы должны управлять правильным IV и состоянием

Ответ №1:

Внутренне CBCDecrypter создает вашу копию iv , поэтому последующие блоки начинаются с начального IV, а не с того, который был изменен предыдущими расшифровками.

Создайте дешифратор один раз, и вы сможете продолжать повторно использовать его для дешифрования блока за блоком (при условии, что размер блока кратен размеру блока, ожидаемому этим криптоалгоритмом).