Added support for ECDH in enveloped data with ECDSA certificates.

- fixed parsing of choice(was compatible with Apple mail.app which tagged kari explicitly)
- minor fixes
This commit is contained in:
InfiniteLoopSpace
2018-12-10 17:18:29 +01:00
parent 58514b0041
commit 5f34d82562
15 changed files with 702 additions and 168 deletions

163
oid/key_wrap.go Normal file
View File

@ -0,0 +1,163 @@
package oid
import (
"crypto/aes"
"crypto/cipher"
"crypto/subtle"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"errors"
)
// KeyWrap wraps and unwraps key with the key encrytion key (KEK) for a given (KeyWrapAlgorithm)
type KeyWrap struct {
KEK []byte
KeyWrapAlgorithm asn1.ObjectIdentifier
}
// Wrap wraps the content encryption key (cek)
func (kw *KeyWrap) Wrap(cek []byte) (ciphertext []byte, err error) {
var block cipher.Block
switch kw.KeyWrapAlgorithm.String() {
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
block, err = aes.NewCipher(kw.KEK)
if err != nil {
return
}
}
return Wrap(block, cek)
}
// UnWrap unwraps the encrypted key (encKey)
func (kw *KeyWrap) UnWrap(encKey []byte) (cek []byte, err error) {
var block cipher.Block
switch kw.KeyWrapAlgorithm.String() {
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
block, err = aes.NewCipher(kw.KEK)
if err != nil {
return
}
}
return Unwrap(block, encKey)
}
// KeyLen returns the key lenght of the key wrap algorithm
func (kw *KeyWrap) KeyLen() (len int) {
switch kw.KeyWrapAlgorithm.String() {
case AES128Wrap.String():
len = 16
case AES192Wrap.String():
len = 24
case AES256Wrap.String():
len = 32
}
return
}
// AlgorithmIdentifier returns the OID of the key wrap algorithm
func (kw *KeyWrap) AlgorithmIdentifier() (algID pkix.AlgorithmIdentifier) {
switch kw.KeyWrapAlgorithm.String() {
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
algID = pkix.AlgorithmIdentifier{Algorithm: kw.KeyWrapAlgorithm}
}
return
}
// defaultIV from RFC-3394
var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}
// Wrap encrypts the content encryption key (cek) with the given AES cipher (block), using the AES Key Wrap algorithm (RFC-3394)
func Wrap(block cipher.Block, cek []byte) (encKey []byte, err error) {
if len(cek)%8 != 0 {
return nil, errors.New("Lenght of cek must be in 8-byte blocks")
}
// 1. Initialize variables
// Set A = IV, an initial value (see 2.2.3)
B := make([]byte, 16)
copy(B, defaultIV)
// For i = 1 to n
// R[i] = P[i]
encKey = make([]byte, len(cek)+8)
copy(encKey[8:], cek)
n := len(cek) / 8
// 2. Calculate intermediate values.
for j := 0; j <= 5; j++ {
for i := 1; i <= n; i++ {
// B = AES(K, A | R[i])
copy(B[8:], encKey[i*8:(i+1)*8])
block.Encrypt(B, B)
// A = MSB(64, B) ^ t where t = (n*j)+i
t := uint64(n*j + i)
b := binary.BigEndian.Uint64(B[:8]) ^ t
binary.BigEndian.PutUint64(B[:8], b)
// R[i] = LSB(64, B)
copy(encKey[i*8:(i+1)*8], B[8:])
}
}
// 3. Output the results.
copy(encKey[:8], B[:8])
return
}
// Unwrap decrypts the provided encrypted key (encKey) with the given AES cipher (block), using the AES Key Wrap algorithm (RFC-3394).
// Returns an error if validation fails.
func Unwrap(block cipher.Block, encKey []byte) (cek []byte, err error) {
if len(cek)%8 != 0 {
return nil, errors.New("Length of encKey must multiple 8-bytes")
}
//Initialize variables
B := make([]byte, 16)
copy(B, encKey[:8])
cek = make([]byte, len(encKey)-8)
copy(cek, encKey[8:])
n := (len(encKey) / 8) - 1
//Compute intermediate values
for j := 5; j >= 0; j-- {
for i := n; i >= 1; i-- {
// B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
copy(B[8:], cek[(i-1)*8:i*8])
t := uint64(n*j + i)
b := binary.BigEndian.Uint64(B[:8]) ^ t
binary.BigEndian.PutUint64(B[:8], b)
block.Decrypt(B, B)
// A = MSB(64, B)
// R[i] = LSB(64, B)
copy(cek[(i-1)*8:i*8], B[8:])
}
}
if subtle.ConstantTimeCompare(B[:8], defaultIV) != 1 {
return nil, errors.New("Integrity check failed - unexpected IV")
}
//Output
return
}

View File

