/** * Parse an EC domain parameter identifier as defined in RFC 5656 */ static chunk_t parse_ec_identifier(chunk_t identifier) { chunk_t oid = chunk_empty; if (chunk_equals(identifier, chunk_from_str("nistp256"))) { oid = asn1_build_known_oid(OID_PRIME256V1); } else if (chunk_equals(identifier, chunk_from_str("nistp384"))) { oid = asn1_build_known_oid(OID_SECT384R1); } else if (chunk_equals(identifier, chunk_from_str("nistp521"))) { oid = asn1_build_known_oid(OID_SECT521R1); } else { char ascii[64]; if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len, identifier.ptr) < sizeof(ascii)) { oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii)); } } return oid; }
/** * @brief Builds a contentType attribute * * @return ASN.1 encoded contentType attribute */ chunk_t pkcs7_contentType_attribute(void) { return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_PKCS9_CONTENT_TYPE), asn1_wrap(ASN1_SET, "m", asn1_build_known_oid(OID_PKCS7_DATA))); }
/** * build a DER-encoded contentInfo object */ static chunk_t pkcs7_build_contentInfo(contentInfo_t *cInfo) { return (cInfo->content.ptr) ? asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(cInfo->type), asn1_simple_object(ASN1_CONTEXT_C_0, cInfo->content)) : asn1_build_known_oid(cInfo->type); }
END_TEST /******************************************************************************* * parse_algorithm_identifier */ START_TEST(test_asn1_parse_algorithmIdentifier) { typedef struct { int alg; bool empty; chunk_t parameters; } testdata_t; testdata_t test[] = { { OID_ECDSA_WITH_SHA1, TRUE, chunk_empty }, { OID_SHA1_WITH_RSA, TRUE, chunk_from_chars(0x05, 0x00) }, { OID_3DES_EDE_CBC, FALSE, chunk_from_chars(0x04, 0x01, 0xaa) }, { OID_PBKDF2, FALSE, chunk_from_chars(0x30, 0x01, 0xaa) } }; chunk_t algid, parameters; int i, alg; for (i = 0; i < countof(test); i++) { algid = asn1_wrap(ASN1_SEQUENCE, "mc", asn1_build_known_oid(test[i].alg), test[i].parameters); parameters = chunk_empty; if (i == 2) { alg = asn1_parse_algorithmIdentifier(algid, 0, NULL); } else { alg = asn1_parse_algorithmIdentifier(algid, 0, ¶meters); if (test[i].empty) { ck_assert(parameters.len == 0 && parameters.ptr == NULL); } else { ck_assert(chunk_equals(parameters, test[i].parameters)); } } ck_assert(alg == test[i].alg); chunk_free(&algid); } }
/** * @brief Builds a messageDigest attribute * * * @param[in] blob content to create digest of * @param[in] digest_alg digest algorithm to be used * @return ASN.1 encoded messageDigest attribute * */ chunk_t pkcs7_messageDigest_attribute(chunk_t content, int digest_alg) { chunk_t digest; hash_algorithm_t hash_alg; hasher_t *hasher; hash_alg = hasher_algorithm_from_oid(digest_alg); hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); hasher->allocate_hash(hasher, content, &digest); hasher->destroy(hasher); return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_PKCS9_MESSAGE_DIGEST), asn1_wrap(ASN1_SET, "m", asn1_wrap(ASN1_OCTET_STRING, "m", digest))); }
/* * Defined in header. */ chunk_t asn1_algorithmIdentifier(int oid) { chunk_t parameters; /* some algorithmIdentifiers have a NULL parameters field and some do not */ switch (oid) { case OID_ECDSA_WITH_SHA1: case OID_ECDSA_WITH_SHA224: case OID_ECDSA_WITH_SHA256: case OID_ECDSA_WITH_SHA384: case OID_ECDSA_WITH_SHA512: parameters = chunk_empty; break; default: parameters = asn1_simple_object(ASN1_NULL, chunk_empty); break; } return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(oid), parameters); }
END_TEST /******************************************************************************* * build_known_oid */ START_TEST(test_asn1_build_known_oid) { typedef struct { int n; chunk_t oid; } testdata_t; testdata_t test[] = { { OID_UNKNOWN, chunk_empty }, { OID_MAX, chunk_empty }, { OID_COUNTRY, chunk_from_chars(0x06, 0x03, 0x55, 0x04, 0x06) }, { OID_STRONGSWAN, chunk_from_chars(0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa0, 0x2a, 0x01) } }; int i; chunk_t oid = chunk_empty; for (i = 0; i < countof(test); i++) { oid = asn1_build_known_oid(test[i].n); if (test[i].oid.len == 0) { ck_assert(oid.len == 0 && oid.ptr == NULL); } else { ck_assert(chunk_equals(oid, test[i].oid)); chunk_free(&oid); } } }
/** * Load a generic public key from an SSH key blob */ static sshkey_public_key_t *parse_public_key(chunk_t blob) { bio_reader_t *reader; chunk_t format; reader = bio_reader_create(blob); if (!reader->read_data32(reader, &format)) { DBG1(DBG_LIB, "invalid key format in SSH key"); reader->destroy(reader); return NULL; } if (chunk_equals(format, chunk_from_str("ssh-rsa"))) { chunk_t n, e; if (!reader->read_data32(reader, &e) || !reader->read_data32(reader, &n)) { DBG1(DBG_LIB, "invalid RSA key in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END); } else if (format.len > strlen(ECDSA_PREFIX) && strneq(format.ptr, ECDSA_PREFIX, strlen(ECDSA_PREFIX))) { chunk_t ec_blob, identifier, q, oid, encoded; sshkey_public_key_t *key; ec_blob = reader->peek(reader); reader->destroy(reader); reader = bio_reader_create(ec_blob); if (!reader->read_data32(reader, &identifier) || !reader->read_data32(reader, &q)) { DBG1(DBG_LIB, "invalid ECDSA key in SSH key"); reader->destroy(reader); return NULL; } oid = parse_ec_identifier(identifier); if (!oid.ptr) { DBG1(DBG_LIB, "invalid ECDSA key identifier in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); /* build key from subjectPublicKeyInfo */ encoded = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_EC_PUBLICKEY), oid), asn1_bitstring("c", q)); key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, BUILD_BLOB_ASN1_DER, encoded, BUILD_END); chunk_free(&encoded); return key; } DBG1(DBG_LIB, "unsupported SSH key format %.*s", (int)format.len, format.ptr); reader->destroy(reader); return NULL; }
/** * create a symmetrically encrypted pkcs7 contentInfo object */ chunk_t pkcs7_build_envelopedData(chunk_t data, certificate_t *cert, int enc_alg) { encryption_algorithm_t alg; size_t alg_key_size; chunk_t symmetricKey, protectedKey, iv, in, out; crypter_t *crypter; alg = encryption_algorithm_from_oid(enc_alg, &alg_key_size); crypter = lib->crypto->create_crypter(lib->crypto, alg, alg_key_size/BITS_PER_BYTE); if (crypter == NULL) { DBG1(DBG_LIB, "crypter for %N not available", encryption_algorithm_names, alg); return chunk_empty; } /* generate a true random symmetric encryption key and a pseudo-random iv */ { rng_t *rng; rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE); rng->allocate_bytes(rng, crypter->get_key_size(crypter), &symmetricKey); DBG4(DBG_LIB, "symmetric encryption key %B", &symmetricKey); rng->destroy(rng); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); rng->allocate_bytes(rng, crypter->get_iv_size(crypter), &iv); DBG4(DBG_LIB, "initialization vector: %B", &iv); rng->destroy(rng); } /* pad the data to a multiple of the block size */ { size_t block_size = crypter->get_block_size(crypter); size_t padding = block_size - data.len % block_size; in.len = data.len + padding; in.ptr = malloc(in.len); DBG2(DBG_LIB, "padding %u bytes of data to multiple block size of %u bytes", data.len, in.len); /* copy data */ memcpy(in.ptr, data.ptr, data.len); /* append padding */ memset(in.ptr + data.len, padding, padding); } DBG3(DBG_LIB, "padded unencrypted data %B", &in); /* symmetric encryption of data object */ crypter->set_key(crypter, symmetricKey); crypter->encrypt(crypter, in, iv, &out); crypter->destroy(crypter); chunk_clear(&in); DBG3(DBG_LIB, "encrypted data %B", &out); /* protect symmetric key by public key encryption */ { public_key_t *key = cert->get_public_key(cert); if (key == NULL) { DBG1(DBG_LIB, "public key not found in encryption certificate"); chunk_clear(&symmetricKey); chunk_free(&iv); chunk_free(&out); return chunk_empty; } key->encrypt(key, ENCRYPT_RSA_PKCS1, symmetricKey, &protectedKey); key->destroy(key); } /* build pkcs7 enveloped data object */ { chunk_t contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "mm" , asn1_build_known_oid(enc_alg) , asn1_simple_object(ASN1_OCTET_STRING, iv)); chunk_t encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "mmm" , asn1_build_known_oid(OID_PKCS7_DATA) , contentEncryptionAlgorithm , asn1_wrap(ASN1_CONTEXT_S_0, "m", out)); chunk_t encryptedKey = asn1_wrap(ASN1_OCTET_STRING, "m" , protectedKey); chunk_t recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmmm" , ASN1_INTEGER_0 , pkcs7_build_issuerAndSerialNumber(cert) , asn1_algorithmIdentifier(OID_RSA_ENCRYPTION) , encryptedKey); chunk_t cInfo; contentInfo_t envelopedData; envelopedData.type = OID_PKCS7_ENVELOPED_DATA; envelopedData.content = asn1_wrap(ASN1_SEQUENCE, "cmm" , ASN1_INTEGER_0 , asn1_wrap(ASN1_SET, "m", recipientInfo) , encryptedContentInfo); cInfo = pkcs7_build_contentInfo(&envelopedData); DBG3(DBG_LIB, "envelopedData %B", &cInfo); chunk_free(&envelopedData.content); chunk_free(&iv); chunk_clear(&symmetricKey); return cInfo; } }