/* * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * MuscleCardKeyHandle.cpp * TokendMuscle */ #include "MuscleCardKeyHandle.h" #include "KeyRecord.h" #include "Msc/MscError.h" #include "Msc/MscKey.h" #include "Msc/MscToken.h" #include #include #include #include #include using CssmClient::AclFactory; // // MuscleCardKeyHandle // MuscleCardKeyHandle::MuscleCardKeyHandle(const Tokend::MetaRecord &metaRecord, Tokend::Record &record, MscKey &key) : Tokend::KeyHandle(metaRecord, &record), mKey(key) { } MuscleCardKeyHandle::~MuscleCardKeyHandle() { } void MuscleCardKeyHandle::getKeySize(CSSM_KEY_SIZE &keySize) { secdebug("crypto", "getKeySize"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } uint32 MuscleCardKeyHandle::getOutputSize(const Context &context, uint32 inputSize, bool encrypting) { secdebug("crypto", "getOutputSize"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } static const MSCUChar8 sha1sigheader[] = { 0x30, // SEQUENCE 0x21, // LENGTH 0x30, // SEQUENCE 0x09, // LENGTH 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1a, // SHA1 OID (1 4 14 3 2 26) 0x05, 0x00, // OPTIONAL ANY algorithm params (NULL) 0x04, 0x14 // OCTECT STRING (20 bytes) }; static const MSCUChar8 md5sigheader[] = { 0x30, // SEQUENCE 0x20, // LENGTH 0x30, // SEQUENCE 0x0C, // LENGTH 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // MD5 OID (1 2 840 113549 2 5) 0x05, 0x00, // OPTIONAL ANY algorithm params (NULL) 0x04, 0x10 // OCTECT STRING (16 bytes) }; void MuscleCardKeyHandle::generateSignature(const Context &context, CSSM_ALGORITHMS signOnly, const CssmData &input, CssmData &signature) { secdebug("crypto", "generateSignature alg: %lu signOnly: %lu", context.algorithm(), signOnly); IFDUMPING("crypto", context.dump("signature context")); if (context.type() != CSSM_ALGCLASS_SIGNATURE) CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); if (context.algorithm() != CSSM_ALGID_RSA) CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); // Find out if we are doing a SHA1 or MD5 signature and setup header to point to the right asn1 blob. MSCPCUChar8 header; MSCULong32 headerLength; if (signOnly == CSSM_ALGID_SHA1) { if (input.Length != 20) CssmError::throwMe(CSSMERR_CSP_BLOCK_SIZE_MISMATCH); header = sha1sigheader; headerLength = sizeof(sha1sigheader); } else if (signOnly == CSSM_ALGID_MD5) { if (input.Length != 16) CssmError::throwMe(CSSMERR_CSP_BLOCK_SIZE_MISMATCH); header = md5sigheader; headerLength = sizeof(md5sigheader); } else if (signOnly == CSSM_ALGID_NONE) { // Special case used by SSL it's an RSA signature, without the ASN1 stuff header = NULL; headerLength = 0; } else CssmError::throwMe(CSSMERR_CSP_INVALID_DIGEST_ALGORITHM); // Create an input buffer in which we construct the data we will send to the token. MSCUChar8 cipherMode; MSCULong32 inputDataSize = headerLength + input.Length; MSCULong32 keyLength = mKey.size() / 8; auto_array inputData(keyLength); MSCPUChar8 to = inputData.get(); // Get padding, but default to pkcs1 style padding uint32 padding = CSSM_PADDING_PKCS1; context.getInt(CSSM_ATTRIBUTE_PADDING, padding); // Figure out whether the underlying token supports RSA_NOPAD, if so we generate our own padding if not, // we let the card do the PKCS1 padding itself. MSCULong32 rsaCapabilities = mKey.connection().getCapabilities(MSC_TAG_CAPABLE_RSA); if (rsaCapabilities & MSC_CAPABLE_RSA_NOPAD) { secdebug("crypto", "generateSignature: card supports RSA_NOPAD"); cipherMode = MSC_MODE_RSA_NOPAD; if (padding == CSSM_PADDING_PKCS1) { // Add PKCS1 style padding *(to++) = 0; *(to++) = 1; /* Private Key Block Type. */ MSCULong32 padLength = keyLength - 3 - inputDataSize; memset(to, 0xff, padLength); to += padLength; *(to++) = 0; inputDataSize = keyLength; } else if (padding == CSSM_PADDING_NONE) { // Token will fail if the input data isn't exactly keysize / 8 octects long } else CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); } else if (rsaCapabilities & MSC_CAPABLE_RSA_PKCS1) { if (padding != CSSM_PADDING_PKCS1) CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING); secdebug("crypto", "generateSignature: card only supports RSA_PKCS1"); cipherMode = MSC_MODE_RSA_PAD_PKCS1; } else { CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); // @@@ Look for a better error. } // Now copy the ASN1 header into the input buffer. // This header is the DER encoding of // DigestInfo ::= SEQUENCE { digestAlgorithm AlgorithmIdentifier, digest OCTET STRING } // Where AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters OPTIONAL ANY } if (headerLength) { memcpy(to, header, headerLength); to += headerLength; } // Finally copy the passed in data to the input buffer. memcpy(to, input.Data, input.Length); // @@@ Switch to using tokend allocators MSCPUChar8 outputData = reinterpret_cast(malloc(keyLength)); size_t outputLength = keyLength; try { // Sign the inputData using the token mKey.computeCrypt(cipherMode, MSC_DIR_SIGN, inputData.get(), inputDataSize, outputData, outputLength); } catch (...) { // @@@ Switch to using tokend allocators free(outputData); throw; } signature.Data = outputData; signature.Length = outputLength; } void MuscleCardKeyHandle::verifySignature(const Context &context, CSSM_ALGORITHMS signOnly, const CssmData &input, const CssmData &signature) { secdebug("crypto", "verifySignature"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } void MuscleCardKeyHandle::generateMac(const Context &context, const CssmData &input, CssmData &output) { secdebug("crypto", "generateMac"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } void MuscleCardKeyHandle::verifyMac(const Context &context, const CssmData &input, const CssmData &compare) { secdebug("crypto", "verifyMac"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } void MuscleCardKeyHandle::encrypt(const Context &context, const CssmData &clear, CssmData &cipher) { secdebug("crypto", "encrypt"); CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } void MuscleCardKeyHandle::decrypt(const Context &context, const CssmData &cipher, CssmData &clear) { secdebug("crypto", "decrypt alg: %lu", context.algorithm()); IFDUMPING("crypto", context.dump("decrypt context")); if (context.type() != CSSM_ALGCLASS_ASYMMETRIC) CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); if (context.algorithm() != CSSM_ALGID_RSA) CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); size_t keyLength = mKey.size() / 8; if (cipher.length() % keyLength != 0) CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); // @@@ Add support for multiples of keyLength by doing multiple blocks if (cipher.length() != keyLength) CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); // @@@ Use a secure allocator for this. auto_array outputData(keyLength); uint8 *output = outputData.get(); size_t outputLength = keyLength; // Figure out whether the underlying token supports RSA_NOPAD, if so we remove the padding // ourselves if not, we let the card remove the PKCS1 padding. MSCULong32 rsaCapabilities = mKey.connection().getCapabilities(MSC_TAG_CAPABLE_RSA); if (rsaCapabilities & MSC_CAPABLE_RSA_NOPAD) { secdebug("crypto", "decrypt: card supports RSA_NOPAD"); // Decrypt the inputData using the token mKey.computeCrypt(MSC_MODE_RSA_NOPAD, MSC_DIR_DECRYPT, cipher.Data, cipher.Length, output, outputLength); // Now check for proper pkcs1 type 2 padding and remove it. if (outputLength != keyLength || *(output++) != 0 || *(output++) != 2) CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); /* Skip over padding data */ outputLength -= 2; // We already skiped the 00 02 at the start of the block. size_t padSize; for (padSize = 0; padSize < outputLength; ++padSize) if (*(output++) == 0) break; if (padSize == outputLength || padSize < 8) CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); outputLength -= padSize + 1; /* Don't count the 00 at the end of the padding. */ } else if (rsaCapabilities & MSC_CAPABLE_RSA_PKCS1) { secdebug("crypto", "generateSignature: card only supports RSA_PKCS1"); // Decrypt the inputData using the token mKey.computeCrypt(MSC_MODE_RSA_PAD_PKCS1, MSC_DIR_DECRYPT, cipher.Data, cipher.Length, output, outputLength); } else { CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); // @@@ Look for a better error. } // @@@ Switch to using tokend allocators clear.Data = reinterpret_cast(malloc(outputLength)); // Finally copy the result into the clear buffer and set the length. memcpy(clear.Data, output, outputLength); clear.Length = outputLength; } void MuscleCardKeyHandle::exportKey(const Context &context, const AccessCredentials *cred, CssmKey &wrappedKey) { wrappedKey.clearPod(); wrappedKey.header().HeaderVersion = CSSM_KEYHEADER_VERSION; wrappedKey.header().cspGuid(Guid::overlay(gGuidAppleSdCSPDL)); wrappedKey.blobType(CSSM_KEYBLOB_RAW); uint32_t keyType = mKey.type(); uint32 algID; uint32 keyClass; CSSM_KEYBLOB_FORMAT format; switch (keyType) { case MSC_KEY_RSA_PRIVATE: format = CSSM_KEYBLOB_RAW_FORMAT_NONE; keyClass = CSSM_KEYCLASS_PRIVATE_KEY; algID = CSSM_ALGID_RSA; break; case MSC_KEY_RSA_PRIVATE_CRT: format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; keyClass = CSSM_KEYCLASS_PRIVATE_KEY; algID = CSSM_ALGID_RSA; break; case MSC_KEY_RSA_PUBLIC: format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; keyClass = CSSM_KEYCLASS_PUBLIC_KEY; algID = CSSM_ALGID_RSA; break; case MSC_KEY_DSA_PRIVATE: format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186; keyClass = CSSM_KEYCLASS_PRIVATE_KEY; algID = CSSM_ALGID_DSA; break; case MSC_KEY_DSA_PUBLIC: format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186; keyClass = CSSM_KEYCLASS_PUBLIC_KEY; algID = CSSM_ALGID_DSA; break; case MSC_KEY_DES: format = CSSM_KEYBLOB_RAW_FORMAT_NONE; keyClass = CSSM_KEYCLASS_SESSION_KEY; algID = CSSM_ALGID_DES; break; case MSC_KEY_3DES: format = CSSM_KEYBLOB_RAW_FORMAT_NONE; keyClass = CSSM_KEYCLASS_SESSION_KEY; // @@@ Which algid is this? algID = CSSM_ALGID_3DES; //algID = CSSM_ALGID_3DES_3KEY_EDE; //algID = CSSM_ALGID_3DES_2KEY_EDE; //algID = CSSM_ALGID_3DES_1KEY_EEE; //algID = CSSM_ALGID_3DES_3KEY_EEE; //algID = CSSM_ALGID_3DES_2KEY_EEE; break; case MSC_KEY_3DES3: format = CSSM_KEYBLOB_RAW_FORMAT_NONE; keyClass = CSSM_KEYCLASS_SESSION_KEY; // @@@ Which algid is this? algID = CSSM_ALGID_3DES_3KEY_EDE; //algID = CSSM_ALGID_3DES_3KEY_EEE; break; default: format = CSSM_KEYBLOB_RAW_FORMAT_OTHER; keyClass = CSSM_KEYCLASS_OTHER; algID = CSSM_ALGID_CUSTOM; break; } wrappedKey.blobFormat(format); wrappedKey.algorithm(algID); wrappedKey.keyClass(keyClass); wrappedKey.header().LogicalKeySizeInBits = mKey.size() / 8; wrappedKey.header().KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE; #if 0 CSSM_KEYUSE usage = (mr.metaAttribute(kSecKeyEncrypt).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_ENCRYPT : 0) | (mr.metaAttribute(kSecKeyDecrypt).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_DECRYPT : 0) | (mr.metaAttribute(kSecKeySign).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_SIGN : 0) | (mr.metaAttribute(kSecKeyVerify).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_VERIFY : 0) | (mr.metaAttribute(kSecKeySignRecover).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_SIGN_RECOVER : 0) | (mr.metaAttribute(kSecKeyVerifyRecover).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_VERIFY_RECOVER : 0) | (mr.metaAttribute(kSecKeyWrap).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_WRAP : 0) | (mr.metaAttribute(kSecKeyUnwrap).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_UNWRAP : 0) | (mr.metaAttribute(kSecKeyDerive).attribute(tokenContext, record).boolValue() ? CSSM_KEYUSE_DERIVE : 0); if (usage == (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_DERIVE)) usage = CSSM_KEYUSE_ANY; wrappedKey.header().KeyUsage = usage; #else wrappedKey.header().KeyUsage = CSSM_KEYUSE_ANY; #endif wrappedKey.KeyData.Length = mKey.size() / 8; void *buffer = malloc(wrappedKey.KeyData.Length); wrappedKey.KeyData.Data = reinterpret_cast(buffer); mKey.exportKey(buffer, wrappedKey.KeyData.Length); } void MuscleCardKeyHandle::getOwner(AclOwnerPrototype &owner) { // we don't really know (right now), so claim we're owned by PIN #0 if (!mAclOwner) { Allocator &alloc = Allocator::standard(); mAclOwner.allocator(alloc); unsigned int acl = mKey.keyACL.readPermission; if (acl == MSC_AUT_NONE) acl = mKey.keyACL.writePermission; if (acl == MSC_AUT_NONE) acl = mKey.keyACL.usePermission; if (acl == MSC_AUT_NONE) { // nobody can do anything with this key? how useless... mAclOwner = AclFactory::NobodySubject(alloc); } else if (acl == MSC_AUT_ALL) { // no restrictions - an ANY ACL mAclOwner = AclFactory::AnySubject(alloc); } else { // we don't currently support ownership by multiple PINs: // pick the first one and ignore the rest for (unsigned n = 0; n < 5; n++) if (acl & (MSC_AUT_PIN_0 << n)) { mAclOwner = AclFactory::PinSubject(alloc, n); break; } // ignoring the KEY and USER bits -- whatever they might be } } owner = mAclOwner; } void MuscleCardKeyHandle::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls) { // we don't (yet) support queries by tag if (tag) CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG); if (!mAclEntries) { mAclEntries.allocator(Allocator::standard()); // Anyone can read the DB record for this key (which is a reference CSSM_KEY) mAclEntries.add(CssmClient::AclFactory::AnySubject(mAclEntries.allocator()), AclAuthorizationSet(CSSM_ACL_AUTHORIZATION_DB_READ, 0)); // READ -> unwrap (extract) keyAcl(mKey.keyACL.readPermission, AclAuthorizationSet( CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR, CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, 0)); // WRITE is currently ignored // USE will have to serve for all crypto operations (pity that) keyAcl(mKey.keyACL.usePermission, AclAuthorizationSet( CSSM_ACL_AUTHORIZATION_ENCRYPT, CSSM_ACL_AUTHORIZATION_DECRYPT, CSSM_ACL_AUTHORIZATION_SIGN, CSSM_ACL_AUTHORIZATION_MAC, CSSM_ACL_AUTHORIZATION_DERIVE, 0)); } count = mAclEntries.size(); acls = mAclEntries.entries(); } void MuscleCardKeyHandle::keyAcl(unsigned int acl, const AclAuthorizationSet &auths) { Allocator &alloc = mAclEntries.allocator(); if (acl == MSC_AUT_NONE) { // there's no way to do this... so say nothing } else if (acl == MSC_AUT_ALL) { // no restrictions - add an ANY ACL mAclEntries.add(AclFactory::AnySubject(alloc), auths); } else { // general case: for each enabling PIN, issue an ACL entry // (we could form a 1-of-n ACL, but that would complicate the sample set) for (unsigned n = 0; n < 5; n++) if (acl & (MSC_AUT_PIN_0 << n)) mAclEntries.add(AclFactory::PinSubject(alloc, n), auths); // ignoring the KEY and USER bits -- whatever they might be } } // // MuscleCardKeyHandleFactory // MuscleCardKeyHandleFactory::~MuscleCardKeyHandleFactory() { } Tokend::KeyHandle *MuscleCardKeyHandleFactory::keyHandle(Tokend::TokenContext *tokenContext, const Tokend::MetaRecord &metaRecord, Tokend::Record &record) const { KeyRecord &keyRecord = dynamic_cast(record); return new MuscleCardKeyHandle(metaRecord, record, keyRecord.key()); } /* arch-tag: 426D0873-05E0-11D9-85AD-000A9595DEEE */