add some KEK and CEK functions

This commit is contained in:
Greg T. Wallace 2024-01-21 15:11:22 -05:00
parent 2dd129c60a
commit 6610c92058
4 changed files with 122 additions and 0 deletions

94
cek.go Normal file
View file

@ -0,0 +1,94 @@
package main
import (
"crypto/cipher"
"crypto/des"
"errors"
"fmt"
)
// decryptCEK decrypts the encrypted CEK and unwraps the CEK so only the
// original CEK is returned
func decryptCEK(encryptedCEK, encryptedCekSalt, KEK []byte) (CEK []byte, err error) {
// ensure proper var lens, or error
encryptedCEKLen := 24
CEKSaltLen := 8
KEKLen := 24
if len(encryptedCEK) != encryptedCEKLen {
return nil, errors.New("wrong encrypted CEK length")
}
if len(encryptedCekSalt) != CEKSaltLen {
return nil, errors.New("wrong encrypted CEK's salt length")
}
if len(KEK) != KEKLen {
return nil, errors.New("wrong KEK length")
}
// 3DES uses block byte size of 8
blockByteSize := 8
// make DES cipher from KEK
kekDesCipher, err := des.NewTripleDESCipher(KEK)
if err != nil {
return nil, fmt.Errorf("failed to make DES cipher for cek decryption (%s)", err)
}
// (1) first use n-1'th block as IV to decrypt n'th block
ivStart := encryptedCEKLen - 2*blockByteSize
ivEnd := encryptedCEKLen - 1*blockByteSize
ivBlockCipherText := encryptedCEK[ivStart:ivEnd]
nthBlockCipherText := encryptedCEK[encryptedCEKLen-1*blockByteSize:]
firstBlockDecrypter := cipher.NewCBCDecrypter(kekDesCipher, ivBlockCipherText)
decryptedNthBlock := make([]byte, len(nthBlockCipherText))
firstBlockDecrypter.CryptBlocks(decryptedNthBlock, nthBlockCipherText)
// (2) decrypt remainder of outer encryption blocks (1 ... n-1'th) using
// the decrypted nthBlock as the IV
outerRemainderDecrypter := cipher.NewCBCDecrypter(kekDesCipher, decryptedNthBlock)
decryptedOuterRemainder := make([]byte, encryptedCEKLen-1*blockByteSize)
outerRemainderDecrypter.CryptBlocks(decryptedOuterRemainder, encryptedCEK[:encryptedCEKLen-1*blockByteSize])
// combine decrypted remainder with decrypted nth block for complete decrypted bytes
// this is equivelant to having the outer encryption removed, AKA the CEK is encrypted
// once now instead of twice
onceEncryptedCEK := append(decryptedOuterRemainder, decryptedNthBlock...)
// (3) Decrypted the inner layer of encryption using the KEK (aka decrypt the remaining
// layer of encryption)
// inner decrypter uses original CEK salt
innerDecrypter := cipher.NewCBCDecrypter(kekDesCipher, encryptedCekSalt)
// once decrypted, the CEK is still formatted as:
// CEK byte count || check value || CEK || padding (if required)
formattedCEK := make([]byte, len(onceEncryptedCEK))
innerDecrypter.CryptBlocks(formattedCEK, onceEncryptedCEK)
// Now that CEK is decrypted, sanity check it
// first byte is CEK byte count
expectedCEKLen := formattedCEK[0]
// (1a) expected cek len must be 16 or 24 or 3DES (which is what APC uses)
if int(expectedCEKLen) != 16 && int(expectedCEKLen) != 24 {
return nil, errors.New("expected CEK len block size is %d but 3DES requires 16 or 24 (decrypting likely failed)")
}
// next 3 bytes are the check value
CEKCheckVal := formattedCEK[1:4]
// CEK itself is the next bytes until CEK is the expected length
CEK = formattedCEK[4 : expectedCEKLen+4]
// (1b) key check data validation
if !isBitwiseCompliment(CEKCheckVal, CEK[0:3]) {
return nil, errors.New("CEK check value did not match CEK")
}
return CEK, nil
}

2
go.mod
View file

@ -3,3 +3,5 @@ module temp
go 1.21 go 1.21
require github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 require github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
require golang.org/x/crypto v0.18.0

2
go.sum
View file

@ -1,2 +1,4 @@
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=

24
kek.go Normal file
View file

@ -0,0 +1,24 @@
package main
import (
"crypto/sha256"
"golang.org/x/crypto/pbkdf2"
)
// makeKEK creates the APC KEK for a given Salt; APC uses a fixed
// password, iteration count, and hash function
func makeKEK(salt []byte) (KEK []byte) {
// password is known constant for APC files
password := "user"
// fixed values for APC files
iterations := 5000
hash := sha256.New
// size of 3DES key (k1 + k2 + k3)
size := 24
// kek
return pbkdf2.Key([]byte(password), salt, iterations, size, hash)
}