#include "cms-01-basic.h"
#include "smime_regressions.h"
#include <AssertMacros.h>
#include <utilities/SecCFRelease.h>
#include <Security/SecBase.h>
#include <Security/SecImportExport.h>
#include <Security/SecKeychain.h>
#include <Security/SecIdentity.h>
#include <Security/SecPolicy.h>
#include <Security/SecCmsMessage.h>
#include <Security/SecCmsSignedData.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsSignerInfo.h>
#include <Security/SecCmsEncoder.h>
#include <Security/SecCmsDecoder.h>
#include <Security/SecCmsEnvelopedData.h>
#include <Security/SecCmsRecipientInfo.h>
#include <security_asn1/secerr.h>
#include <security_asn1/seccomon.h>
#include <unistd.h>
#define TMP_KEYCHAIN_PATH "/tmp/cms_01_test.keychain"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-function"
#define kNumberSetupTests 10
static SecKeychainRef setup_keychain(const uint8_t *p12, size_t p12_len, SecIdentityRef *identity, SecCertificateRef *cert) {
CFDataRef p12Data = NULL;
CFArrayRef imported_items = NULL, oldSearchList = NULL;
CFMutableArrayRef newSearchList = NULL;
SecKeychainRef keychain = NULL;
SecExternalFormat sef = kSecFormatPKCS12;
SecItemImportExportKeyParameters keyParams = {
.passphrase = CFSTR("password")
};
unlink(TMP_KEYCHAIN_PATH);
ok_status(SecKeychainCopySearchList(&oldSearchList),
"Copy keychain search list");
require(oldSearchList, out);
ok(newSearchList = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(oldSearchList)+1, oldSearchList),
"Create new search list");
ok_status(SecKeychainCreate(TMP_KEYCHAIN_PATH, 8, "password", false, NULL, &keychain),
"Create keychain for identity");
require(keychain, out);
CFArrayAppendValue(newSearchList, keychain);
ok_status(SecKeychainSetSearchList(newSearchList),
"Set keychain search list");
ok(p12Data = CFDataCreate(NULL, p12, p12_len),
"Create p12 data");
ok_status(SecItemImport(p12Data, NULL, &sef, NULL, 0, &keyParams, keychain, &imported_items),
"Import identity");
is(CFArrayGetCount(imported_items),1,"Imported 1 items");
is(CFGetTypeID(CFArrayGetValueAtIndex(imported_items, 0)), SecIdentityGetTypeID(),
"Got back an identity");
ok(*identity = (SecIdentityRef) CFRetainSafe(CFArrayGetValueAtIndex(imported_items, 0)),
"Retrieve identity");
ok_status(SecIdentityCopyCertificate(*identity, cert),
"Copy certificate");
CFReleaseNull(p12Data);
CFReleaseNull(imported_items);
out:
CFReleaseNull(oldSearchList);
CFReleaseNull(newSearchList);
return keychain;
}
#define kNumberCleanupTests 1
static void cleanup_keychain(SecKeychainRef keychain, SecIdentityRef identity, SecCertificateRef cert) {
ok_status(SecKeychainDelete(keychain), "Delete temporary keychain");
CFReleaseNull(keychain);
CFReleaseNull(cert);
CFReleaseNull(identity);
}
static OSStatus sign_please(SecIdentityRef identity, SECOidTag digestAlgTag, bool withAttrs, uint8_t *expected_output, size_t expected_len) {
OSStatus status = SECFailure;
SecCmsMessageRef cmsg = NULL;
SecCmsSignedDataRef sigd = NULL;
SecCmsContentInfoRef cinfo = NULL;
SecCmsSignerInfoRef signerInfo = NULL;
SecCmsEncoderRef encoder = NULL;
SecArenaPoolRef arena = NULL;
CSSM_DATA cms_data = {
.Data = NULL,
.Length = 0
};
uint8_t string_to_sign[] = "This message is signed. Ain't it pretty?";
require_action_string(cmsg = SecCmsMessageCreate(NULL), out,
status = errSecAllocate, "Failed to create message");
require_action_string(sigd = SecCmsSignedDataCreate(cmsg), out,
status = errSecAllocate, "Failed to create signed data");
require_action_string(cinfo = SecCmsMessageGetContentInfo(cmsg), out,
status = errSecParam, "Failed to get cms content info");
require_noerr_string(status = SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd), out,
"Failed to set signed data into content info");
require_action_string(cinfo = SecCmsSignedDataGetContentInfo(sigd), out,
status = errSecParam, "Failed to get content info from signed data");
require_noerr_string(status = SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false), out,
"Failed to set signed data content info");
require_action_string(signerInfo = SecCmsSignerInfoCreate(cmsg, identity, digestAlgTag), out,
status = errSecAllocate, "Failed to create signer info");
require_noerr_string(status = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertOnly,
certUsageEmailSigner), out,
"Failed to put certs in signer info");
if(withAttrs) {
require_noerr_string(status = SecCmsSignerInfoAddSigningTime(signerInfo, 480000000.0), out,
"Couldn't add an attribute");
}
require_noerr_string(status = SecCmsSignedDataAddSignerInfo(sigd, signerInfo), out,
"Couldn't add signer info to signed data");
require_noerr_string(status = SecArenaPoolCreate(1024, &arena), out,
"Failed to create arena");
require_noerr_string(status = SecCmsEncoderCreate(cmsg, NULL, NULL, &cms_data, arena, NULL, NULL,
NULL, NULL, NULL, NULL, &encoder), out,
"Failed to create encoder");
require_noerr_string(status = SecCmsEncoderUpdate(encoder, string_to_sign, sizeof(string_to_sign)), out,
"Failed to add data ");
status = SecCmsEncoderFinish(encoder);
encoder = NULL; require_noerr_quiet(status, out);
if (expected_output) {
require_action_string(expected_len == cms_data.Length, out,
status = -1, "Output size differs from expected");
require_noerr_action_string(memcmp(expected_output, cms_data.Data, expected_len), out,
status = -1, "Output differs from expected");
}
out:
if (encoder) {
SecCmsEncoderDestroy(encoder);
}
if (arena) {
SecArenaPoolFree(arena, false);
}
if (cmsg) {
SecCmsMessageDestroy(cmsg);
}
return status;
}
static OSStatus verify_please(SecKeychainRef keychain, uint8_t *data_to_verify, size_t length) {
OSStatus status = SECFailure;
SecCmsDecoderRef decoder = NULL;
SecCmsMessageRef cmsg = NULL;
SecCmsContentInfoRef cinfo = NULL;
SecCmsSignedDataRef sigd = NULL;
SecPolicyRef policy = NULL;
SecTrustRef trust = NULL;
if (!data_to_verify) {
return errSecSuccess; }
require_noerr_string(status = SecCmsDecoderCreate(NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &decoder), out,
"Failed to create decoder");
require_noerr_string(status = SecCmsDecoderUpdate(decoder, data_to_verify, length), out,
"Failed to add data ");
status = SecCmsDecoderFinish(decoder, &cmsg);
decoder = NULL; require_noerr_quiet(status, out);
require_action_string(cinfo = SecCmsMessageContentLevel(cmsg, 0), out,
status = errSecDecode, "Failed to get content info");
require_action_string(SEC_OID_PKCS7_SIGNED_DATA == SecCmsContentInfoGetContentTypeTag(cinfo), out,
status = errSecDecode, "Content type was pkcs7 signed data");
require_action_string(sigd = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(cinfo), out,
status = errSecDecode, "Failed to get signed data");
require_action_string(policy = SecPolicyCreateBasicX509(), out,
status = errSecAllocate, "Failed to create basic policy");
status = SecCmsSignedDataVerifySignerInfo(sigd, 0, keychain, policy, &trust);
out:
if (decoder) {
SecCmsDecoderDestroy(decoder);
}
if (cmsg) {
SecCmsMessageDestroy(cmsg);
}
CFReleaseNull(policy);
CFReleaseNull(trust);
return status;
}
static uint8_t *invalidate_signature(uint8_t *cms_data, size_t length) {
if (!cms_data || !length || (length < 10)) {
return NULL;
}
uint8_t *invalid_cms = NULL;
invalid_cms = malloc(length);
if (invalid_cms) {
memcpy(invalid_cms, cms_data, length);
invalid_cms[length - 10] = 0x00;
}
return invalid_cms;
}
static OSStatus invalidate_and_verify(SecKeychainRef kc, uint8_t *cms_data, size_t length) {
OSStatus status = SECFailure;
uint8_t *invalid_cms_data = NULL;
if (!cms_data) {
return SECFailure; }
require_action_string(invalid_cms_data = invalidate_signature(cms_data, length), out,
status = errSecAllocate, "Unable to allocate buffer for invalid cms data");
status = verify_please(kc, invalid_cms_data, length);
out:
if (invalid_cms_data) {
free(invalid_cms_data);
}
return status;
}
static OSStatus decrypt_please(uint8_t *data_to_decrypt, size_t length);
static OSStatus encrypt_please(SecCertificateRef recipient, SECOidTag encAlg, int keysize) {
OSStatus status = SECFailure;
SecCmsMessageRef cmsg = NULL;
SecCmsEnvelopedDataRef envd = NULL;
SecCmsContentInfoRef cinfo = NULL;
SecCmsRecipientInfoRef rinfo = NULL;
SecArenaPoolRef arena = NULL;
SecCmsEncoderRef encoder = NULL;
CSSM_DATA cms_data = {
.Data = NULL,
.Length = 0
};
const uint8_t data_to_encrypt[] = "This data is encrypted. Is cool, no?";
require_action_string(cmsg = SecCmsMessageCreate(NULL), out,
status = errSecAllocate, "Failed to create message");
require_action_string(envd = SecCmsEnvelopedDataCreate(cmsg, encAlg, keysize), out,
status = errSecAllocate, "Failed to create enveloped data");
require_action_string(cinfo = SecCmsMessageGetContentInfo(cmsg), out,
status = errSecParam, "Failed to get content info from cms message");
require_noerr_string(status = SecCmsContentInfoSetContentEnvelopedData(cmsg, cinfo, envd), out,
"Failed to set enveloped data in cms message");
require_action_string(cinfo = SecCmsEnvelopedDataGetContentInfo(envd), out,
status = errSecParam, "Failed to get content info from enveloped data");
require_noerr_string(status = SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false), out,
"Failed to set data type in envelope");
require_action_string(rinfo = SecCmsRecipientInfoCreate(cmsg, recipient), out,
status = errSecAllocate, "Failed to create recipient info");
require_noerr_string(status = SecCmsEnvelopedDataAddRecipient(envd, rinfo), out,
"Failed to add recipient info to envelope");
require_noerr_string(status = SecArenaPoolCreate(1024, &arena), out,
"Failed to create arena");
require_noerr_string(status = SecCmsEncoderCreate(cmsg, NULL, NULL, &cms_data, arena, NULL, NULL,
NULL, NULL, NULL, NULL, &encoder), out,
"Failed to create encoder");
require_noerr_string(status = SecCmsEncoderUpdate(encoder, data_to_encrypt, sizeof(data_to_encrypt)), out,
"Failed to update encoder with data");
status = SecCmsEncoderFinish(encoder);
encoder = NULL; require_noerr_quiet(status, out);
require_noerr_string(status = decrypt_please(cms_data.Data, cms_data.Length), out,
"Failed to decrypt the data we just encrypted");
out:
if (encoder) {
SecCmsEncoderDestroy(encoder);
}
if (arena) {
SecArenaPoolFree(arena, false);
}
if (cmsg) {
SecCmsMessageDestroy(cmsg);
}
return status;
}
static OSStatus decrypt_please(uint8_t *data_to_decrypt, size_t length) {
OSStatus status = SECFailure;
SecCmsDecoderRef decoder = NULL;
SecCmsMessageRef cmsg = NULL;
CSSM_DATA_PTR content = NULL;
const uint8_t encrypted_string[] = "This data is encrypted. Is cool, no?";
require_noerr_string(status = SecCmsDecoderCreate(NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &decoder), out,
"Failed to create decoder");
require_noerr_string(status = SecCmsDecoderUpdate(decoder, data_to_decrypt, length), out,
"Failed to add data ");
status = SecCmsDecoderFinish(decoder, &cmsg);
decoder = NULL; require_noerr_quiet(status, out);
require_action_string(content = SecCmsMessageGetContent(cmsg), out,
status = errSecDecode, "Unable to get message contents");
require_action_string(sizeof(encrypted_string) == content->Length, out,
status = -1, "Output size differs from expected");
require_noerr_action_string(memcmp(encrypted_string, content->Data, content->Length), out,
status = -1, "Output differs from expected");
out:
if (cmsg) {
SecCmsMessageDestroy(cmsg);
}
return status;
}
#define kNumberSignTests 10
static void sign_tests(SecIdentityRef identity, bool isRSA) {
is(sign_please(identity, SEC_OID_MD5, false, NULL, 0),
SEC_ERROR_INVALID_ALGORITHM, "Signed with MD5. Not cool.");
is(sign_please(identity, SEC_OID_SHA1, false, (isRSA) ? rsa_sha1 : NULL,
(isRSA) ? sizeof(rsa_sha1) : 0),
errSecSuccess, "Signed with SHA-1");
is(sign_please(identity, SEC_OID_SHA256, false, (isRSA) ? rsa_sha256 : NULL,
(isRSA) ? sizeof(rsa_sha256) : 0),
errSecSuccess, "Signed with SHA-256");
is(sign_please(identity, SEC_OID_SHA384, false, NULL, 0), errSecSuccess, "Signed with SHA-384");
is(sign_please(identity, SEC_OID_SHA512, false, NULL, 0), errSecSuccess, "Signed with SHA-512");
is(sign_please(identity, SEC_OID_MD5, true, NULL, 0),
SEC_ERROR_INVALID_ALGORITHM, "Signed with MD5 and attributes. Not cool.");
is(sign_please(identity, SEC_OID_SHA1, true, (isRSA) ? rsa_sha1_attr : NULL,
(isRSA) ? sizeof(rsa_sha1_attr) : 0),
errSecSuccess, "Signed with SHA-1 and attributes");
is(sign_please(identity, SEC_OID_SHA256, true, (isRSA) ? rsa_sha256_attr : NULL,
(isRSA) ? sizeof(rsa_sha256_attr) : 0),
errSecSuccess, "Signed with SHA-256 and attributes");
is(sign_please(identity, SEC_OID_SHA384, true, NULL, 0),
errSecSuccess, "Signed with SHA-384 and attributes");
is(sign_please(identity, SEC_OID_SHA512, true, NULL, 0),
errSecSuccess, "Signed with SHA-512 and attributes");
}
#define kNumberVerifyTests 12
static void verify_tests(SecKeychainRef kc, bool isRsa) {
is(verify_please(kc, (isRsa) ? rsa_md5 : ec_md5,
(isRsa) ? sizeof(rsa_md5) : sizeof(ec_md5)),
(isRsa) ? errSecSuccess : SECFailure,
"Verify MD5, no attributes");
is(verify_please(kc, (isRsa) ? rsa_sha1 : ec_sha1,
(isRsa) ? sizeof(rsa_sha1) : sizeof(ec_sha1)),
errSecSuccess, "Verify SHA1, no attributes");
is(verify_please(kc, (isRsa) ? rsa_sha256 : ec_sha256,
(isRsa) ? sizeof(rsa_sha256) : sizeof(ec_sha256)),
errSecSuccess, "Verify SHA256, no attributes");
is(verify_please(kc, (isRsa) ? rsa_md5_attr : NULL,
(isRsa) ? sizeof(rsa_md5_attr) : 0),
errSecSuccess, "Verify MD5, with attributes");
is(verify_please(kc, (isRsa) ? rsa_sha1_attr : ec_sha1_attr,
(isRsa) ? sizeof(rsa_sha1_attr) : sizeof(ec_sha1_attr)),
errSecSuccess, "Verify SHA1, with attributes");
is(verify_please(kc, (isRsa) ? rsa_sha256_attr : ec_sha256_attr,
(isRsa) ? sizeof(rsa_sha256_attr) : sizeof(ec_sha256_attr)),
errSecSuccess, "Verify SHA256, with attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_md5 : ec_md5,
(isRsa) ? sizeof(rsa_md5) : sizeof(ec_md5)),
SECFailure, "Verify invalid MD5, no attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_sha1 : ec_sha1,
(isRsa) ? sizeof(rsa_sha1) : sizeof(ec_sha1)),
SECFailure, "Verify invalid SHA1, no attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_sha256 : ec_sha256,
(isRsa) ? sizeof(rsa_sha256) : sizeof(ec_sha256)),
SECFailure, "Verify invalid SHA256, no attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_md5_attr : NULL,
(isRsa) ? sizeof(rsa_md5_attr) : 0),
SECFailure, "Verify invalid MD5, with attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_sha1_attr : ec_sha1_attr,
(isRsa) ? sizeof(rsa_sha1_attr) : sizeof(ec_sha1_attr)),
SECFailure, "Verify invalid SHA1, with attributes");
is(invalidate_and_verify(kc, (isRsa) ? rsa_sha256_attr : ec_sha256_attr,
(isRsa) ? sizeof(rsa_sha256_attr) : sizeof(ec_sha256_attr)),
SECFailure, "Verify invalid SHA256, with attributes");
}
#define kNumberEncryptTests 5
static void encrypt_tests(SecCertificateRef certificate) {
is(encrypt_please(certificate, SEC_OID_DES_EDE3_CBC, 192),
errSecSuccess, "Encrypt with 3DES");
is(encrypt_please(certificate, SEC_OID_RC2_CBC, 128),
errSecSuccess, "Encrypt with 128-bit RC2");
is(encrypt_please(certificate, SEC_OID_AES_128_CBC, 128),
errSecSuccess, "Encrypt with 128-bit AES");
is(encrypt_please(certificate, SEC_OID_AES_192_CBC, 192),
errSecSuccess, "Encrypt with 192-bit AES");
is(encrypt_please(certificate, SEC_OID_AES_256_CBC, 256),
errSecSuccess, "Encrypt with 256-bit AES");
}
#define kNumberDecryptTests 5
static void decrypt_tests(bool isRsa) {
is(decrypt_please((isRsa) ? rsa_3DES : ec_3DES,
(isRsa) ? sizeof(rsa_3DES) : sizeof(ec_3DES)),
errSecSuccess, "Decrypt 3DES");
is(decrypt_please((isRsa) ? rsa_RC2 : ec_RC2,
(isRsa) ? sizeof(rsa_RC2) : sizeof(ec_RC2)),
errSecSuccess, "Decrypt 128-bit RC2");
is(decrypt_please((isRsa) ? rsa_AES_128 : ec_AES_128,
(isRsa) ? sizeof(rsa_AES_128) : sizeof(ec_AES_128)),
errSecSuccess, "Decrypt 128-bit AES");
is(decrypt_please((isRsa) ? rsa_AES_192 : ec_AES_192,
(isRsa) ? sizeof(rsa_AES_192) : sizeof(ec_AES_192)),
errSecSuccess, "Decrypt 192-bit AES");
is(decrypt_please((isRsa) ? rsa_AES_256 : ec_AES_256,
(isRsa) ? sizeof(rsa_AES_256) : sizeof(ec_AES_256)),
errSecSuccess, "Decrypt 256-bit AES");
}
int cms_01_basic(int argc, char *const *argv)
{
plan_tests(2*(kNumberSetupTests + kNumberSignTests + kNumberVerifyTests +
kNumberEncryptTests + kNumberDecryptTests + kNumberCleanupTests));
SecKeychainRef kc = NULL;
SecIdentityRef identity = NULL;
SecCertificateRef certificate = NULL;
kc = setup_keychain(_rsa_identity, sizeof(_rsa_identity), &identity, &certificate);
sign_tests(identity, true);
verify_tests(kc, true);
encrypt_tests(certificate);
decrypt_tests(true);
cleanup_keychain(kc, identity, certificate);
kc = setup_keychain(_ec_identity, sizeof(_ec_identity), &identity, &certificate);
sign_tests(identity, false);
verify_tests(kc, false);
encrypt_tests(certificate);
decrypt_tests(false);
cleanup_keychain(kc, identity, certificate);
return 0;
}