crypto-embedded.c   [plain text]


/*
 * Copyright (c) 2008-2011,2013,2015 Apple 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@
 */

#include <stdio.h>

#include "cert.h"
#include "cryptohi.h"

#include "cmstpriv.h"
#include "secoid.h"
#include "cmspriv.h"

#include <libDER/DER_Decode.h>
#include <security_asn1/secerr.h>
#include <security_asn1/secport.h>

#include <Security/SecBase.h>

#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>

#include <Security/oidsalg.h>
#include <Security/SecPolicy.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecIdentity.h>
#include <Security/SecCertificateInternal.h>
#include <Security/SecKeyPriv.h>

#include <CommonCrypto/CommonDigest.h>
#include <AssertMacros.h>

SECStatus
CERT_VerifyCert(SecKeychainRef keychainOrArray __unused, CFArrayRef certs,
                CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef)
{
    SecTrustRef trust = NULL;
    OSStatus rv;

    rv = SecTrustCreateWithCertificates(certs, policies, &trust);
    if (rv)
        goto loser;

    CFDateRef verifyDate = CFDateCreate(NULL, stime);
    rv = SecTrustSetVerifyDate(trust, verifyDate);
    CFRelease(verifyDate);
    if (rv)
        goto loser;

    if (trustRef)
    {
        *trustRef = trust;
    }
    else
    {
        SecTrustResultType result;
        /* The caller doesn't want a SecTrust object, so let's evaluate it for them. */
        rv = SecTrustEvaluate(trust, &result);
        if (rv)
            goto loser;

        switch (result)
        {
            case kSecTrustResultProceed:
            case kSecTrustResultUnspecified:
                /* TP Verification succeeded and there was either a UserTurst entry
                 telling us to procceed, or no user trust setting was specified. */
                CFRelease(trust);
                break;
            default:
                PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
                rv = SECFailure;
                goto loser;
                break;
        }
    }

    return SECSuccess;
loser:
    if (trust)
        CFRelease(trust);

    return rv;
}

static CFTypeRef CERT_FindItemInAllAvailableKeychains(CFDictionaryRef query) {
    CFTypeRef item = NULL;
    CFMutableDictionaryRef q = NULL;
    CFDictionaryRef whoAmI = NULL;
    CFErrorRef error = NULL;
    CFDataRef musr = NULL;
    const uint8_t activeUserUuid[16] = "\xA7\x5A\x3A\x35\xA5\x57\x4B\x10\xBE\x2E\x83\x94\x7E\x4A\x34\x72";

    /* Do the standard keychain query */
    require_quiet(errSecItemNotFound == SecItemCopyMatching(query, &item), out);

    /* No item found. Can caller use the system keychain? */
    whoAmI = _SecSecuritydCopyWhoAmI(&error);
    require_quiet(NULL == error && whoAmI && CFDictionaryGetValue(whoAmI, CFSTR("status")), out);
    musr = CFDictionaryGetValue(whoAmI, CFSTR("musr"));
    /* Caller has system-keychain entitlement, is in multi-user mode, and is an active user. */
    if (CFDictionaryGetValue(whoAmI, CFSTR("system-keychain")) && musr &&
        (16 == CFDataGetLength(musr)) && (0 == memcmp(activeUserUuid,CFDataGetBytePtr(musr),12))) {
        q = CFDictionaryCreateMutableCopy(NULL, CFDictionaryGetCount(query) + 1, query);
        CFDictionaryAddValue(q, kSecUseSystemKeychain, kCFBooleanTrue);
        SecItemCopyMatching(q, &item);
    }

out:
    if (q)
        CFRelease(q);
    if (whoAmI)
        CFRelease(whoAmI);
    if (error)
        CFRelease(error);

    return item;
}

SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray,
			 char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win)
{
	CFStringRef nickname_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
	const void *keys[] = { kSecClass, kSecAttrLabel };
	const void *values[] = { kSecClassCertificate,  nickname_cfstr };
	CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL);
	CFTypeRef result = NULL;
	result = CERT_FindItemInAllAvailableKeychains(query);
	CFRelease(query);
	CFRelease(nickname_cfstr);
	return (SecCertificateRef)result;
}