@ -28,8 +28,13 @@ var (
// Signature Algorithm OIDs
var (
SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
SignatureAlgorithmECDSAwithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
SignatureAlgorithmECDSAwithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 1}
SignatureAlgorithmECDSAwithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
SignatureAlgorithmECDSAwithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
SignatureAlgorithmECDSAwithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
// Public Key Encryption OIDs
@ -51,6 +56,27 @@ var (
SubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
)
// Elliptic curve public key OID
var (
ECPublicKey = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
)
// DH Key Derivation Schemes OIDs
var (
DHSinglePassstdDHsha1kdfscheme = asn1.ObjectIdentifier{1, 3, 133, 16, 840, 63, 0, 2}
DHSinglePassstdDHsha224kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 0}
DHSinglePassstdDHsha256kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 1}
DHSinglePassstdDHsha384kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 2}
DHSinglePassstdDHsha512kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 3}
)
// Key wrap algorithm OIDs
var (
AES128Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 5}
AES192Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 25}
AES256Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 45}
)
// DigestAlgorithmToHash maps digest OIDs to crypto.Hash values.
var DigestAlgorithmToHash = map[string]crypto.Hash{
DigestAlgorithmSHA1.String(): crypto.SHA1,
@ -113,6 +139,18 @@ var SignatureAlgorithms = map[string]map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA384.String(): x509.ECDSAWithSHA384,
DigestAlgorithmSHA512.String(): x509.ECDSAWithSHA512,
},
SignatureAlgorithmECDSAwithSHA1.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA1.String(): x509.ECDSAWithSHA1,
},
SignatureAlgorithmECDSAwithSHA256.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA256.String(): x509.ECDSAWithSHA256,
},
SignatureAlgorithmECDSAwithSHA384.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA384.String(): x509.ECDSAWithSHA384,
},
SignatureAlgorithmECDSAwithSHA512.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA512.String(): x509.ECDSAWithSHA512,
},
}
// PublicKeyAlgorithmToSignatureAlgorithm maps certificate public key
@ -127,3 +165,12 @@ var PublicKeyAlgorithmToSignatureAlgorithm = map[x509.PublicKeyAlgorithm]pkix.Al
var PublicKeyAlgorithmToEncrytionAlgorithm = map[x509.PublicKeyAlgorithm]pkix.AlgorithmIdentifier{
x509.RSA: pkix.AlgorithmIdentifier{Algorithm: EncryptionAlgorithmRSA},
}
// KDFHashAlgorithm key derivation schemes to its hash algorithms
var KDFHashAlgorithm = map[string]crypto.Hash{
DHSinglePassstdDHsha1kdfscheme.String(): crypto.SHA1,
DHSinglePassstdDHsha224kdfscheme.String(): crypto.SHA224,
DHSinglePassstdDHsha256kdfscheme.String(): crypto.SHA256,
DHSinglePassstdDHsha384kdfscheme.String(): crypto.SHA384,
DHSinglePassstdDHsha512kdfscheme.String(): crypto.SHA512,
}

View File

@ -32,9 +32,10 @@ var (
AEADChaCha20Poly1305 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 18}
)
var symmetricKeyLen = map[string]int{
EncryptionAlgorithmDESCBC.String(): 7,
EncryptionAlgorithmDESEDE3CBC.String(): 21,
// SymmetricKeyLen maps the encryption algorithm to its key length
var SymmetricKeyLen = map[string]int{
EncryptionAlgorithmDESCBC.String(): 8,
EncryptionAlgorithmDESEDE3CBC.String(): 24,
EncryptionAlgorithmAES128CBC.String(): 16,
EncryptionAlgorithmAES256CBC.String(): 32,
//AEAD
@ -46,7 +47,7 @@ var symmetricKeyLen = map[string]int{
func (e *EncryptionAlgorithm) Encrypt(plaintext []byte) (ciphertext []byte, err error) {
if e.Key == nil {
e.Key = make([]byte, symmetricKeyLen[e.EncryptionAlgorithmIdentifier.String()])
e.Key = make([]byte, SymmetricKeyLen[e.EncryptionAlgorithmIdentifier.String()])
rand.Read(e.Key)
}
@ -71,7 +72,7 @@ func (e *EncryptionAlgorithm) Encrypt(plaintext []byte) (ciphertext []byte, err
switch e.EncryptionAlgorithmIdentifier.String() {
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String():
if e.IV == nil {
e.IV = make([]byte, len(e.Key))
e.IV = make([]byte, blockCipher.BlockSize())
rand.Read(e.IV)
}
@ -180,7 +181,6 @@ func (e *EncryptionAlgorithm) Decrypt(ciphertext []byte) (plaintext []byte, err
switch e.EncryptionAlgorithmIdentifier.String() {
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
e.IV = e.ContentEncryptionAlgorithmIdentifier.Parameters.Bytes
blockMode = cipher.NewCBCDecrypter(blockCipher, e.IV)
case EncryptionAlgorithmAES128GCM.String():
aead, err = cipher.NewGCM(blockCipher)
@ -198,7 +198,6 @@ func (e *EncryptionAlgorithm) Decrypt(ciphertext []byte) (plaintext []byte, err
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
plaintext = make([]byte, len(ciphertext))
blockMode.CryptBlocks(plaintext, ciphertext)
return unpad(plaintext, blockMode.BlockSize())
case EncryptionAlgorithmAES128GCM.String(), AEADChaCha20Poly1305.String():
var cipher []byte
@ -247,6 +246,9 @@ func unpad(data []byte, blocklen int) ([]byte, error) {
// the last byte is the length of padding
padlen := int(data[len(data)-1])
if padlen > blocklen {
return nil, fmt.Errorf("pad len %d is bigger than block len len %d", padlen, blocklen)
}
// check padding integrity, all bytes should be the same
pad := data[len(data)-padlen:]