static int do_ber_convert(const char *name, const uint8_t *der_expected, size_t der_len, const uint8_t *ber, size_t ber_len) { CBS in; uint8_t *out; size_t out_len; CBS_init(&in, ber, ber_len); if (!CBS_asn1_ber_to_der(&in, &out, &out_len)) { fprintf(stderr, "%s: CBS_asn1_ber_to_der failed.\n", name); return 0; } if (out == NULL) { if (ber_len != der_len || memcmp(der_expected, ber, ber_len) != 0) { fprintf(stderr, "%s: incorrect unconverted result.\n", name); return 0; } return 1; } if (out_len != der_len || memcmp(out, der_expected, der_len) != 0) { fprintf(stderr, "%s: incorrect converted result.\n", name); return 0; } free(out); return 1; }
/* pkcs7_parse_header reads the non-certificate/non-CRL prefix of a PKCS#7 * SignedData blob from |cbs| and sets |*out| to point to the rest of the * input. If the input is in BER format, then |*der_bytes| will be set to a * pointer that needs to be freed by the caller once they have finished * processing |*out| (which will be pointing into |*der_bytes|). * * It returns one on success or zero on error. On error, |*der_bytes| is * NULL. */ static int pkcs7_parse_header(uint8_t **der_bytes, CBS *out, CBS *cbs) { size_t der_len; CBS in, content_info, content_type, wrapped_signed_data, signed_data; uint64_t version; /* The input may be in BER format. */ *der_bytes = NULL; if (!CBS_asn1_ber_to_der(cbs, der_bytes, &der_len)) { return 0; } if (*der_bytes != NULL) { CBS_init(&in, *der_bytes, der_len); } else { CBS_init(&in, CBS_data(cbs), CBS_len(cbs)); } /* See https://tools.ietf.org/html/rfc2315#section-7 */ if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) || !CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) { goto err; } if (OBJ_cbs2nid(&content_type) != NID_pkcs7_signed) { OPENSSL_PUT_ERROR(X509, pkcs7_parse_header, X509_R_NOT_PKCS7_SIGNED_DATA); goto err; } /* See https://tools.ietf.org/html/rfc2315#section-9.1 */ if (!CBS_get_asn1(&content_info, &wrapped_signed_data, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || !CBS_get_asn1(&wrapped_signed_data, &signed_data, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&signed_data, &version) || !CBS_get_asn1(&signed_data, NULL /* digests */, CBS_ASN1_SET) || !CBS_get_asn1(&signed_data, NULL /* content */, CBS_ASN1_SEQUENCE)) { goto err; } if (version < 1) { OPENSSL_PUT_ERROR(X509, pkcs7_parse_header, X509_R_BAD_PKCS7_VERSION); goto err; } CBS_init(out, CBS_data(&signed_data), CBS_len(&signed_data)); return 1; err: if (*der_bytes) { OPENSSL_free(*der_bytes); *der_bytes = NULL; } return 0; }
/* PKCS12_handle_content_infos parses a series of PKCS#7 ContentInfos in a * SEQUENCE. */ static int PKCS12_handle_content_infos(CBS *content_infos, unsigned depth, struct pkcs12_context *ctx) { uint8_t *der_bytes = NULL; size_t der_len; CBS in; int ret = 0; /* Generally we only expect depths 0 (the top level, with a * pkcs7-encryptedData and a pkcs7-data) and depth 1 (the various PKCS#12 * bags). */ if (depth > 3) { OPENSSL_PUT_ERROR(PKCS8, PKCS12_handle_content_infos, PKCS8_R_PKCS12_TOO_DEEPLY_NESTED); return 0; } /* Although a BER->DER conversion is done at the beginning of |PKCS12_parse|, * the ASN.1 data gets wrapped in OCTETSTRINGs and/or encrypted and the * conversion cannot see through those wrappings. So each time we step * through one we need to convert to DER again. */ if (!CBS_asn1_ber_to_der(content_infos, &der_bytes, &der_len)) { return 0; } if (der_bytes != NULL) { CBS_init(&in, der_bytes, der_len); } else { CBS_init(&in, CBS_data(content_infos), CBS_len(content_infos)); } if (!CBS_get_asn1(&in, &in, CBS_ASN1_SEQUENCE)) { OPENSSL_PUT_ERROR(PKCS8, PKCS12_handle_content_infos, PKCS8_R_BAD_PKCS12_DATA); goto err; } while (CBS_len(&in) > 0) { CBS content_info; if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE)) { OPENSSL_PUT_ERROR(PKCS8, PKCS12_handle_content_infos, PKCS8_R_BAD_PKCS12_DATA); goto err; } if (!PKCS12_handle_content_info(&content_info, depth + 1, ctx)) { goto err; } } /* NSS includes additional data after the SEQUENCE, but it's an (unwrapped) * copy of the same encrypted private key (with the same IV and * ciphertext)! */ ret = 1; err: if (der_bytes != NULL) { OPENSSL_free(der_bytes); } return ret; }