CF_RETURNS_RETAINED CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot)
{
    CFMutableArrayRef certs = NULL;
    SecPolicyRef policy = NULL;
    SecTrustRef trust = NULL;
    CFArrayRef wrappedCert = NULL;
    
    policy = SecPolicyCreateBasicX509();
    if (!policy)
        goto out;
    
    wrappedCert = CERT_CertListFromCert(cert);
    if (SecTrustCreateWithCertificates(wrappedCert, policy, &trust))
        goto out;

	SecTrustResultType result;
    if (SecTrustEvaluate(trust, &result))
        goto out;
    CFIndex idx, count = SecTrustGetCertificateCount(trust);

    /* If we weren't able to build a chain to a self-signed cert, warn. */
    Boolean isSelfSigned = false;
    SecCertificateRef lastCert = SecTrustGetCertificateAtIndex(trust, count - 1);
    if (lastCert && (0 == SecCertificateIsSelfSigned(lastCert, &isSelfSigned)) && !isSelfSigned) {
        CFStringRef commonName = NULL;
        (void)SecCertificateCopyCommonName(cert, &commonName);
        fprintf(stderr, "Warning: unable to build chain to self-signed root for signer \"%s\"",
                commonName ? CFStringGetCStringPtr(commonName, kCFStringEncodingUTF8) : "");
        if (commonName) { CFRelease(commonName); }
    }

    /* We don't drop the root if there is only 1 certificate in the chain. */
    if (!includeRoot && count > 1) { count--; }
    certs = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
    for(idx = 0; idx < count; idx++)
        CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(trust, idx));

out:
    if (trust) CFRelease(trust);
    if (policy) CFRelease(policy);
    if (wrappedCert) CFRelease(wrappedCert);

    return certs;
}

CFArrayRef CERT_CertListFromCert(SecCertificateRef cert)
{
    const void *value = cert;
    return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL;
}

CFArrayRef CERT_DupCertList(CFArrayRef oldList)
{
    CFRetain(oldList);
    return oldList;
}

// Extract a public key object from a SubjectPublicKeyInfo
SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert)
{
    return SecCertificateCopyKey(cert);
}

// Extract the issuer and serial number from a certificate
SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert)
{
    SecCmsIssuerAndSN *certIssuerAndSN;

    void *mark;
    mark = PORT_ArenaMark(pl);
    CFDataRef serial_data = NULL;
    CFDataRef issuer_data = SecCertificateCopyIssuerSequence(cert);
    if (!issuer_data)
        goto loser;
    serial_data = SecCertificateCopySerialNumberData(cert, NULL);
    if (!serial_data)
        goto loser;
    
    SecAsn1Item serialNumber = { CFDataGetLength(serial_data),
        (uint8_t *)CFDataGetBytePtr(serial_data) };
    SecAsn1Item issuer = { CFDataGetLength(issuer_data),
        (uint8_t *)CFDataGetBytePtr(issuer_data) };
    
        /* Allocate the SecCmsIssuerAndSN struct. */
    certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN));
    if (certIssuerAndSN == NULL)
	goto loser;

    /* Copy the issuer. */
    certIssuerAndSN->derIssuer.Data = (uint8_t *) PORT_ArenaAlloc(pl, issuer.Length);
    if (!certIssuerAndSN->derIssuer.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer.Data, issuer.Length);
    certIssuerAndSN->derIssuer.Length = issuer.Length;

    /* Copy the serialNumber. */
    certIssuerAndSN->serialNumber.Data = (uint8_t *) PORT_ArenaAlloc(pl, serialNumber.Length);
    if (!certIssuerAndSN->serialNumber.Data)
	goto loser;
    PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber.Data, serialNumber.Length);
    certIssuerAndSN->serialNumber.Length = serialNumber.Length;

    CFRelease(serial_data);
    CFRelease(issuer_data);

    PORT_ArenaUnmark(pl, mark);
    
    return certIssuerAndSN;

