// 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
}