From 4177c1585e312bee00c1592af3df6423c366e806 Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 1 Sep 2020 00:40:56 +0800 Subject: [PATCH] Resolve #199, init password protection spreadsheet support --- encrypt.go | 304 ++++++++++++++++++++++++++++++++++++++++++ excelize.go | 47 ++++--- excelize_test.go | 16 +-- go.mod | 19 ++- go.sum | 48 +++---- test/encryptSHA1.xlsx | Bin 0 -> 14336 bytes 6 files changed, 372 insertions(+), 62 deletions(-) create mode 100644 encrypt.go create mode 100644 test/encryptSHA1.xlsx diff --git a/encrypt.go b/encrypt.go new file mode 100644 index 0000000..e5dc2af --- /dev/null +++ b/encrypt.go @@ -0,0 +1,304 @@ +// Copyright 2016 - 2020 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.10 or later. + +package excelize + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "encoding/binary" + "encoding/xml" + "hash" + "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 + packageOffset = 8 // First 8 bytes are the size of the stream + packageEncryptionChunkSize = 4096 + cryptoIdentifier = []byte{ // checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier + 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, + 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + } +) + +// Encryption specifies the encryption structure, streams, and storages are +// required when encrypting ECMA-376 documents. +type Encryption struct { + 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 +} + +// Decrypt API decrypt the CFB file format with Agile Encryption. Support +// cryptographic algorithm: MD4, MD5, RIPEMD-160, SHA1, SHA256, SHA384 and +// SHA512. +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) + var encryptionInfo Encryption + if encryptionInfo, err = parseEncryptionInfo(encryptionInfoBuf[8:]); err != nil { + return + } + // Convert the password into an encryption key. + key, err := convertPasswdToKey(opt.Password, 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, err := crypt(false, encryptedKey.CipherAlgorithm, encryptedKey.CipherChaining, key, saltValue, encryptedKeyValue) + // Use the package key to decrypt the package. + return cryptPackage(false, packageKey, encryptedPackageBuf, encryptionInfo) +} + +// 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 + break + } + case "EncryptedPackage": + buf := make([]byte, entry.Size) + i, _ := doc.Read(buf) + if i > 0 { + encryptedPackageBuf = buf + break + } + } + } + return +} + +// convertPasswdToKey convert the password into an encryption key. +func convertPasswdToKey(passwd string, 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) + 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) []byte { + buf := make([]byte, 4) + 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 + } + 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(encrypt, 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++ + } + return +} + +// createIV create an initialization vector (IV). +func createIV(encrypt bool, blockKey int, encryption Encryption) ([]byte, error) { + encryptedKey := encryption.KeyData + // Create the block key from the current index + blockKeyBuf := createUInt32LEBuffer(blockKey) + var b bytes.Buffer + saltValue, err := base64.StdEncoding.DecodeString(encryptedKey.SaltValue) + if err != nil { + return nil, err + } + b.Write(saltValue) + b.Write(blockKeyBuf) + // 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, b.Bytes()) + 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 +} diff --git a/excelize.go b/excelize.go index 75b0c13..6e2b849 100644 --- a/excelize.go +++ b/excelize.go @@ -56,15 +56,30 @@ type File struct { type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error) -// OpenFile take the name of an spreadsheet file and returns a populated -// spreadsheet file struct for it. -func OpenFile(filename string) (*File, error) { +// Options define the options for open spreadsheet. +type Options struct { + Password string +} + +// OpenFile take the name of an spreadsheet file and returns a populated spreadsheet file struct +// for it. For example, open spreadsheet with password protection: +// +// f, err := excelize.OpenFile("Book1.xlsx", excelize.Options{Password: "password"}) +// if err != nil { +// return +// } +// +func OpenFile(filename string, opt ...Options) (*File, error) { file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() - f, err := OpenReader(file) + var option Options + for _, o := range opt { + option = o + } + f, err := OpenReader(file, option) if err != nil { return nil, err } @@ -91,25 +106,23 @@ func newFile() *File { // OpenReader read data stream from io.Reader and return a populated // spreadsheet file. -func OpenReader(r io.Reader) (*File, error) { +func OpenReader(r io.Reader, opt ...Options) (*File, error) { b, err := ioutil.ReadAll(r) if err != nil { return nil, err } - + if bytes.Contains(b, cryptoIdentifier) { + var option Options + for _, o := range opt { + option = o + } + b, err = Decrypt(b, &option) + if err != nil { + return nil, fmt.Errorf("decrypted file failed") + } + } zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b))) if err != nil { - identifier := []byte{ - // checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier - 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, - 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, - 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - } - if bytes.Contains(b, identifier) { - return nil, errors.New("not support encrypted file currently") - } return nil, err } diff --git a/excelize_test.go b/excelize_test.go index c2bd3ad..923e4c5 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -201,14 +201,14 @@ func TestCharsetTranscoder(t *testing.T) { func TestOpenReader(t *testing.T) { _, err := OpenReader(strings.NewReader("")) assert.EqualError(t, err, "zip: not a valid zip file") - _, err = OpenReader(bytes.NewReader([]byte{ - 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, - 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, - 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - })) - assert.EqualError(t, err, "not support encrypted file currently") + _, err = OpenReader(bytes.NewReader(cryptoIdentifier)) + assert.EqualError(t, err, "decrypted file failed") + + f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"}) + assert.NoError(t, err) + val, err := f.GetCellValue("Sheet1", "A1") + assert.NoError(t, err) + assert.Equal(t, "SECRET", val) // Test unexpected EOF. var b bytes.Buffer diff --git a/go.mod b/go.mod index f94f33b..9b01356 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,14 @@ module github.com/360EntSecGroup-Skylar/excelize/v2 -go 1.12 +go 1.15 require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/stretchr/testify v1.5.1 - github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91 - golang.org/x/image v0.0.0-20200430140353-33d19683fad8 - golang.org/x/net v0.0.0-20200506145744-7e3656a0809f - golang.org/x/text v0.3.2 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect + github.com/richardlehane/mscfb v1.0.3 + github.com/stretchr/testify v1.6.1 + github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf + golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a + golang.org/x/image v0.0.0-20200801110659-972c09e46d76 + golang.org/x/net v0.0.0-20200822124328-c89045814202 + golang.org/x/text v0.3.3 ) diff --git a/go.sum b/go.sum index 7fa49fe..bc606df 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,35 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI= +github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91 h1:gp02YctZuIPTk0t7qI+wvg3VQwTPyNmSGG6ZqOsjSL8= -github.com/xuri/efp v0.0.0-20191019043341-b7dc4fe9aa91/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf h1:spotWVWg9DP470pPFQ7LaYtUqDpWEOS/BUrSmwFZE4k= +github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20200801110659-972c09e46d76 h1:U7GPaoQyQmX+CBRWXKrvRzWTbd+slqeSh8uARsIyhAw= +golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/encryptSHA1.xlsx b/test/encryptSHA1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e7bc852fb729473c7c9c033d4aede26e48e8dc21 GIT binary patch literal 14336 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 literal 0 HcmV?d00001