loser:
    if (serial_data)
        CFRelease(serial_data);
    if (issuer_data)
        CFRelease(issuer_data);
    PORT_ArenaRelease(pl, mark);
    PORT_SetError(SEC_INTERNAL_ONLY);

    return NULL;
}

// find the smime symmetric capabilities profile for a given cert
SecAsn1Item *CERT_FindSMimeProfile(SecCertificateRef cert)
{
    return NULL;
}

// Generate a certificate key from the issuer and serialnumber, then look it up in the database.
// Return the cert if found. "issuerAndSN" is the issuer and serial number to look for
static CFTypeRef CERT_FindByIssuerAndSN (CFTypeRef keychainOrArray, CFTypeRef class, const SecCmsIssuerAndSN *issuerAndSN)
{
    CFTypeRef ident = NULL;
	CFDictionaryRef query = NULL;
	CFDataRef issuer = NULL;
    CFDataRef serial = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 
        issuerAndSN->serialNumber.Data, issuerAndSN->serialNumber.Length, 
		kCFAllocatorNull);
	
	DERItem der_issuer = { issuerAndSN->derIssuer.Data,
		 					issuerAndSN->derIssuer.Length };
	DERDecodedInfo content;	
	require_noerr_quiet(DERDecodeItem(&der_issuer, &content), out);
    require_quiet(issuer = createNormalizedX501Name(kCFAllocatorDefault,
		&content.content), out);

    if (keychainOrArray && (CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate))
    {
        CFIndex c, count = CFArrayGetCount((CFArrayRef)keychainOrArray);
        for (c = 0; c < count; c++) {
            SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c);
            CFDataRef nic = (cert) ? SecCertificateGetNormalizedIssuerContent(cert) : NULL;
            if (nic && CFEqual(nic, issuer)) {
                CFDataRef cert_serial = SecCertificateCopySerialNumberData(cert, NULL);
                if (cert_serial) {
                    bool found = CFEqual(cert_serial, serial);
                    CFRelease(cert_serial);
                    if (found) {
                        CFRetain(cert);
                        ident = cert;
                        goto out;
                    }
                }
            }
        }
    }

	const void *keys[] = { kSecClass, kSecAttrIssuer, kSecAttrSerialNumber, kSecReturnRef };
	const void *values[] = { class, issuer, serial, kCFBooleanTrue };
	query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL);
	ident = CERT_FindItemInAllAvailableKeychains(query);

out:
    if (query)
        CFRelease(query);
    if (issuer)
        CFRelease(issuer);
    if (serial)
        CFRelease(serial);

    return ident;
}

SecIdentityRef CERT_FindIdentityByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN)
{
    return (SecIdentityRef)CERT_FindByIssuerAndSN(keychainOrArray, kSecClassIdentity, issuerAndSN);
}

SecCertificateRef CERT_FindCertificateByIssuerAndSN (CFTypeRef keychainOrArray, const SecCmsIssuerAndSN *issuerAndSN)
{
    return (SecCertificateRef)CERT_FindByIssuerAndSN(keychainOrArray, kSecClassCertificate, issuerAndSN);
}

// Generate a certificate key from the Subject Key ID, then look it up in the database.
// Return the cert if found. "subjKeyID" is the Subject Key ID to look for
static CFTypeRef CERT_FindBySubjectKeyID (CFTypeRef keychainOrArray, CFTypeRef class, const SecAsn1Item *subjKeyID)
{
    CFTypeRef ident = NULL;
    CFDictionaryRef query = NULL;

    if (!subjKeyID || !subjKeyID->Data || !subjKeyID->Length) {
        return NULL;
    }

    CFDataRef subjectkeyid = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, subjKeyID->Data, subjKeyID->Length, kCFAllocatorNull);
    if (keychainOrArray && (CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) && CFEqual(class, kSecClassCertificate))
    {
        CFIndex c, count = CFArrayGetCount((CFArrayRef)keychainOrArray);
        for (c = 0; c < count; c++) {
            SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)keychainOrArray, c);
            CFDataRef skid = (cert) ? SecCertificateGetSubjectKeyID(cert) : NULL;
            if (skid && CFEqual(skid, subjectkeyid)) {
                CFRetain(cert);
                ident = cert;
                goto out;
            }
        }
    }

    const void *keys[] = { kSecClass, kSecAttrSubjectKeyID, kSecReturnRef };
    const void *values[] = { class, subjectkeyid, kCFBooleanTrue };
    query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), NULL, NULL);
    ident = CERT_FindItemInAllAvailableKeychains(query);

