// Copyright 2016 - 2022 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 / XLSM / XLTM files. Supports reading and writing // spreadsheet documents generated by Microsoft Excelâ„¢ 2007 and later. Supports // complex components by high compatibility, and provided streaming API for // generating or reading data from a worksheet with huge amounts of data. This // library needs Go version 1.15 or later. package excelize import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/md5" "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/base64" "encoding/binary" "encoding/xml" "hash" "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 sheetProtectionSpinCount = 1e5 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 = ErrUnsupportedEncryptMechanism } 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 = ErrEncrypt 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 = ErrUnknownEncryptMechanism return } versionMajor, versionMinor := binary.LittleEndian.Uint16(buffer[: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 = ErrUnsupportedEncryptMechanism 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...) } else if len(key) > keyBytes { key = key[:keyBytes] } return } // hashing data by specified hash algorithm. func hashing(hashAlgorithm string, buffer ...[]byte) (key []byte) { 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, offset := encryption.KeyData, 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...) } else if len(iv) > encryptedKey.BlockSize { iv = iv[: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 } // ISO Write Protection Method // genISOPasswdHash implements the ISO password hashing algorithm by given // plaintext password, name of the cryptographic hash algorithm, salt value // and spin count. func genISOPasswdHash(passwd, hashAlgorithm, salt string, spinCount int) (hashValue, saltValue string, err error) { if len(passwd) < 1 || len(passwd) > MaxFieldLength { err = ErrPasswordLengthInvalid return } hash, ok := map[string]string{ "MD4": "md4", "MD5": "md5", "SHA-1": "sha1", "SHA-256": "sha256", "SHA-384": "sha384", "SHA-512": "sha512", }[hashAlgorithm] if !ok { err = ErrUnsupportedHashAlgorithm return } var b bytes.Buffer s, _ := randomBytes(16) if salt != "" { if s, err = base64.StdEncoding.DecodeString(salt); err != nil { return } } b.Write(s) encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder() passwordBuffer, _ := encoder.Bytes([]byte(passwd)) b.Write(passwordBuffer) // Generate the initial hash. key := hashing(hash, b.Bytes()) // Now regenerate until spin count. for i := 0; i < spinCount; i++ { iterator := createUInt32LEBuffer(i, 4) key = hashing(hash, key, iterator) } hashValue, saltValue = base64.StdEncoding.EncodeToString(key), base64.StdEncoding.EncodeToString(s) return }