SecWrappedKeys.cpp [plain text]
#include "SecExternalRep.h"
#include "SecImportExportUtils.h"
#include "SecImportExportPem.h"
#include "SecImportExportCrypto.h"
#include <Security/cssmtype.h>
#include <Security/cssmapi.h>
#include <Security/SecKeyPriv.h>
#include <security_asn1/SecNssCoder.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_utilities/devrandom.h>
#include <assert.h>
using namespace Security;
using namespace KeychainCore;
static int hexToDigit(
char digit,
uint8 *rtn) {
if((digit >= '0') && (digit <= '9')) {
*rtn = digit - '0';
return 0;
}
if((digit >= 'a') && (digit <= 'f')) {
*rtn = digit - 'a' + 10;
return 0;
}
if((digit >= 'A') && (digit <= 'F')) {
*rtn = digit - 'A' + 10;
return 0;
}
return -1;
}
static int hexToUchar(
const char *cp,
uint8 *rtn) {
uint8 rtnc = 0;
uint8 c;
if(hexToDigit(*cp++, &c)) {
return -1;
}
rtnc = c << 4;
if(hexToDigit(*cp, &c)) {
return -1;
}
rtnc |= c;
*rtn = rtnc;
return 0;
}
static OSStatus opensslPbeParams(
CFArrayRef paramLines, SecNssCoder &coder,
CSSM_ALGORITHMS &pbeAlg,
CSSM_ALGORITHMS &keyAlg,
CSSM_ALGORITHMS &encrAlg,
CSSM_ENCRYPT_MODE &encrMode,
CSSM_PADDING &encrPad,
uint32 &keySizeInBits,
unsigned &blockSizeInBytes,
CSSM_DATA &iv)
{
if(paramLines == NULL) {
SecImpExpDbg("importWrappedKeyOpenssl: no PEM parameter lines");
return errSecUnknownFormat;
}
CFStringRef dekInfo = NULL;
CFIndex numLines = CFArrayGetCount(paramLines);
for(CFIndex dex=0; dex<numLines; dex++) {
CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(paramLines, dex);
CFRange range;
range = CFStringFind(str, CFSTR("DEK-Info: "), 0);
if(range.length != 0) {
dekInfo = str;
break;
}
}
if(dekInfo == NULL) {
SecImpExpDbg("importWrappedKeyOpenssl: no DEK-Info lines");
return errSecUnknownFormat;
}
char cstr[1024];
if(!CFStringGetCString(dekInfo, cstr, sizeof(cstr), kCFStringEncodingASCII)) {
SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (1)");
return errSecUnknownFormat;
}
char *cp = strchr(cstr, ':');
if(cp == NULL) {
SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (2)");
return errSecUnknownFormat;
}
if((cp[1] == ' ') && (cp[2] != '\0')) {
cp += 2;
}
if(!strncmp(cp, "DES-EDE3-CBC", 12)) {
keyAlg = CSSM_ALGID_3DES_3KEY;
encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
keySizeInBits = 64 * 3;
blockSizeInBytes = 8;
}
else if(!strncmp(cp, "DES-CBC", 7)) {
keyAlg = CSSM_ALGID_DES;
encrAlg = CSSM_ALGID_DES;
keySizeInBits = 64;
blockSizeInBytes = 8;
}
else {
SecImpExpDbg("importWrappedKeyOpenssl: unrecognized wrap alg (%s)",
cp);
return errSecUnknownFormat;
}
pbeAlg = CSSM_ALGID_PBE_OPENSSL_MD5;
encrMode = CSSM_ALGMODE_CBCPadIV8;
encrPad = CSSM_PADDING_PKCS7;
cp = strchr(cp, ',');
if(cp == NULL) {
SecImpExpDbg("importWrappedKeyOpenssl: No IV in DEK-Info line");
return errSecUnknownFormat;
}
if(cp[1] != '\0') {
cp++;
}
if(strlen(cp) != (blockSizeInBytes * 2)) {
SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (1)");
return errSecUnknownFormat;
}
coder.allocItem(iv, blockSizeInBytes);
for(unsigned dex=0; dex<blockSizeInBytes; dex++) {
if(hexToUchar(cp + (dex * 2), &iv.Data[dex])) {
SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (2)");
return errSecUnknownFormat;
}
}
return errSecSuccess;
}
static OSStatus deriveKeyOpensslWrap(
const SecKeyImportExportParameters *keyParams, CSSM_CSP_HANDLE cspHand, impExpVerifyPhrase vp, CSSM_ALGORITHMS pbeAlg,
CSSM_ALGORITHMS keyAlg,
uint32 keySizeInBits,
const CSSM_DATA &salt,
CSSM_KEY_PTR derivedKey)
{
CFDataRef cfPhrase = NULL;
CSSM_KEY *passKey = NULL;
OSStatus ortn;
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, vp,
(CFTypeRef *)&cfPhrase, &passKey);
if(ortn) {
return ortn;
}
CSSM_CRYPTO_DATA seed;
CSSM_CC_HANDLE ccHand = 0;
CSSM_ACCESS_CREDENTIALS creds;
SecNssCoder coder;
CSSM_DATA param = {0, NULL};
CSSM_DATA dummyLabel;
memset(&seed, 0, sizeof(seed));
if(cfPhrase != NULL) {
size_t len = CFDataGetLength(cfPhrase);
coder.allocItem(seed.Param, len);
memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len);
CFRelease(cfPhrase);
}
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
ortn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
pbeAlg,
keyAlg,
keySizeInBits,
&creds,
passKey, 1, &salt,
&seed,
&ccHand);
if(ortn) {
SecImpExpDbg("deriveKeyOpensslWrap: CSSM_CSP_CreateDeriveKeyContext error");
goto errOut;
}
memset(derivedKey, 0, sizeof(CSSM_KEY));
dummyLabel.Data = (uint8 *)"temp unwrap key";
dummyLabel.Length = strlen((char *)dummyLabel.Data);
ortn = CSSM_DeriveKey(ccHand,
¶m, CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
&dummyLabel,
NULL, derivedKey);
if(ortn) {
SecImpExpDbg("importWrappedKeyOpenssl: PKCS5 v1.5 CSSM_DeriveKey failure");
}
errOut:
if(ccHand != 0) {
CSSM_DeleteContext(ccHand);
}
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
return ortn;
}
OSStatus SecImportRep::importWrappedKeyOpenssl(
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, CFMutableArrayRef outArray) {
assert(mExternFormat == kSecFormatWrappedOpenSSL);
assert(mExternType == kSecItemTypePrivateKey);
assert(cspHand != 0);
if(keyParams == NULL) {
return errSecParam;
}
OSStatus ortn;
SecNssCoder coder;
impExpKeyUnwrapParams unwrapParams;
CSSM_ALGORITHMS pbeAlg = CSSM_ALGID_NONE;
CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE;
uint32 keySizeInBits;
unsigned blockSizeInBytes;
memset(&unwrapParams, 0, sizeof(unwrapParams));
ortn = opensslPbeParams(mPemParamLines, coder,
pbeAlg, keyAlg,
unwrapParams.encrAlg,
unwrapParams.encrMode,
unwrapParams.encrPad,
keySizeInBits,
blockSizeInBytes,
unwrapParams.iv);
if(ortn) {
return ortn;
}
CSSM_KEY unwrappingKey;
ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Import, pbeAlg, keyAlg,
keySizeInBits,
unwrapParams.iv,
&unwrappingKey);
if(ortn) {
return ortn;
}
CSSM_KEY wrappedKey;
CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader;
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr.BlobType = CSSM_KEYBLOB_WRAPPED;
hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL;
hdr.AlgorithmId = mKeyAlg;
hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
hdr.KeyUsage = CSSM_KEYUSE_ANY;
wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(mExternal);
wrappedKey.KeyData.Length = CFDataGetLength(mExternal);
unwrapParams.unwrappingKey = &unwrappingKey;
ortn = impExpImportKeyCommon(&wrappedKey, importKeychain, cspHand,
flags, keyParams, &unwrapParams, NULL, outArray);
if(unwrappingKey.KeyData.Data != NULL) {
CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE);
}
return ortn;
}
#define OPENSSL_WRAP_KEY_ALG CSSM_ALGID_3DES_3KEY
#define OPENSSL_WRAP_PBE_ALG CSSM_ALGID_PBE_OPENSSL_MD5
#define OPENSSL_WRAP_KEY_SIZE (64 * 3)
#define OPENSSL_WRAP_ENCR_ALG CSSM_ALGID_3DES_3KEY_EDE
#define OPENSSL_WRAP_ENCR_MODE CSSM_ALGMODE_CBCPadIV8
#define OPENSSL_WRAP_ENCR_PAD CSSM_PADDING_PKCS7
OSStatus impExpWrappedKeyOpenSslExport(
SecKeyRef secKey,
SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, CFMutableDataRef outData, const char **pemHeader, CFArrayRef *pemParamLines) {
DevRandomGenerator rng;
SecNssCoder coder;
CSSM_CSP_HANDLE cspHand = 0;
OSStatus ortn;
bool releaseCspHand = false;
CFMutableArrayRef paramLines;
CFStringRef cfStr;
char dekStr[100];
char ivStr[3];
if(keyParams == NULL) {
return errSecParam;
}
ortn = SecKeyGetCSPHandle(secKey, &cspHand);
if(ortn) {
cspHand = cuCspStartup(CSSM_FALSE);
if(cspHand == 0) {
return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
}
releaseCspHand = true;
}
uint8 saltIv[8];
CSSM_DATA saltIvData = { 8, saltIv} ;
rng.random(saltIv, 8);
CSSM_KEY wrappingKey;
wrappingKey.KeyData.Data = NULL;
wrappingKey.KeyData.Length = 0;
ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Export,
OPENSSL_WRAP_PBE_ALG, OPENSSL_WRAP_KEY_ALG,
OPENSSL_WRAP_KEY_SIZE,
saltIvData, &wrappingKey);
if(ortn) {
goto errOut;
}
CSSM_KEY wrappedKey;
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
ortn = impExpExportKeyCommon(cspHand, secKey, &wrappingKey, &wrappedKey,
OPENSSL_WRAP_ENCR_ALG, OPENSSL_WRAP_ENCR_MODE, OPENSSL_WRAP_ENCR_PAD,
CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL,
CSSM_ATTRIBUTE_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
NULL, &saltIvData);
if(ortn) {
goto errOut;
}
CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length);
switch(wrappedKey.KeyHeader.AlgorithmId) {
case CSSM_ALGID_RSA:
*pemHeader = PEM_STRING_RSA;
break;
case CSSM_ALGID_DH:
*pemHeader = PEM_STRING_DH_PRIVATE;
break;
case CSSM_ALGID_DSA:
*pemHeader = PEM_STRING_DSA;
break;
case CSSM_ALGID_ECDSA:
*pemHeader = PEM_STRING_ECDSA_PRIVATE;
break;
default:
SecImpExpDbg("impExpWrappedKeyOpenSslExport unknown private key alg "
"%lu", (unsigned long)wrappedKey.KeyHeader.AlgorithmId);
*pemHeader = "Private Key";
}
CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE);
assert(pemParamLines != NULL);
paramLines = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
cfStr = CFStringCreateWithCString(NULL,
"Proc-Type: 4,ENCRYPTED", kCFStringEncodingASCII);
CFArrayAppendValue(paramLines, cfStr);
CFRelease(cfStr); strcpy(dekStr, "DEK-Info: DES-EDE3-CBC,");
for(unsigned dex=0; dex<8; dex++) {
sprintf(ivStr, "%02X", saltIv[dex]);
strcat(dekStr, ivStr);
}
cfStr = CFStringCreateWithCString(NULL, dekStr, kCFStringEncodingASCII);
CFArrayAppendValue(paramLines, cfStr);
CFRelease(cfStr);
cfStr = CFStringCreateWithCString(NULL, "", kCFStringEncodingASCII);
CFArrayAppendValue(paramLines, cfStr);
CFRelease(cfStr); *pemParamLines = paramLines;
errOut:
if(wrappingKey.KeyData.Data != NULL) {
CSSM_FreeKey(cspHand, NULL, &wrappingKey, CSSM_FALSE);
}
return ortn;
}