// implementation of https://tools.ietf.org/html/rfc2898#section-6.1.2
package pkcs12
import (
"bytes"
"crypto/cipher"
"crypto/des"
"crypto/rand"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"io"
"github.com/mitchellh/packer/builder/azure/pkcs12/rc2"
)
const (
pbeWithSHAAnd3KeyTripleDESCBC = "pbeWithSHAAnd3-KeyTripleDES-CBC"
pbewithSHAAnd40BitRC2CBC = "pbewithSHAAnd40BitRC2-CBC"
)
const (
pbeIterationCount = 2048
pbeSaltSizeBytes = 8
)
var (
oidPbeWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 3}
oidPbewithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 6}
)
var algByOID = map[string]string{
oidPbeWithSHAAnd3KeyTripleDESCBC.String(): pbeWithSHAAnd3KeyTripleDESCBC,
oidPbewithSHAAnd40BitRC2CBC.String(): pbewithSHAAnd40BitRC2CBC,
}
var blockcodeByAlg = map[string]func(key []byte) (cipher.Block, error){
pbeWithSHAAnd3KeyTripleDESCBC: des.NewTripleDESCipher,
pbewithSHAAnd40BitRC2CBC: func(key []byte) (cipher.Block, error) {
return rc2.New(key, len(key)*8)
},
}
type pbeParams struct {
Salt []byte
Iterations int
}
func pad(src []byte, blockSize int) []byte {
paddingLength := blockSize - len(src)%blockSize
paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength)
return append(src, paddingText...)
}
func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []byte, err error) {
_, err = io.ReadFull(rand.Reader, salt)
if err != nil {
return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error())
}
key := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations)
iv := deriveIVByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations)
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, errors.New("pkcs12: failed to create a block cipher: " + err.Error())
}
paddedPlainText := pad(plainText, block.BlockSize())
encrypter := cipher.NewCBCEncrypter(block, iv)
cipherText = make([]byte, len(paddedPlainText))
encrypter.CryptBlocks(cipherText, paddedPlainText)
return cipherText, nil
}
type decryptable interface {
GetAlgorithm() pkix.AlgorithmIdentifier
GetData() []byte
}