123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- // Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
- // this source code is governed by a BSD-style license that can be found in
- // the LICENSE file.
- //
- // Package excelize providing a set of functions that allow you to write to
- // and read from XLSX files. Support reads and writes XLSX file generated by
- // Microsoft Excel™ 2007 and later. Support save file without losing original
- // charts of XLSX. This library needs Go version 1.15 or later.
- package excelize
- import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/hmac"
- "crypto/md5"
- "crypto/sha1"
- "crypto/sha256"
- "crypto/sha512"
- "encoding/base64"
- "encoding/binary"
- "encoding/xml"
- "errors"
- "hash"
- "math/rand"
- "reflect"
- "strings"
- "github.com/richardlehane/mscfb"
- "golang.org/x/crypto/md4"
- "golang.org/x/crypto/ripemd160"
- "golang.org/x/text/encoding/unicode"
- )
- var (
- blockKey = []byte{0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} // Block keys used for encryption
- blockKeyHmacKey = []byte{0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, 0xf6}
- blockKeyHmacValue = []byte{0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, 0x33}
- blockKeyVerifierHashInput = []byte{0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79}
- blockKeyVerifierHashValue = []byte{0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e}
- packageOffset = 8 // First 8 bytes are the size of the stream
- packageEncryptionChunkSize = 4096
- iterCount = 50000
- oleIdentifier = []byte{
- 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
- }
- )
- // Encryption specifies the encryption structure, streams, and storages are
- // required when encrypting ECMA-376 documents.
- type Encryption struct {
- XMLName xml.Name `xml:"encryption"`
- KeyData KeyData `xml:"keyData"`
- DataIntegrity DataIntegrity `xml:"dataIntegrity"`
- KeyEncryptors KeyEncryptors `xml:"keyEncryptors"`
- }
- // KeyData specifies the cryptographic attributes used to encrypt the data.
- type KeyData struct {
- SaltSize int `xml:"saltSize,attr"`
- BlockSize int `xml:"blockSize,attr"`
- KeyBits int `xml:"keyBits,attr"`
- HashSize int `xml:"hashSize,attr"`
- CipherAlgorithm string `xml:"cipherAlgorithm,attr"`
- CipherChaining string `xml:"cipherChaining,attr"`
- HashAlgorithm string `xml:"hashAlgorithm,attr"`
- SaltValue string `xml:"saltValue,attr"`
- }
- // DataIntegrity specifies the encrypted copies of the salt and hash values
- // used to help ensure that the integrity of the encrypted data has not been
- // compromised.
- type DataIntegrity struct {
- EncryptedHmacKey string `xml:"encryptedHmacKey,attr"`
- EncryptedHmacValue string `xml:"encryptedHmacValue,attr"`
- }
- // KeyEncryptors specifies the key encryptors used to encrypt the data.
- type KeyEncryptors struct {
- KeyEncryptor []KeyEncryptor `xml:"keyEncryptor"`
- }
- // KeyEncryptor specifies that the schema used by this encryptor is the schema
- // specified for password-based encryptors.
- type KeyEncryptor struct {
- XMLName xml.Name `xml:"keyEncryptor"`
- URI string `xml:"uri,attr"`
- EncryptedKey EncryptedKey `xml:"encryptedKey"`
- }
- // EncryptedKey used to generate the encrypting key.
- type EncryptedKey struct {
- XMLName xml.Name `xml:"http://schemas.microsoft.com/office/2006/keyEncryptor/password encryptedKey"`
- SpinCount int `xml:"spinCount,attr"`
- EncryptedVerifierHashInput string `xml:"encryptedVerifierHashInput,attr"`
- EncryptedVerifierHashValue string `xml:"encryptedVerifierHashValue,attr"`
- EncryptedKeyValue string `xml:"encryptedKeyValue,attr"`
- KeyData
- }
- // StandardEncryptionHeader structure is used by ECMA-376 document encryption
- // [ECMA-376] and Office binary document RC4 CryptoAPI encryption, to specify
- // encryption properties for an encrypted stream.
- type StandardEncryptionHeader struct {
- Flags uint32
- SizeExtra uint32
- AlgID uint32
- AlgIDHash uint32
- KeySize uint32
- ProviderType uint32
- Reserved1 uint32
- Reserved2 uint32
- CspName string
- }
- // StandardEncryptionVerifier structure is used by Office Binary Document RC4
- // CryptoAPI Encryption and ECMA-376 Document Encryption. Every usage of this
- // structure MUST specify the hashing algorithm and encryption algorithm used
- // in the EncryptionVerifier structure.
- type StandardEncryptionVerifier struct {
- SaltSize uint32
- Salt []byte
- EncryptedVerifier []byte
- VerifierHashSize uint32
- EncryptedVerifierHash []byte
- }
- // Decrypt API decrypt the CFB file format with ECMA-376 agile encryption and
- // standard encryption. Support cryptographic algorithm: MD4, MD5, RIPEMD-160,
- // SHA1, SHA256, SHA384 and SHA512 currently.
- func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {
- doc, err := mscfb.New(bytes.NewReader(raw))
- if err != nil {
- return
- }
- encryptionInfoBuf, encryptedPackageBuf := extractPart(doc)
- mechanism, err := encryptionMechanism(encryptionInfoBuf)
- if err != nil || mechanism == "extensible" {
- return
- }
- switch mechanism {
- case "agile":
- return agileDecrypt(encryptionInfoBuf, encryptedPackageBuf, opt)
- case "standard":
- return standardDecrypt(encryptionInfoBuf, encryptedPackageBuf, opt)
- default:
- err = errors.New("unsupport encryption mechanism")
- }
- return
- }
- // Encrypt API encrypt data with the password.
- func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {
- // Generate a random key to use to encrypt the document. Excel uses 32 bytes. We'll use the password to encrypt this key.
- packageKey, _ := randomBytes(32)
- keyDataSaltValue, _ := randomBytes(16)
- keyEncryptors, _ := randomBytes(16)
- encryptionInfo := Encryption{
- KeyData: KeyData{
- BlockSize: 16,
- KeyBits: len(packageKey) * 8,
- HashSize: 64,
- CipherAlgorithm: "AES",
- CipherChaining: "ChainingModeCBC",
- HashAlgorithm: "SHA512",
- SaltValue: base64.StdEncoding.EncodeToString(keyDataSaltValue),
- },
- KeyEncryptors: KeyEncryptors{KeyEncryptor: []KeyEncryptor{{
- EncryptedKey: EncryptedKey{SpinCount: 100000, KeyData: KeyData{
- CipherAlgorithm: "AES",
- CipherChaining: "ChainingModeCBC",
- HashAlgorithm: "SHA512",
- HashSize: 64,
- BlockSize: 16,
- KeyBits: 256,
- SaltValue: base64.StdEncoding.EncodeToString(keyEncryptors)},
- }}},
- },
- }
- // Package Encryption
- // Encrypt package using the package key.
- encryptedPackage, err := cryptPackage(true, packageKey, raw, encryptionInfo)
- if err != nil {
- return
- }
- // Data Integrity
- // Create the data integrity fields used by clients for integrity checks.
- // Generate a random array of bytes to use in HMAC. The docs say to use the same length as the key salt, but Excel seems to use 64.
- hmacKey, _ := randomBytes(64)
- if err != nil {
- return
- }
- // Create an initialization vector using the package encryption info and the appropriate block key.
- hmacKeyIV, err := createIV(blockKeyHmacKey, encryptionInfo)
- if err != nil {
- return
- }
- // Use the package key and the IV to encrypt the HMAC key.
- encryptedHmacKey, _ := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacKeyIV, hmacKey)
- // Create the HMAC.
- h := hmac.New(sha512.New, append(hmacKey, encryptedPackage...))
- for _, buf := range [][]byte{hmacKey, encryptedPackage} {
- _, _ = h.Write(buf)
- }
- hmacValue := h.Sum(nil)
- // Generate an initialization vector for encrypting the resulting HMAC value.
- hmacValueIV, err := createIV(blockKeyHmacValue, encryptionInfo)
- if err != nil {
- return
- }
- // Encrypt the value.
- encryptedHmacValue, _ := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacValueIV, hmacValue)
- // Put the encrypted key and value on the encryption info.
- encryptionInfo.DataIntegrity.EncryptedHmacKey = base64.StdEncoding.EncodeToString(encryptedHmacKey)
- encryptionInfo.DataIntegrity.EncryptedHmacValue = base64.StdEncoding.EncodeToString(encryptedHmacValue)
- // Key Encryption
- // Convert the password to an encryption key.
- key, err := convertPasswdToKey(opt.Password, blockKey, encryptionInfo)
- if err != nil {
- return
- }
- // Encrypt the package key with the encryption key.
- encryptedKeyValue, _ := crypt(true, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherAlgorithm, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherChaining, key, keyEncryptors, packageKey)
- encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.EncryptedKeyValue = base64.StdEncoding.EncodeToString(encryptedKeyValue)
- // Verifier hash
- // Create a random byte array for hashing.
- verifierHashInput, _ := randomBytes(16)
- // Create an encryption key from the password for the input.
- verifierHashInputKey, err := convertPasswdToKey(opt.Password, blockKeyVerifierHashInput, encryptionInfo)
- if err != nil {
- return
- }
- // Use the key to encrypt the verifier input.
- encryptedVerifierHashInput, err := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, verifierHashInputKey, keyEncryptors, verifierHashInput)
- if err != nil {
- return
- }
- encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.EncryptedVerifierHashInput = base64.StdEncoding.EncodeToString(encryptedVerifierHashInput)
- // Create a hash of the input.
- verifierHashValue := hashing(encryptionInfo.KeyData.HashAlgorithm, verifierHashInput)
- // Create an encryption key from the password for the hash.
- verifierHashValueKey, err := convertPasswdToKey(opt.Password, blockKeyVerifierHashValue, encryptionInfo)
- if err != nil {
- return
- }
- // Use the key to encrypt the hash value.
- encryptedVerifierHashValue, err := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, verifierHashValueKey, keyEncryptors, verifierHashValue)
- if err != nil {
- return
- }
- encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.EncryptedVerifierHashValue = base64.StdEncoding.EncodeToString(encryptedVerifierHashValue)
- // Marshal the encryption info buffer.
- encryptionInfoBuffer, err := xml.Marshal(encryptionInfo)
- if err != nil {
- return
- }
- // TODO: Create a new CFB.
- _, _ = encryptedPackage, encryptionInfoBuffer
- err = errors.New("not support encryption currently")
- return
- }
- // extractPart extract data from storage by specified part name.
- func extractPart(doc *mscfb.Reader) (encryptionInfoBuf, encryptedPackageBuf []byte) {
- for entry, err := doc.Next(); err == nil; entry, err = doc.Next() {
- switch entry.Name {
- case "EncryptionInfo":
- buf := make([]byte, entry.Size)
- i, _ := doc.Read(buf)
- if i > 0 {
- encryptionInfoBuf = buf
- }
- case "EncryptedPackage":
- buf := make([]byte, entry.Size)
- i, _ := doc.Read(buf)
- if i > 0 {
- encryptedPackageBuf = buf
- }
- }
- }
- return
- }
- // encryptionMechanism parse password-protected documents created mechanism.
- func encryptionMechanism(buffer []byte) (mechanism string, err error) {
- if len(buffer) < 4 {
- err = errors.New("unknown encryption mechanism")
- return
- }
- versionMajor, versionMinor := binary.LittleEndian.Uint16(buffer[0:2]), binary.LittleEndian.Uint16(buffer[2:4])
- if versionMajor == 4 && versionMinor == 4 {
- mechanism = "agile"
- return
- } else if (2 <= versionMajor && versionMajor <= 4) && versionMinor == 2 {
- mechanism = "standard"
- return
- } else if (versionMajor == 3 || versionMajor == 4) && versionMinor == 3 {
- mechanism = "extensible"
- }
- err = errors.New("unsupport encryption mechanism")
- return
- }
- // ECMA-376 Standard Encryption
- // standardDecrypt decrypt the CFB file format with ECMA-376 standard encryption.
- func standardDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opt *Options) ([]byte, error) {
- encryptionHeaderSize := binary.LittleEndian.Uint32(encryptionInfoBuf[8:12])
- block := encryptionInfoBuf[12 : 12+encryptionHeaderSize]
- header := StandardEncryptionHeader{
- Flags: binary.LittleEndian.Uint32(block[:4]),
- SizeExtra: binary.LittleEndian.Uint32(block[4:8]),
- AlgID: binary.LittleEndian.Uint32(block[8:12]),
- AlgIDHash: binary.LittleEndian.Uint32(block[12:16]),
- KeySize: binary.LittleEndian.Uint32(block[16:20]),
- ProviderType: binary.LittleEndian.Uint32(block[20:24]),
- Reserved1: binary.LittleEndian.Uint32(block[24:28]),
- Reserved2: binary.LittleEndian.Uint32(block[28:32]),
- CspName: string(block[32:]),
- }
- block = encryptionInfoBuf[12+encryptionHeaderSize:]
- algIDMap := map[uint32]string{
- 0x0000660E: "AES-128",
- 0x0000660F: "AES-192",
- 0x00006610: "AES-256",
- }
- algorithm := "AES"
- _, ok := algIDMap[header.AlgID]
- if !ok {
- algorithm = "RC4"
- }
- verifier := standardEncryptionVerifier(algorithm, block)
- secretKey, err := standardConvertPasswdToKey(header, verifier, opt)
- if err != nil {
- return nil, err
- }
- // decrypted data
- x := encryptedPackageBuf[8:]
- blob, err := aes.NewCipher(secretKey)
- if err != nil {
- return nil, err
- }
- decrypted := make([]byte, len(x))
- size := 16
- for bs, be := 0, size; bs < len(x); bs, be = bs+size, be+size {
- blob.Decrypt(decrypted[bs:be], x[bs:be])
- }
- return decrypted, err
- }
- // standardEncryptionVerifier extract ECMA-376 standard encryption verifier.
- func standardEncryptionVerifier(algorithm string, blob []byte) StandardEncryptionVerifier {
- verifier := StandardEncryptionVerifier{
- SaltSize: binary.LittleEndian.Uint32(blob[:4]),
- Salt: blob[4:20],
- EncryptedVerifier: blob[20:36],
- VerifierHashSize: binary.LittleEndian.Uint32(blob[36:40]),
- }
- if algorithm == "RC4" {
- verifier.EncryptedVerifierHash = blob[40:60]
- } else if algorithm == "AES" {
- verifier.EncryptedVerifierHash = blob[40:72]
- }
- return verifier
- }
- // standardConvertPasswdToKey generate intermediate key from given password.
- func standardConvertPasswdToKey(header StandardEncryptionHeader, verifier StandardEncryptionVerifier, opt *Options) ([]byte, error) {
- encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
- passwordBuffer, err := encoder.Bytes([]byte(opt.Password))
- if err != nil {
- return nil, err
- }
- key := hashing("sha1", verifier.Salt, passwordBuffer)
- for i := 0; i < iterCount; i++ {
- iterator := createUInt32LEBuffer(i, 4)
- key = hashing("sha1", iterator, key)
- }
- var block int
- hfinal := hashing("sha1", key, createUInt32LEBuffer(block, 4))
- cbRequiredKeyLength := int(header.KeySize) / 8
- cbHash := sha1.Size
- buf1 := bytes.Repeat([]byte{0x36}, 64)
- buf1 = append(standardXORBytes(hfinal, buf1[:cbHash]), buf1[cbHash:]...)
- x1 := hashing("sha1", buf1)
- buf2 := bytes.Repeat([]byte{0x5c}, 64)
- buf2 = append(standardXORBytes(hfinal, buf2[:cbHash]), buf2[cbHash:]...)
- x2 := hashing("sha1", buf2)
- x3 := append(x1, x2...)
- keyDerived := x3[:cbRequiredKeyLength]
- return keyDerived, err
- }
- // standardXORBytes perform XOR operations for two bytes slice.
- func standardXORBytes(a, b []byte) []byte {
- r := make([][2]byte, len(a))
- for i, e := range a {
- r[i] = [2]byte{e, b[i]}
- }
- buf := make([]byte, len(a))
- for p, q := range r {
- buf[p] = q[0] ^ q[1]
- }
- return buf
- }
- // ECMA-376 Agile Encryption
- // agileDecrypt decrypt the CFB file format with ECMA-376 agile encryption.
- // Support cryptographic algorithm: MD4, MD5, RIPEMD-160, SHA1, SHA256, SHA384 and SHA512.
- func agileDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opt *Options) (packageBuf []byte, err error) {
- var encryptionInfo Encryption
- if encryptionInfo, err = parseEncryptionInfo(encryptionInfoBuf[8:]); err != nil {
- return
- }
- // Convert the password into an encryption key.
- key, err := convertPasswdToKey(opt.Password, blockKey, encryptionInfo)
- if err != nil {
- return
- }
- // Use the key to decrypt the package key.
- encryptedKey := encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey
- saltValue, err := base64.StdEncoding.DecodeString(encryptedKey.SaltValue)
- if err != nil {
- return
- }
- encryptedKeyValue, err := base64.StdEncoding.DecodeString(encryptedKey.EncryptedKeyValue)
- if err != nil {
- return
- }
- packageKey, _ := crypt(false, encryptedKey.CipherAlgorithm, encryptedKey.CipherChaining, key, saltValue, encryptedKeyValue)
- // Use the package key to decrypt the package.
- return cryptPackage(false, packageKey, encryptedPackageBuf, encryptionInfo)
- }
- // convertPasswdToKey convert the password into an encryption key.
- func convertPasswdToKey(passwd string, blockKey []byte, encryption Encryption) (key []byte, err error) {
- var b bytes.Buffer
- saltValue, err := base64.StdEncoding.DecodeString(encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.SaltValue)
- if err != nil {
- return
- }
- b.Write(saltValue)
- encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
- passwordBuffer, err := encoder.Bytes([]byte(passwd))
- if err != nil {
- return
- }
- b.Write(passwordBuffer)
- // Generate the initial hash.
- key = hashing(encryption.KeyData.HashAlgorithm, b.Bytes())
- // Now regenerate until spin count.
- for i := 0; i < encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.SpinCount; i++ {
- iterator := createUInt32LEBuffer(i, 4)
- key = hashing(encryption.KeyData.HashAlgorithm, iterator, key)
- }
- // Now generate the final hash.
- key = hashing(encryption.KeyData.HashAlgorithm, key, blockKey)
- // Truncate or pad as needed to get to length of keyBits.
- keyBytes := encryption.KeyEncryptors.KeyEncryptor[0].EncryptedKey.KeyBits / 8
- if len(key) < keyBytes {
- tmp := make([]byte, 0x36)
- key = append(key, tmp...)
- key = tmp
- } else if len(key) > keyBytes {
- key = key[:keyBytes]
- }
- return
- }
- // hashing data by specified hash algorithm.
- func hashing(hashAlgorithm string, buffer ...[]byte) (key []byte) {
- var hashMap = map[string]hash.Hash{
- "md4": md4.New(),
- "md5": md5.New(),
- "ripemd-160": ripemd160.New(),
- "sha1": sha1.New(),
- "sha256": sha256.New(),
- "sha384": sha512.New384(),
- "sha512": sha512.New(),
- }
- handler, ok := hashMap[strings.ToLower(hashAlgorithm)]
- if !ok {
- return key
- }
- for _, buf := range buffer {
- _, _ = handler.Write(buf)
- }
- key = handler.Sum(nil)
- return key
- }
- // createUInt32LEBuffer create buffer with little endian 32-bit unsigned
- // integer.
- func createUInt32LEBuffer(value int, bufferSize int) []byte {
- buf := make([]byte, bufferSize)
- binary.LittleEndian.PutUint32(buf, uint32(value))
- return buf
- }
- // parseEncryptionInfo parse the encryption info XML into an object.
- func parseEncryptionInfo(encryptionInfo []byte) (encryption Encryption, err error) {
- err = xml.Unmarshal(encryptionInfo, &encryption)
- return
- }
- // crypt encrypt / decrypt input by given cipher algorithm, cipher chaining,
- // key and initialization vector.
- func crypt(encrypt bool, cipherAlgorithm, cipherChaining string, key, iv, input []byte) (packageKey []byte, err error) {
- block, err := aes.NewCipher(key)
- if err != nil {
- return input, err
- }
- var stream cipher.BlockMode
- if encrypt {
- stream = cipher.NewCBCEncrypter(block, iv)
- } else {
- stream = cipher.NewCBCDecrypter(block, iv)
- }
- stream.CryptBlocks(input, input)
- return input, nil
- }
- // cryptPackage encrypt / decrypt package by given packageKey and encryption
- // info.
- func cryptPackage(encrypt bool, packageKey, input []byte, encryption Encryption) (outputChunks []byte, err error) {
- encryptedKey := encryption.KeyData
- var offset = packageOffset
- if encrypt {
- offset = 0
- }
- var i, start, end int
- var iv, outputChunk []byte
- for end < len(input) {
- start = end
- end = start + packageEncryptionChunkSize
- if end > len(input) {
- end = len(input)
- }
- // Grab the next chunk
- var inputChunk []byte
- if (end + offset) < len(input) {
- inputChunk = input[start+offset : end+offset]
- } else {
- inputChunk = input[start+offset : end]
- }
- // Pad the chunk if it is not an integer multiple of the block size
- remainder := len(inputChunk) % encryptedKey.BlockSize
- if remainder != 0 {
- inputChunk = append(inputChunk, make([]byte, encryptedKey.BlockSize-remainder)...)
- }
- // Create the initialization vector
- iv, err = createIV(i, encryption)
- if err != nil {
- return
- }
- // Encrypt/decrypt the chunk and add it to the array
- outputChunk, err = crypt(encrypt, encryptedKey.CipherAlgorithm, encryptedKey.CipherChaining, packageKey, iv, inputChunk)
- if err != nil {
- return
- }
- outputChunks = append(outputChunks, outputChunk...)
- i++
- }
- if encrypt {
- outputChunks = append(createUInt32LEBuffer(len(input), 8), outputChunks...)
- }
- return
- }
- // createIV create an initialization vector (IV).
- func createIV(blockKey interface{}, encryption Encryption) ([]byte, error) {
- encryptedKey := encryption.KeyData
- // Create the block key from the current index
- var blockKeyBuf []byte
- if reflect.TypeOf(blockKey).Kind() == reflect.Int {
- blockKeyBuf = createUInt32LEBuffer(blockKey.(int), 4)
- } else {
- blockKeyBuf = blockKey.([]byte)
- }
- saltValue, err := base64.StdEncoding.DecodeString(encryptedKey.SaltValue)
- if err != nil {
- return nil, err
- }
- // Create the initialization vector by hashing the salt with the block key.
- // Truncate or pad as needed to meet the block size.
- iv := hashing(encryptedKey.HashAlgorithm, append(saltValue, blockKeyBuf...))
- if len(iv) < encryptedKey.BlockSize {
- tmp := make([]byte, 0x36)
- iv = append(iv, tmp...)
- iv = tmp
- } else if len(iv) > encryptedKey.BlockSize {
- iv = iv[0:encryptedKey.BlockSize]
- }
- return iv, nil
- }
- // randomBytes returns securely generated random bytes. It will return an error if the system's
- // secure random number generator fails to function correctly, in which case the caller should not
- // continue.
- func randomBytes(n int) ([]byte, error) {
- b := make([]byte, n)
- _, err := rand.Read(b)
- return b, err
- }
|