static int test_asn1_uint64(void) { size_t i; for (i = 0; i < sizeof(kAsn1Uint64Tests) / sizeof(kAsn1Uint64Tests[0]); i++) { const ASN1_UINT64_TEST *test = &kAsn1Uint64Tests[i]; CBS cbs; uint64_t value; CBB cbb; uint8_t *out; size_t len; CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len); if (!CBS_get_asn1_uint64(&cbs, &value) || CBS_len(&cbs) != 0 || value != test->value) { return 0; } if (!CBB_init(&cbb, 0)) { return 0; } if (!CBB_add_asn1_uint64(&cbb, test->value) || !CBB_finish(&cbb, &out, &len)) { CBB_cleanup(&cbb); return 0; } if (len != test->encoding_len || memcmp(out, test->encoding, len) != 0) { free(out); return 0; } free(out); } for (i = 0; i < sizeof(kAsn1InvalidUint64Tests) / sizeof(kAsn1InvalidUint64Tests[0]); i++) { const ASN1_INVALID_UINT64_TEST *test = &kAsn1InvalidUint64Tests[i]; CBS cbs; uint64_t value; CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len); if (CBS_get_asn1_uint64(&cbs, &value)) { return 0; } } return 1; }
DSA *DSA_parse_private_key(CBS *cbs) { DSA *ret = DSA_new(); if (ret == NULL) { return NULL; } CBS child; uint64_t version; if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&child, &version)) { OPENSSL_PUT_ERROR(DSA, DSA_R_DECODE_ERROR); goto err; } if (version != 0) { OPENSSL_PUT_ERROR(DSA, DSA_R_BAD_VERSION); goto err; } if (!parse_integer(&child, &ret->p) || !parse_integer(&child, &ret->q) || !parse_integer(&child, &ret->g) || !parse_integer(&child, &ret->pub_key) || !parse_integer(&child, &ret->priv_key) || CBS_len(&child) != 0) { OPENSSL_PUT_ERROR(DSA, DSA_R_DECODE_ERROR); goto err; } return ret; err: DSA_free(ret); return NULL; }
/* 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; }
RSA *RSA_parse_private_key(CBS *cbs) { BN_CTX *ctx = NULL; RSA *ret = RSA_new(); if (ret == NULL) { return NULL; } CBS child; uint64_t version; if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&child, &version)) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING); goto err; } if (version != kVersionTwoPrime) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_VERSION); goto err; } if (!parse_integer(&child, &ret->n) || !parse_integer(&child, &ret->e) || !parse_integer(&child, &ret->d) || !parse_integer(&child, &ret->p) || !parse_integer(&child, &ret->q) || !parse_integer(&child, &ret->dmp1) || !parse_integer(&child, &ret->dmq1) || !parse_integer(&child, &ret->iqmp)) { goto err; } if (CBS_len(&child) != 0) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING); goto err; } BN_CTX_free(ctx); return ret; err: BN_CTX_free(ctx); RSA_free(ret); return NULL; }
EVP_PKEY *EVP_parse_private_key(CBS *cbs) { // Parse the PrivateKeyInfo. CBS pkcs8, algorithm, key; uint64_t version; int type; if (!CBS_get_asn1(cbs, &pkcs8, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&pkcs8, &version) || version != 0 || !CBS_get_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) || !parse_key_type(&algorithm, &type) || !CBS_get_asn1(&pkcs8, &key, CBS_ASN1_OCTETSTRING)) { OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); return NULL; } // A PrivateKeyInfo ends with a SET of Attributes which we ignore. // Set up an |EVP_PKEY| of the appropriate type. EVP_PKEY *ret = EVP_PKEY_new(); if (ret == NULL || !EVP_PKEY_set_type(ret, type)) { goto err; } // Call into the type-specific PrivateKeyInfo decoding function. if (ret->ameth->priv_decode == NULL) { OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM); goto err; } if (!ret->ameth->priv_decode(ret, &algorithm, &key)) { goto err; } return ret; err: EVP_PKEY_free(ret); return NULL; }
SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method, CRYPTO_BUFFER_POOL *pool) { SSL_SESSION *ret = ssl_session_new(x509_method); if (ret == NULL) { goto err; } CBS session; uint64_t version, ssl_version; if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&session, &version) || version != kVersion || !CBS_get_asn1_uint64(&session, &ssl_version)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->ssl_version = ssl_version; CBS cipher; uint16_t cipher_value; if (!CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || !CBS_get_u16(&cipher, &cipher_value) || CBS_len(&cipher) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->cipher = SSL_get_cipher_by_value(cipher_value); if (ret->cipher == NULL) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CIPHER); goto err; } CBS session_id, master_key; if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH || !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } OPENSSL_memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); ret->session_id_length = CBS_len(&session_id); OPENSSL_memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); ret->master_key_length = CBS_len(&master_key); CBS child; uint64_t timeout; if (!CBS_get_asn1(&session, &child, kTimeTag) || !CBS_get_asn1_uint64(&child, &ret->time) || !CBS_get_asn1(&session, &child, kTimeoutTag) || !CBS_get_asn1_uint64(&child, &timeout) || timeout > UINT32_MAX) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->timeout = (uint32_t)timeout; CBS peer; int has_peer; if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) || (has_peer && CBS_len(&peer) == 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* |peer| is processed with the certificate chain. */ if (!SSL_SESSION_parse_bounded_octet_string( &session, ret->sid_ctx, &ret->sid_ctx_length, sizeof(ret->sid_ctx), kSessionIDContextTag) || !SSL_SESSION_parse_long(&session, &ret->verify_result, kVerifyResultTag, X509_V_OK) || !SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, kHostNameTag) || !SSL_SESSION_parse_string(&session, &ret->psk_identity, kPSKIdentityTag) || !SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint, kTicketLifetimeHintTag, 0) || !SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, &ret->tlsext_ticklen, kTicketTag)) { goto err; } if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) { CBS peer_sha256; if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) || !CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) || CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) || CBS_len(&child) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } OPENSSL_memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256)); ret->peer_sha256_valid = 1; } else { ret->peer_sha256_valid = 0; } if (!SSL_SESSION_parse_bounded_octet_string( &session, ret->original_handshake_hash, &ret->original_handshake_hash_len, sizeof(ret->original_handshake_hash), kOriginalHandshakeHashTag) || !SSL_SESSION_parse_octet_string( &session, &ret->tlsext_signed_cert_timestamp_list, &ret->tlsext_signed_cert_timestamp_list_length, kSignedCertTimestampListTag) || !SSL_SESSION_parse_octet_string( &session, &ret->ocsp_response, &ret->ocsp_response_length, kOCSPResponseTag)) { goto err; } int extended_master_secret; if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, kExtendedMasterSecretTag, 0 /* default to false */)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->extended_master_secret = !!extended_master_secret; if (!SSL_SESSION_parse_u16(&session, &ret->group_id, kGroupIDTag, 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } CBS cert_chain; CBS_init(&cert_chain, NULL, 0); int has_cert_chain; if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain, kCertChainTag) || (has_cert_chain && CBS_len(&cert_chain) == 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } if (has_cert_chain && !has_peer) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } if (has_peer || has_cert_chain) { ret->certs = sk_CRYPTO_BUFFER_new_null(); if (ret->certs == NULL) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (has_peer) { /* TODO(agl): this should use the |SSL_CTX|'s pool. */ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, pool); if (buffer == NULL || !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { CRYPTO_BUFFER_free(buffer); OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } while (CBS_len(&cert_chain) > 0) { CBS cert; if (!CBS_get_any_asn1_element(&cert_chain, &cert, NULL, NULL) || CBS_len(&cert) == 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* TODO(agl): this should use the |SSL_CTX|'s pool. */ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, pool); if (buffer == NULL || !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { CRYPTO_BUFFER_free(buffer); OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } } if (!x509_method->session_cache_objects(ret)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } CBS age_add; int age_add_present; if (!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present, kTicketAgeAddTag) || (age_add_present && !CBS_get_u32(&age_add, &ret->ticket_age_add)) || CBS_len(&age_add) != 0) { goto err; } ret->ticket_age_add_valid = age_add_present; int is_server; if (!CBS_get_optional_asn1_bool(&session, &is_server, kIsServerTag, 1 /* default to true */)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* TODO: in time we can include |is_server| for servers too, then we can enforce that client and server sessions are never mixed up. */ ret->is_server = is_server; if (!SSL_SESSION_parse_u16(&session, &ret->peer_signature_algorithm, kPeerSignatureAlgorithmTag, 0) || !SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data, kTicketMaxEarlyDataTag, 0) || !SSL_SESSION_parse_u32(&session, &ret->auth_timeout, kAuthTimeoutTag, ret->timeout) || !SSL_SESSION_parse_octet_string(&session, &ret->early_alpn, &ret->early_alpn_len, kEarlyALPNTag) || CBS_len(&session) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } return ret; err: SSL_SESSION_free(ret); return NULL; }
RSA *RSA_parse_private_key(CBS *cbs) { BN_CTX *ctx = NULL; BIGNUM *product_of_primes_so_far = NULL; RSA *ret = RSA_new(); if (ret == NULL) { return NULL; } CBS child; uint64_t version; if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&child, &version)) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING); goto err; } if (version != kVersionTwoPrime && version != kVersionMulti) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_VERSION); goto err; } if (!parse_integer(&child, &ret->n) || !parse_integer(&child, &ret->e) || !parse_integer(&child, &ret->d) || !parse_integer(&child, &ret->p) || !parse_integer(&child, &ret->q) || !parse_integer(&child, &ret->dmp1) || !parse_integer(&child, &ret->dmq1) || !parse_integer(&child, &ret->iqmp)) { goto err; } if (version == kVersionMulti) { /* Although otherPrimeInfos is written as OPTIONAL in RFC 3447, it later * says "[otherPrimeInfos] shall be omitted if version is 0 and shall * contain at least one instance of OtherPrimeInfo if version is 1." The * OPTIONAL is just so both versions share a single definition. */ CBS other_prime_infos; if (!CBS_get_asn1(&child, &other_prime_infos, CBS_ASN1_SEQUENCE) || CBS_len(&other_prime_infos) == 0) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING); goto err; } ret->additional_primes = sk_RSA_additional_prime_new_null(); if (ret->additional_primes == NULL) { OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE); goto err; } ctx = BN_CTX_new(); product_of_primes_so_far = BN_new(); if (ctx == NULL || product_of_primes_so_far == NULL || !BN_mul(product_of_primes_so_far, ret->p, ret->q, ctx)) { goto err; } while (CBS_len(&other_prime_infos) > 0) { RSA_additional_prime *ap = rsa_parse_additional_prime(&other_prime_infos); if (ap == NULL) { goto err; } if (!sk_RSA_additional_prime_push(ret->additional_primes, ap)) { OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE); RSA_additional_prime_free(ap); goto err; } ap->r = BN_dup(product_of_primes_so_far); if (ap->r == NULL || !BN_mul(product_of_primes_so_far, product_of_primes_so_far, ap->prime, ctx)) { goto err; } } } if (CBS_len(&child) != 0) { OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING); goto err; } BN_CTX_free(ctx); BN_free(product_of_primes_so_far); return ret; err: BN_CTX_free(ctx); BN_free(product_of_primes_so_far); RSA_free(ret); return NULL; }
SSL_SESSION * d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length) { CBS cbs, session, cipher_suite, session_id, master_key, peer_cert; CBS hostname, ticket; uint64_t version, tls_version, stime, timeout, verify_result, lifetime; const unsigned char *peer_cert_bytes; uint16_t cipher_value; SSL_SESSION *s = NULL; size_t data_len; int present; if (a != NULL) s = *a; if (s == NULL) { if ((s = SSL_SESSION_new()) == NULL) { SSLerrorx(ERR_R_MALLOC_FAILURE); return (NULL); } } CBS_init(&cbs, *pp, length); if (!CBS_get_asn1(&cbs, &session, CBS_ASN1_SEQUENCE)) goto err; /* Session ASN1 version. */ if (!CBS_get_asn1_uint64(&session, &version)) goto err; if (version != SSL_SESSION_ASN1_VERSION) goto err; /* TLS/SSL Protocol Version. */ if (!CBS_get_asn1_uint64(&session, &tls_version)) goto err; if (tls_version > INT_MAX) goto err; s->ssl_version = (int)tls_version; /* Cipher suite. */ if (!CBS_get_asn1(&session, &cipher_suite, CBS_ASN1_OCTETSTRING)) goto err; if (!CBS_get_u16(&cipher_suite, &cipher_value)) goto err; if (CBS_len(&cipher_suite) != 0) goto err; /* XXX - populate cipher instead? */ s->cipher = NULL; s->cipher_id = SSL3_CK_ID | cipher_value; /* Session ID. */ if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING)) goto err; if (!CBS_write_bytes(&session_id, s->session_id, sizeof(s->session_id), &data_len)) goto err; if (data_len > UINT_MAX) goto err; s->session_id_length = (unsigned int)data_len; /* Master key. */ if (!CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING)) goto err; if (!CBS_write_bytes(&master_key, s->master_key, sizeof(s->master_key), &data_len)) goto err; if (data_len > INT_MAX) goto err; s->master_key_length = (int)data_len; /* Time [1]. */ s->time = time(NULL); if (!CBS_get_optional_asn1_uint64(&session, &stime, SSLASN1_TIME_TAG, 0)) goto err; if (stime > time_max()) goto err; if (stime != 0) s->time = (time_t)stime; /* Timeout [2]. */ s->timeout = 3; if (!CBS_get_optional_asn1_uint64(&session, &timeout, SSLASN1_TIMEOUT_TAG, 0)) goto err; if (timeout > LONG_MAX) goto err; if (timeout != 0) s->timeout = (long)timeout; /* Peer certificate [3]. */ X509_free(s->peer); s->peer = NULL; if (!CBS_get_optional_asn1(&session, &peer_cert, &present, SSLASN1_PEER_CERT_TAG)) goto err; if (present) { data_len = CBS_len(&peer_cert); if (data_len > LONG_MAX) goto err; peer_cert_bytes = CBS_data(&peer_cert); if (d2i_X509(&s->peer, &peer_cert_bytes, (long)data_len) == NULL) goto err; } /* Session ID context [4]. */ s->sid_ctx_length = 0; if (!CBS_get_optional_asn1_octet_string(&session, &session_id, &present, SSLASN1_SESSION_ID_CTX_TAG)) goto err; if (present) { if (!CBS_write_bytes(&session_id, (uint8_t *)&s->sid_ctx, sizeof(s->sid_ctx), &data_len)) goto err; if (data_len > UINT_MAX) goto err; s->sid_ctx_length = (unsigned int)data_len; } /* Verify result [5]. */ s->verify_result = X509_V_OK; if (!CBS_get_optional_asn1_uint64(&session, &verify_result, SSLASN1_VERIFY_RESULT_TAG, X509_V_OK)) goto err; if (verify_result > LONG_MAX) goto err; s->verify_result = (long)verify_result; /* Hostname [6]. */ free(s->tlsext_hostname); s->tlsext_hostname = NULL; if (!CBS_get_optional_asn1_octet_string(&session, &hostname, &present, SSLASN1_HOSTNAME_TAG)) goto err; if (present) { if (CBS_contains_zero_byte(&hostname)) goto err; if (!CBS_strdup(&hostname, &s->tlsext_hostname)) goto err; } /* PSK identity hint [7]. */ /* PSK identity [8]. */ /* Ticket lifetime [9]. */ s->tlsext_tick_lifetime_hint = 0; /* XXX - tlsext_ticklen is not yet set... */ if (s->tlsext_ticklen > 0 && s->session_id_length > 0) s->tlsext_tick_lifetime_hint = -1; if (!CBS_get_optional_asn1_uint64(&session, &lifetime, SSLASN1_LIFETIME_TAG, 0)) goto err; if (lifetime > LONG_MAX) goto err; if (lifetime > 0) s->tlsext_tick_lifetime_hint = (long)lifetime; /* Ticket [10]. */ free(s->tlsext_tick); s->tlsext_tick = NULL; if (!CBS_get_optional_asn1_octet_string(&session, &ticket, &present, SSLASN1_TICKET_TAG)) goto err; if (present) { if (!CBS_stow(&ticket, &s->tlsext_tick, &s->tlsext_ticklen)) goto err; } /* Compression method [11]. */ /* SRP username [12]. */ *pp = CBS_data(&cbs); if (a != NULL) *a = s; return (s); err: ERR_asprintf_error_data("offset=%d", (int)(CBS_data(&cbs) - *pp)); if (s != NULL && (a == NULL || *a != s)) SSL_SESSION_free(s); return (NULL); }