mirror of
https://github.com/gregtwallace/apc-p15-tool.git
synced 2025-01-22 16:14:09 +00:00
95 lines
3.1 KiB
Go
95 lines
3.1 KiB
Go
|
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
|
||
|
}
|