int32_t mz_crypt_sign_verify(uint8_t *message, int32_t message_size, uint8_t *signature, int32_t signature_size) { CMSDecoderRef decoder = NULL; CMSSignerStatus signer_status = 0; CFDataRef message_out = NULL; SecPolicyRef trust_policy = NULL; OSStatus status = noErr; OSStatus verify_status = noErr; size_t signer_count = 0; size_t i = 0; int32_t err = MZ_SIGN_ERROR; if (message == NULL || signature == NULL) return MZ_PARAM_ERROR; status = CMSDecoderCreate(&decoder); if (status == errSecSuccess) status = CMSDecoderUpdateMessage(decoder, signature, signature_size); if (status == errSecSuccess) status = CMSDecoderFinalizeMessage(decoder); if (status == errSecSuccess) trust_policy = SecPolicyCreateBasicX509(); if (status == errSecSuccess && trust_policy) { CMSDecoderGetNumSigners(decoder, &signer_count); if (signer_count > 0) err = MZ_OK; for (i = 0; i < signer_count; i += 1) { status = CMSDecoderCopySignerStatus(decoder, i, trust_policy, TRUE, &signer_status, NULL, &verify_status); if (status != errSecSuccess || verify_status != 0 || signer_status != kCMSSignerValid) { err = MZ_SIGN_ERROR; break; } } } if (err == MZ_OK) { status = CMSDecoderCopyContent(decoder, &message_out); if ((status != errSecSuccess) || (CFDataGetLength(message_out) != message_size) || (memcmp(message, CFDataGetBytePtr(message_out), message_size) != 0)) err = MZ_SIGN_ERROR; } if (trust_policy) CFRelease(trust_policy); if (decoder) CFRelease(decoder); return err; }
/* * Parse a ContentInfo as best we can. All return fields are optional. * If signer_cert_status is NULL on entry, NO signature or cert evaluation * will be performed. */ krb5_error_code krb5int_pkinit_parse_cms_msg( const krb5_data *content_info, krb5_pkinit_cert_db_t cert_db, /* may be required for SignedData */ krb5_boolean is_client_msg, /* TRUE : msg is from client */ krb5_boolean *is_signed, /* RETURNED */ krb5_boolean *is_encrypted, /* RETURNED */ krb5_data *raw_data, /* RETURNED */ krb5int_cms_content_type *inner_content_type,/* Returned, ContentType of */ /* EncapsulatedData */ krb5_data *signer_cert, /* RETURNED */ krb5int_cert_sig_status *signer_cert_status,/* RETURNED */ unsigned *num_all_certs, /* size of *all_certs RETURNED */ krb5_data **all_certs) /* entire cert chain RETURNED */ { SecPolicySearchRef policy_search = NULL; SecPolicyRef policy = NULL; OSStatus ortn; krb5_error_code krtn = 0; CMSDecoderRef decoder = NULL; size_t num_signers; CMSSignerStatus signer_status; OSStatus cert_verify_status; CFArrayRef cf_all_certs = NULL; int msg_is_signed = 0; if(content_info == NULL) { pkiDebug("krb5int_pkinit_parse_cms_msg: no ContentInfo\n"); return KRB5_CRYPTO_INTERNAL; } ortn = CMSDecoderCreate(&decoder); if(ortn) { return ENOMEM; } ortn = CMSDecoderUpdateMessage(decoder, content_info->data, content_info->length); if(ortn) { /* no verify yet, must be bad message */ krtn = KRB5_PARSE_MALFORMED; goto errOut; } ortn = CMSDecoderFinalizeMessage(decoder); if(ortn) { pkiCssmErr("CMSDecoderFinalizeMessage", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } /* expect zero or one signers */ ortn = CMSDecoderGetNumSigners(decoder, &num_signers); switch(num_signers) { case 0: msg_is_signed = 0; break; case 1: msg_is_signed = 1; break; default: krtn = KRB5_PARSE_MALFORMED; goto errOut; } /* * We need a cert verify policy even if we're not actually evaluating * the cert due to requirements in libsecurity_smime. */ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, is_client_msg ? &CSSMOID_APPLE_TP_PKINIT_CLIENT : &CSSMOID_APPLE_TP_PKINIT_SERVER, NULL, &policy_search); if(ortn) { pkiCssmErr("SecPolicySearchCreate", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ortn = SecPolicySearchCopyNext(policy_search, &policy); if(ortn) { pkiCssmErr("SecPolicySearchCopyNext", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } /* get some basic status that doesn't need heavyweight evaluation */ if(msg_is_signed) { if(is_signed) { *is_signed = TRUE; } if(inner_content_type) { CSSM_OID ec_oid = {0, NULL}; CFDataRef ec_data = NULL; krb5int_cms_content_type ctype; ortn = CMSDecoderCopyEncapsulatedContentType(decoder, &ec_data); if(ortn || (ec_data == NULL)) { pkiCssmErr("CMSDecoderCopyEncapsulatedContentType", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ec_oid.Data = (uint8 *)CFDataGetBytePtr(ec_data); ec_oid.Length = CFDataGetLength(ec_data); if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_Data)) { ctype = ECT_Data; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_SignedData)) { ctype = ECT_SignedData; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EnvelopedData)) { ctype = ECT_EnvelopedData; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EncryptedData)) { ctype = ECT_EncryptedData; } else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_AUTH_DATA)) { ctype = ECT_PkAuthData; } else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_RKEY_DATA)) { ctype = ECT_PkReplyKeyKata; } else { ctype = ECT_Other; } *inner_content_type = ctype; CFRelease(ec_data); } /* * Get SignedData's certs if the caller wants them */ if(all_certs) { ortn = CMSDecoderCopyAllCerts(decoder, &cf_all_certs); if(ortn) { pkiCssmErr("CMSDecoderCopyAllCerts", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } krtn = pkiCertArrayToKrb5Data(cf_all_certs, num_all_certs, all_certs); if(krtn) { goto errOut; } } /* optional signer cert */ if(signer_cert) { SecCertificateRef sec_signer_cert = NULL; CSSM_DATA cert_data; ortn = CMSDecoderCopySignerCert(decoder, 0, &sec_signer_cert); if(ortn) { /* should never happen if it's signed */ pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ortn = SecCertificateGetData(sec_signer_cert, &cert_data); if(ortn) { pkiCssmErr("SecCertificateGetData", ortn); CFRelease(sec_signer_cert); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } krtn = pkiDataToKrb5Data(cert_data.Data, cert_data.Length, signer_cert); CFRelease(sec_signer_cert); if(krtn) { goto errOut; } } } else { /* not signed */ if(is_signed) { *is_signed = FALSE; } if(inner_content_type) { *inner_content_type = ECT_Other; } if(signer_cert) { signer_cert->data = NULL; signer_cert->length = 0; } if(signer_cert_status) { *signer_cert_status = pki_not_signed; } if(num_all_certs) { *num_all_certs = 0; } if(all_certs) { *all_certs = NULL; } } if(is_encrypted) { Boolean bencr; ortn = CMSDecoderIsContentEncrypted(decoder, &bencr); if(ortn) { pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } *is_encrypted = bencr ? TRUE : FALSE; } /* * Verify signature and cert. The actual verify operation is optional, * per our signer_cert_status argument, but we do this anyway if we need * to get the signer cert. */ if((signer_cert_status != NULL) || (signer_cert != NULL)) { ortn = CMSDecoderCopySignerStatus(decoder, 0, /* signerIndex */ policy, signer_cert_status ? TRUE : FALSE, /* evaluateSecTrust */ &signer_status, NULL, /* secTrust - not needed */ &cert_verify_status); if(ortn) { /* gross error - subsequent processing impossible */ pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } } /* obtain & return status */ if(signer_cert_status) { *signer_cert_status = pkiInferSigStatus(signer_status, cert_verify_status); } /* finally, the payload */ if(raw_data) { CFDataRef cf_content = NULL; ortn = CMSDecoderCopyContent(decoder, &cf_content); if(ortn) { pkiCssmErr("CMSDecoderCopyContent", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } krtn = pkiCfDataToKrb5Data(cf_content, raw_data); CFRELEASE(cf_content); } errOut: CFRELEASE(policy_search); CFRELEASE(policy); CFRELEASE(cf_all_certs); CFRELEASE(decoder); return krtn; }