out:
    if (query)
        CFRelease(query);
    if (subjectkeyid)
        CFRelease(subjectkeyid);

    return ident;
}

SecIdentityRef CERT_FindIdentityBySubjectKeyID (CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID)
{
    return (SecIdentityRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassIdentity, subjKeyID);
}

SecCertificateRef CERT_FindCertificateBySubjectKeyID(CFTypeRef keychainOrArray, const SecAsn1Item *subjKeyID)
{
     return (SecCertificateRef)CERT_FindBySubjectKeyID(keychainOrArray, kSecClassCertificate, subjKeyID);
}



SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey)
{
    CFRetain(pubKey);
    return pubKey;
}

void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey)
{
    CFRelease(pubKey);
}

SecPublicKeyRef SECKEY_CopyPrivateKey(SecPublicKeyRef privKey)
{
    CFRetain(privKey);
    return privKey;
}

void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey)
{
    CFRelease(privKey);
}

void CERT_DestroyCertificate(SecCertificateRef cert)
{
    CFRelease(cert);
}

SecCertificateRef CERT_DupCertificate(SecCertificateRef cert)
{
    CFRetain(cert);
    return cert;
}

SECStatus
WRAP_PubWrapSymKey(SecPublicKeyRef publickey,
		   SecSymmetricKeyRef bulkkey,
		   SecAsn1Item * encKey)
{
    return SecKeyEncrypt(publickey, kSecPaddingPKCS1, 
                        CFDataGetBytePtr(bulkkey), CFDataGetLength(bulkkey),
                        encKey->Data, &encKey->Length);
}

#define MAX_KEY_SIZE 8192/8
SecSymmetricKeyRef
WRAP_PubUnwrapSymKey(SecPrivateKeyRef privkey, const SecAsn1Item *encKey, SECOidTag bulkalgtag)
{
    size_t bulkkey_size = encKey->Length;
    if (bulkkey_size > MAX_KEY_SIZE) {
        return NULL;
    }

    uint8_t *bulkkey_buffer = (uint8_t *)malloc(bulkkey_size);
    if (!bulkkey_buffer) {
        return NULL;
    }
    if (SecKeyDecrypt(privkey, kSecPaddingPKCS1, encKey->Data, encKey->Length, bulkkey_buffer, &bulkkey_size)) {
        return NULL;
    }

    CFDataRef bulkkey = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bulkkey_buffer, bulkkey_size, kCFAllocatorMalloc);
    return (SecSymmetricKeyRef)bulkkey;
}


bool
CERT_CheckIssuerAndSerial(SecCertificateRef cert, SecAsn1Item *issuer, SecAsn1Item *serial)
{
    do {
        CFDataRef cert_issuer = SecCertificateCopyIssuerSequence(cert);
        if (!cert_issuer)
            break;
        if ((issuer->Length != (size_t)CFDataGetLength(cert_issuer)) ||
            memcmp(issuer->Data, CFDataGetBytePtr(cert_issuer), issuer->Length)) {
                CFRelease(cert_issuer);
                break;
            }
        CFRelease(cert_issuer);
        CFDataRef cert_serial = SecCertificateCopySerialNumberData(cert, NULL);
        if (!cert_serial)
            break;
        if ((serial->Length != (size_t)CFDataGetLength(cert_serial)) ||
            memcmp(serial->Data, CFDataGetBytePtr(cert_serial), serial->Length)) {
                CFRelease(cert_serial);
                break;
        }
        CFRelease(cert_serial);
        return true;
    } while(0);
    return false;
}