From fc06ecbf83914c538c771d0f0969cc6afe0ee4bf Mon Sep 17 00:00:00 2001 From: dkostic <25055813+dkostic@users.noreply.github.com> Date: Mon, 20 May 2024 19:57:45 +0000 Subject: [PATCH 01/42] [EC] Unify point doubling for P-256/384/521 (#1567) Implement and use a single version of point doubling for implementations of NIST curves P-384, P-521, and Fiat-crypto based implementation of P-256. The change does not affect performance. Point addition will be unified in a subsequent change. I verified the performance was not affected on Graviton 3, Intel, and M1 CPUs. Example for M1: ``` Before Did 2882750 EC POINT P-384 dbl operations in 1000082us (2882513.6 ops/sec) Did 1600000 EC POINT P-384 add operations in 1000497us (1599205.2 ops/sec) Did 7051 EC POINT P-384 mul operations in 1078289us (6539.1 ops/sec) Did 28000 EC POINT P-384 mul base operations in 1000115us (27996.8 ops/sec) Did 5632 EC POINT P-384 mul public operations in 1062456us (5300.9 ops/sec) Did 2685500 EC POINT P-521 dbl operations in 1000037us (2685400.6 ops/sec) Did 1435000 EC POINT P-521 add operations in 1000129us (1434814.9 ops/sec) Did 4928 EC POINT P-521 mul operations in 1055318us (4669.7 ops/sec) Did 19000 EC POINT P-521 mul base operations in 1022199us (18587.4 ops/sec) Did 3850 EC POINT P-521 mul public operations in 1036809us (3713.3 ops/sec) After: Did 2888250 EC POINT P-384 dbl operations in 1000028us (2888169.1 ops/sec) Did 1593000 EC POINT P-384 add operations in 1000405us (1592355.1 ops/sec) Did 6875 EC POINT P-384 mul operations in 1054301us (6520.9 ops/sec) Did 28000 EC POINT P-384 mul base operations in 1000818us (27977.1 ops/sec) Did 5555 EC POINT P-384 mul public operations in 1056370us (5258.6 ops/sec) Did 2775250 EC POINT P-521 dbl operations in 1000021us (2775191.7 ops/sec) Did 1435000 EC POINT P-521 add operations in 1000085us (1434878.0 ops/sec) Did 4840 EC POINT P-521 mul operations in 1044164us (4635.3 ops/sec) Did 19000 EC POINT P-521 mul base operations in 1027887us (18484.5 ops/sec) Did 3883 EC POINT P-521 mul public operations in 1051447us (3693.0 ops/sec) ``` --- crypto/fipsmodule/bcm.c | 2 +- crypto/fipsmodule/ec/ec_nistp.c | 112 ++++++++++++++++++++++++++ crypto/fipsmodule/ec/ec_nistp.h | 65 ++++++++++++++++ crypto/fipsmodule/ec/make_tables.go | 6 +- crypto/fipsmodule/ec/p256.c | 74 +++--------------- crypto/fipsmodule/ec/p384.c | 117 ++++++---------------------- crypto/fipsmodule/ec/p384_table.h | 2 +- crypto/fipsmodule/ec/p521.c | 115 ++++++--------------------- crypto/fipsmodule/ec/p521_table.h | 4 +- 9 files changed, 244 insertions(+), 253 deletions(-) create mode 100644 crypto/fipsmodule/ec/ec_nistp.c create mode 100644 crypto/fipsmodule/ec/ec_nistp.h diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c index 517e3fdbfe..3915104447 100644 --- a/crypto/fipsmodule/bcm.c +++ b/crypto/fipsmodule/bcm.c @@ -28,7 +28,6 @@ // to control the order. $b section will place bcm in between the start/end markers // which are in $a and $z. #if defined(BORINGSSL_FIPS) && defined(OPENSSL_WINDOWS) - #pragma code_seg(".fipstx$b") #pragma data_seg(".fipsda$b") #pragma const_seg(".fipsco$b") @@ -93,6 +92,7 @@ #include "ec/ec.c" #include "ec/ec_key.c" #include "ec/ec_montgomery.c" +#include "ec/ec_nistp.c" #include "ec/felem.c" #include "ec/oct.c" #include "ec/p224-64.c" diff --git a/crypto/fipsmodule/ec/ec_nistp.c b/crypto/fipsmodule/ec/ec_nistp.c new file mode 100644 index 0000000000..a4484dc06c --- /dev/null +++ b/crypto/fipsmodule/ec/ec_nistp.c @@ -0,0 +1,112 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +// In this file we will implement elliptic curve point operations for +// NIST curves P-256, P-384, and P-521. The idea is to implement the operations +// in a generic way such that the code can be reused instead of having +// a separate implementation for each of the curves. We implement: +// 1. point addition, +// 2. point doubling, +// 3. scalar multiplication of a base point, +// 4. scalar multiplication of an arbitrary point, +// 5. scalar multiplication of a base and an arbitrary point. +// +// Matrix of what has been done so far: +// +// | op | P-521 | P-384 | P-256 | +// |----------------------------| +// | 1. | | | | +// | 2. | x | x | x* | +// | 3. | | | | +// | 4. | | | | +// | 5. | | | | +// * For P-256, only the Fiat-crypto implementation in p256.c is replaced. + +#include "ec_nistp.h" + +// Some of the functions below need temporary field element variables. +// To avoid dynamic allocation we define nistp_felem type to have the maximum +// size possible (which is currently P-521 curve). The values are hard-coded +// for the moment, this will be fixed when we migrate the whole P-521 +// implementation to ec_nistp.c. +#if defined(EC_NISTP_USE_64BIT_LIMB) +#define NISTP_FELEM_MAX_NUM_OF_LIMBS (9) +#else +#define NISTP_FELEM_MAX_NUM_OF_LIMBS (19) +#endif +typedef ec_nistp_felem_limb ec_nistp_felem[NISTP_FELEM_MAX_NUM_OF_LIMBS]; + +// Group operations +// ---------------- +// +// Building on top of the field operations we have the operations on the +// elliptic curve group itself. Points on the curve are represented in Jacobian +// coordinates. +// +// ec_nistp_point_double calculates 2*(x_in, y_in, z_in) +// +// The method is based on: +// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b +// for which there is a Coq transcription and correctness proof: +// +// +// +// However, we slighty changed the computation for efficiency (see the full +// explanation within the function body), which makes the Coq proof above +// not applicable to our implementation. +// TODO(awslc): Write a Coq correctness proof for our version of the algorithm. +// +// Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed; +// while x_out == y_in is not (maybe this works, but it's not tested). +void ec_nistp_point_double(const ec_nistp_felem_meth *ctx, + ec_nistp_felem_limb *x_out, + ec_nistp_felem_limb *y_out, + ec_nistp_felem_limb *z_out, + const ec_nistp_felem_limb *x_in, + const ec_nistp_felem_limb *y_in, + const ec_nistp_felem_limb *z_in) { + ec_nistp_felem delta, gamma, beta, ftmp, ftmp2, tmptmp, alpha, fourbeta; + // delta = z^2 + ctx->sqr(delta, z_in); + // gamma = y^2 + ctx->sqr(gamma, y_in); + // beta = x*gamma + ctx->mul(beta, x_in, gamma); + + // alpha = 3*(x-delta)*(x+delta) + ctx->sub(ftmp, x_in, delta); + ctx->add(ftmp2, x_in, delta); + + ctx->add(tmptmp, ftmp2, ftmp2); + ctx->add(ftmp2, ftmp2, tmptmp); + ctx->mul(alpha, ftmp, ftmp2); + + // x' = alpha^2 - 8*beta + ctx->sqr(x_out, alpha); + ctx->add(fourbeta, beta, beta); + ctx->add(fourbeta, fourbeta, fourbeta); + ctx->add(tmptmp, fourbeta, fourbeta); + ctx->sub(x_out, x_out, tmptmp); + + // z' = (y + z)^2 - gamma - delta + // The following calculation differs from the Coq proof cited above. + // The proof is for: + // add(delta, gamma, delta); + // add(ftmp, y_in, z_in); + // square(z_out, ftmp); + // sub(z_out, z_out, delta); + // Our operations sequence is a bit more efficient because it saves us + // a certain number of conditional moves. + ctx->add(ftmp, y_in, z_in); + ctx->sqr(z_out, ftmp); + ctx->sub(z_out, z_out, gamma); + ctx->sub(z_out, z_out, delta); + + // y' = alpha*(4*beta - x') - 8*gamma^2 + ctx->sub(y_out, fourbeta, x_out); + ctx->add(gamma, gamma, gamma); + ctx->sqr(gamma, gamma); + ctx->mul(y_out, alpha, y_out); + ctx->add(gamma, gamma, gamma); + ctx->sub(y_out, y_out, gamma); +} diff --git a/crypto/fipsmodule/ec/ec_nistp.h b/crypto/fipsmodule/ec/ec_nistp.h new file mode 100644 index 0000000000..027380581f --- /dev/null +++ b/crypto/fipsmodule/ec/ec_nistp.h @@ -0,0 +1,65 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC +#ifndef EC_NISTP_H +#define EC_NISTP_H + +#include + +#include + +// We have two implementations of the field arithmetic for NIST curves: +// - Fiat-crypto +// - s2n-bignum +// Both Fiat-crypto and s2n-bignum implementations are formally verified. +// Fiat-crypto implementation is fully portable C code, while s2n-bignum +// implements the operations in assembly for x86_64 and aarch64 platforms. +// If (1) x86_64 or aarch64, (2) linux or apple, and (3) OPENSSL_NO_ASM is not +// set, s2n-bignum path is capable. +#if !defined(OPENSSL_NO_ASM) && \ + (defined(OPENSSL_LINUX) || defined(OPENSSL_APPLE)) && \ + ((defined(OPENSSL_X86_64) && !defined(MY_ASSEMBLER_IS_TOO_OLD_FOR_AVX)) || \ + defined(OPENSSL_AARCH64)) +# define EC_NISTP_USE_S2N_BIGNUM +# define EC_NISTP_USE_64BIT_LIMB +#else +// Fiat-crypto has both 64-bit and 32-bit implementation. +# if defined(BORINGSSL_HAS_UINT128) +# define EC_NISTP_USE_64BIT_LIMB +# endif +#endif + +#if defined(EC_NISTP_USE_64BIT_LIMB) +typedef uint64_t ec_nistp_felem_limb; +#else +typedef uint32_t ec_nistp_felem_limb; +#endif + +// ec_nistp_felem_meth is a struct that holds pointers to implementations of field +// arithmetic functions for specific curves. It is meant to be used +// in higher level functions like this: +// void point_double(nistp_felem_methods *ctx, ...) { +// ctx->add(...); +// ctx->mul(...); +// } +// This makes the functions reusable between different curves by simply +// providing an appropriate methods object. +typedef struct { + void (*add)(ec_nistp_felem_limb *c, const ec_nistp_felem_limb *a, const ec_nistp_felem_limb *b); + void (*sub)(ec_nistp_felem_limb *c, const ec_nistp_felem_limb *a, const ec_nistp_felem_limb *b); + void (*mul)(ec_nistp_felem_limb *c, const ec_nistp_felem_limb *a, const ec_nistp_felem_limb *b); + void (*sqr)(ec_nistp_felem_limb *c, const ec_nistp_felem_limb *a); +} ec_nistp_felem_meth; + +const ec_nistp_felem_meth *p256_felem_methods(void); +const ec_nistp_felem_meth *p384_felem_methods(void); +const ec_nistp_felem_meth *p521_felem_methods(void); + +void ec_nistp_point_double(const ec_nistp_felem_meth *ctx, + ec_nistp_felem_limb *x_out, + ec_nistp_felem_limb *y_out, + ec_nistp_felem_limb *z_out, + const ec_nistp_felem_limb *x_in, + const ec_nistp_felem_limb *y_in, + const ec_nistp_felem_limb *z_in); +#endif // EC_NISTP_H + diff --git a/crypto/fipsmodule/ec/make_tables.go b/crypto/fipsmodule/ec/make_tables.go index 7ea6fcd4d0..08e092f656 100644 --- a/crypto/fipsmodule/ec/make_tables.go +++ b/crypto/fipsmodule/ec/make_tables.go @@ -392,7 +392,7 @@ func writeP384Table(path string) error { // is based on the generation method in: // https://gitlab.com/nisec/ecckiila/-/blob/master/main.py#L296 -#if defined(P384_USE_64BIT_LIMBS_FELEM)` +#if defined(EC_NISTP_USE_64BIT_LIMB)` table_def_str := fmt.Sprintf("static const p384_felem p384_g_pre_comp[%d][%d][2] = ", num_subtables, pts_per_subtable) @@ -462,7 +462,7 @@ func writeP521Table(path string) error { // is based on the generation method in: // https://gitlab.com/nisec/ecckiila/-/blob/master/main.py#L296 -#if defined(P521_USE_S2N_BIGNUM_FIELD_ARITH)` +#if defined(EC_NISTP_USE_S2N_BIGNUM)` table_def_str := fmt.Sprintf("static const p521_felem p521_g_pre_comp[%d][%d][2] = ", num_subtables, pts_per_subtable) @@ -472,7 +472,7 @@ func writeP521Table(path string) error { if err := writeTables(w, curve, tables, writeU64, nil); err != nil { return err } - if _, err := io.WriteString(w, ";\n#else\n#if defined(P521_USE_64BIT_LIMBS_FELEM)\n" + table_def_str); err != nil { + if _, err := io.WriteString(w, ";\n#else\n#if defined(EC_NISTP_USE_64BIT_LIMB)\n" + table_def_str); err != nil { return err } // P-521 Fiat-crypto implementation for 64-bit systems represents a field diff --git a/crypto/fipsmodule/ec/p256.c b/crypto/fipsmodule/ec/p256.c index 7572b68202..ba81fcc912 100644 --- a/crypto/fipsmodule/ec/p256.c +++ b/crypto/fipsmodule/ec/p256.c @@ -30,6 +30,7 @@ #include "../../internal.h" #include "../delocate.h" #include "./internal.h" +#include "ec_nistp.h" #if defined(BORINGSSL_HAS_UINT128) #define BORINGSSL_NISTP256_64BIT 1 @@ -166,73 +167,20 @@ static void fiat_p256_inv_square(fiat_p256_felem out, fiat_p256_square(out, ret); // 2^256 - 2^224 + 2^192 + 2^96 - 2^2 } -// Group operations -// ---------------- -// -// Building on top of the field operations we have the operations on the -// elliptic curve group itself. Points on the curve are represented in Jacobian -// coordinates. -// -// Both operations were transcribed to Coq and proven to correspond to naive -// implementations using Affine coordinates, for all suitable fields. In the -// Coq proofs, issues of constant-time execution and memory layout (aliasing) -// conventions were not considered. Specification of affine coordinates: -// -// As a sanity check, a proof that these points form a commutative group: -// - -// fiat_p256_point_double calculates 2*(x_in, y_in, z_in) -// -// The method is taken from: -// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b -// -// Coq transcription and correctness proof: -// -// -// -// Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed. -// while x_out == y_in is not (maybe this works, but it's not tested). -static void fiat_p256_point_double(fiat_p256_felem x_out, fiat_p256_felem y_out, +DEFINE_METHOD_FUNCTION(ec_nistp_felem_meth, p256_felem_methods) { + out->add = fiat_p256_add; + out->sub = fiat_p256_sub; + out->mul = fiat_p256_mul; + out->sqr = fiat_p256_square; +} + +static void fiat_p256_point_double(fiat_p256_felem x_out, + fiat_p256_felem y_out, fiat_p256_felem z_out, const fiat_p256_felem x_in, const fiat_p256_felem y_in, const fiat_p256_felem z_in) { - fiat_p256_felem delta, gamma, beta, ftmp, ftmp2, tmptmp, alpha, fourbeta; - // delta = z^2 - fiat_p256_square(delta, z_in); - // gamma = y^2 - fiat_p256_square(gamma, y_in); - // beta = x*gamma - fiat_p256_mul(beta, x_in, gamma); - - // alpha = 3*(x-delta)*(x+delta) - fiat_p256_sub(ftmp, x_in, delta); - fiat_p256_add(ftmp2, x_in, delta); - - fiat_p256_add(tmptmp, ftmp2, ftmp2); - fiat_p256_add(ftmp2, ftmp2, tmptmp); - fiat_p256_mul(alpha, ftmp, ftmp2); - - // x' = alpha^2 - 8*beta - fiat_p256_square(x_out, alpha); - fiat_p256_add(fourbeta, beta, beta); - fiat_p256_add(fourbeta, fourbeta, fourbeta); - fiat_p256_add(tmptmp, fourbeta, fourbeta); - fiat_p256_sub(x_out, x_out, tmptmp); - - // z' = (y + z)^2 - gamma - delta - fiat_p256_add(delta, gamma, delta); - fiat_p256_add(ftmp, y_in, z_in); - fiat_p256_square(z_out, ftmp); - fiat_p256_sub(z_out, z_out, delta); - - // y' = alpha*(4*beta - x') - 8*gamma^2 - fiat_p256_sub(y_out, fourbeta, x_out); - fiat_p256_add(gamma, gamma, gamma); - fiat_p256_square(gamma, gamma); - fiat_p256_mul(y_out, alpha, y_out); - fiat_p256_add(gamma, gamma, gamma); - fiat_p256_sub(y_out, y_out, gamma); + ec_nistp_point_double(p256_felem_methods(), x_out, y_out, z_out, x_in, y_in, z_in); } // fiat_p256_point_add calculates (x1, y1, z1) + (x2, y2, z2) diff --git a/crypto/fipsmodule/ec/p384.c b/crypto/fipsmodule/ec/p384.c index 9e1011a123..777fd20b26 100644 --- a/crypto/fipsmodule/ec/p384.c +++ b/crypto/fipsmodule/ec/p384.c @@ -14,49 +14,21 @@ #include "../cpucap/internal.h" #include "../delocate.h" #include "internal.h" +#include "ec_nistp.h" #if !defined(OPENSSL_SMALL) -// We have two implementations of the field arithmetic for P-384 curve: -// - Fiat-crypto -// - s2n-bignum -// Both Fiat-crypto and s2n-bignum implementations are formally verified. -// Fiat-crypto implementation is fully portable C code, while s2n-bignum -// implements the operations in assembly for x86_64 and aarch64 platforms. -// All the P-384 field operations supported by Fiat-crypto are supported -// by s2n-bignum as well, so s2n-bignum can be used as a drop-in replacement -// when appropriate. To do that we define macros for the functions. -// For example, field addition macro is either defined as -// #define p384_felem_add(out, in0, in1) fiat_p384_add(out, in0, in1) -// when Fiat-crypto is used, or as: -// #define p384_felem_add(out, in0, in1) bignum_add_p384(out, in0, in1) -// when s2n-bignum is used. -// -// If (1) x86_64 or aarch64, (2) linux or apple, and (3) OPENSSL_NO_ASM is not -// set, s2n-bignum path is capable. -#if !defined(OPENSSL_NO_ASM) && \ - (defined(OPENSSL_LINUX) || defined(OPENSSL_APPLE) || \ - defined(OPENSSL_OPENBSD)) && \ - ((defined(OPENSSL_X86_64) && !defined(MY_ASSEMBLER_IS_TOO_OLD_FOR_AVX)) || \ - defined(OPENSSL_AARCH64)) - +#if defined(EC_NISTP_USE_S2N_BIGNUM) # include "../../../third_party/s2n-bignum/include/s2n-bignum_aws-lc.h" - -# define P384_USE_S2N_BIGNUM_FIELD_ARITH 1 -# define P384_USE_64BIT_LIMBS_FELEM 1 - #else - -# if defined(BORINGSSL_HAS_UINT128) +# if defined(EC_NISTP_USE_64BIT_LIMB) # include "../../../third_party/fiat/p384_64.h" -# define P384_USE_64BIT_LIMBS_FELEM 1 # else # include "../../../third_party/fiat/p384_32.h" # endif - #endif -#if defined(P384_USE_64BIT_LIMBS_FELEM) +#if defined(EC_NISTP_USE_64BIT_LIMB) #define P384_NLIMBS (6) typedef uint64_t p384_limb_t; @@ -74,8 +46,7 @@ static const p384_felem p384_felem_one = { #endif // 64BIT - -#if defined(P384_USE_S2N_BIGNUM_FIELD_ARITH) +#if defined(EC_NISTP_USE_S2N_BIGNUM) #define p384_felem_add(out, in0, in1) bignum_add_p384(out, in0, in1) #define p384_felem_sub(out, in0, in1) bignum_sub_p384(out, in0, in1) @@ -91,7 +62,7 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { return bignum_nonzero_6(in1); } -#else // P384_USE_S2N_BIGNUM_FIELD_ARITH +#else // EC_NISTP_USE_S2N_BIGNUM // Fiat-crypto implementation of field arithmetic #define p384_felem_add(out, in0, in1) fiat_p384_add(out, in0, in1) @@ -110,7 +81,7 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { return ret; } -#endif // P384_USE_S2N_BIGNUM_FIELD_ARITH +#endif // EC_NISTP_USE_S2N_BIGNUM static void p384_felem_copy(p384_limb_t out[P384_NLIMBS], @@ -270,69 +241,29 @@ static void p384_inv_square(p384_felem out, p384_felem_sqr(out, ret); // 2^384 - 2^128 - 2^96 + 2^32 - 2^2 = p - 3 } -// Group operations -// ---------------- -// -// Building on top of the field operations we have the operations on the -// elliptic curve group itself. Points on the curve are represented in Jacobian -// coordinates. -// -// p384_point_double calculates 2*(x_in, y_in, z_in) -// -// The method is taken from: -// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b -// -// Coq transcription and correctness proof: -// -// -// Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed; -// while x_out == y_in is not (maybe this works, but it's not tested). +#if defined(EC_NISTP_USE_S2N_BIGNUM) +DEFINE_METHOD_FUNCTION(ec_nistp_felem_meth, p384_felem_methods) { + out->add = bignum_add_p384; + out->sub = bignum_sub_p384; + out->mul = bignum_montmul_p384_selector; + out->sqr = bignum_montsqr_p384_selector; +} +#else +DEFINE_METHOD_FUNCTION(ec_nistp_felem_meth, p384_felem_methods) { + out->add = fiat_p384_add; + out->sub = fiat_p384_sub; + out->mul = fiat_p384_mul; + out->sqr = fiat_p384_square; +} +#endif + static void p384_point_double(p384_felem x_out, p384_felem y_out, p384_felem z_out, const p384_felem x_in, const p384_felem y_in, const p384_felem z_in) { - p384_felem delta, gamma, beta, ftmp, ftmp2, tmptmp, alpha, fourbeta; - // delta = z^2 - p384_felem_sqr(delta, z_in); - // gamma = y^2 - p384_felem_sqr(gamma, y_in); - // beta = x*gamma - p384_felem_mul(beta, x_in, gamma); - - // alpha = 3*(x-delta)*(x+delta) - p384_felem_sub(ftmp, x_in, delta); - p384_felem_add(ftmp2, x_in, delta); - - p384_felem_add(tmptmp, ftmp2, ftmp2); - p384_felem_add(ftmp2, ftmp2, tmptmp); - p384_felem_mul(alpha, ftmp, ftmp2); - - // x' = alpha^2 - 8*beta - p384_felem_sqr(x_out, alpha); - p384_felem_add(fourbeta, beta, beta); - p384_felem_add(fourbeta, fourbeta, fourbeta); - p384_felem_add(tmptmp, fourbeta, fourbeta); - p384_felem_sub(x_out, x_out, tmptmp); - - // z' = (y + z)^2 - gamma - delta - // The following calculation differs from that in p256.c: - // an add is replaced with a sub. This saves us 5 cmovznz operations - // when Fiat-crypto implementation of felem_add and felem_sub is used, - // and also a certain number of intructions when s2n-bignum is used. - p384_felem_add(ftmp, y_in, z_in); - p384_felem_sqr(z_out, ftmp); - p384_felem_sub(z_out, z_out, gamma); - p384_felem_sub(z_out, z_out, delta); - - // y' = alpha*(4*beta - x') - 8*gamma^2 - p384_felem_sub(y_out, fourbeta, x_out); - p384_felem_add(gamma, gamma, gamma); - p384_felem_sqr(gamma, gamma); - p384_felem_mul(y_out, alpha, y_out); - p384_felem_add(gamma, gamma, gamma); - p384_felem_sub(y_out, y_out, gamma); + ec_nistp_point_double(p384_felem_methods(), x_out, y_out, z_out, x_in, y_in, z_in); } // p384_point_add calculates (x1, y1, z1) + (x2, y2, z2) diff --git a/crypto/fipsmodule/ec/p384_table.h b/crypto/fipsmodule/ec/p384_table.h index 511ba6ef48..91a1396059 100644 --- a/crypto/fipsmodule/ec/p384_table.h +++ b/crypto/fipsmodule/ec/p384_table.h @@ -25,7 +25,7 @@ // is based on the generation method in: // https://gitlab.com/nisec/ecckiila/-/blob/master/main.py#L296 -#if defined(P384_USE_64BIT_LIMBS_FELEM) +#if defined(EC_NISTP_USE_64BIT_LIMB) static const p384_felem p384_g_pre_comp[20][16][2] = { {{{0x3dd0756649c0b528, 0x20e378e2a0d6ce38, 0x879c3afc541b4d6e, 0x6454868459a30eff, 0x812ff723614ede2b, 0x4d3aadc2299e1513}, diff --git a/crypto/fipsmodule/ec/p521.c b/crypto/fipsmodule/ec/p521.c index ec720651a7..d5ad43c7ec 100644 --- a/crypto/fipsmodule/ec/p521.c +++ b/crypto/fipsmodule/ec/p521.c @@ -17,46 +17,21 @@ #include "../cpucap/internal.h" #include "../delocate.h" #include "internal.h" +#include "ec_nistp.h" #if !defined(OPENSSL_SMALL) -// We have two implementations of the field arithmetic for P-521 curve: -// - Fiat-crypto -// - s2n-bignum -// Both Fiat-crypto and s2n-bignum implementations are formally verified. -// Fiat-crypto implementation is fully portable C code, while s2n-bignum -// implements the operations in assembly for x86_64 and aarch64 platforms. -// All the P-521 field operations supported by Fiat-crypto are supported -// by s2n-bignum as well, so s2n-bignum can be used as a drop-in replacement -// when appropriate. To do that we define macros for the functions. -// For example, field addition macro is either defined as -// #define p521_felem_add(out, in0, in1) fiat_p521_add(out, in0, in1) -// when Fiat-crypto is used, or as: -// #define p521_felem_add(out, in0, in1) bignum_add_p521(out, in0, in1) -// when s2n-bignum is used. -// If (1) x86_64 or aarch64, (2) linux or apple, and (3) OPENSSL_NO_ASM is not -// set, s2n-bignum path is capable. -#if !defined(OPENSSL_NO_ASM) && \ - (defined(OPENSSL_LINUX) || defined(OPENSSL_APPLE) || \ - defined(OPENSSL_OPENBSD)) && \ - ((defined(OPENSSL_X86_64) && !defined(MY_ASSEMBLER_IS_TOO_OLD_FOR_AVX)) || \ - defined(OPENSSL_AARCH64)) +#if defined(EC_NISTP_USE_S2N_BIGNUM) # include "../../../third_party/s2n-bignum/include/s2n-bignum_aws-lc.h" -# define P521_USE_S2N_BIGNUM_FIELD_ARITH 1 - #else - -// Fiat-crypto has both 64-bit and 32-bit implementation for P-521. -# if defined(BORINGSSL_HAS_UINT128) +# if defined(EC_NISTP_USE_64BIT_LIMB) # include "../../../third_party/fiat/p521_64.h" -# define P521_USE_64BIT_LIMBS_FELEM 1 # else # include "../../../third_party/fiat/p521_32.h" # endif - #endif -#if defined(P521_USE_S2N_BIGNUM_FIELD_ARITH) +#if defined(EC_NISTP_USE_S2N_BIGNUM) #define P521_NLIMBS (9) @@ -87,9 +62,9 @@ static const p521_limb_t p521_felem_p[P521_NLIMBS] = { #define p521_felem_mul(out, in0, in1) bignum_mul_p521_selector(out, in0, in1) #define p521_felem_sqr(out, in0) bignum_sqr_p521_selector(out, in0) -#else // P521_USE_S2N_BIGNUM_FIELD_ARITH +#else // EC_NISTP_USE_S2N_BIGNUM -#if defined(P521_USE_64BIT_LIMBS_FELEM) +#if defined(EC_NISTP_USE_64BIT_LIMB) // In the 64-bit case Fiat-crypto represents a field element by 9 58-bit digits. #define P521_NLIMBS (9) @@ -149,7 +124,7 @@ static const p521_limb_t p521_felem_p[P521_NLIMBS] = { #define p521_felem_to_bytes(out, in0) fiat_secp521r1_to_bytes(out, in0) #define p521_felem_from_bytes(out, in0) fiat_secp521r1_from_bytes(out, in0) -#endif // P521_USE_S2N_BIGNUM_FIELD_ARITH +#endif // EC_NISTP_USE_S2N_BIGNUM static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) { p521_limb_t is_not_zero = 0; @@ -157,7 +132,7 @@ static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) { is_not_zero |= in1[i]; } -#if defined(P521_USE_S2N_BIGNUM_FIELD_ARITH) +#if defined(EC_NISTP_USE_S2N_BIGNUM) return is_not_zero; #else // Fiat-crypto functions may return p (the field characteristic) @@ -284,69 +259,29 @@ static void p521_felem_inv(p521_felem output, const p521_felem t1) { p521_felem_mul(output, acc, t1); } -// Group operations -// ---------------- -// -// Building on top of the field operations we have the operations on the -// elliptic curve group itself. Points on the curve are represented in Jacobian -// coordinates. -// -// p521_point_double calculates 2*(x_in, y_in, z_in) -// -// The method is taken from: -// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b -// -// Coq transcription and correctness proof: -// -// -// Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed; -// while x_out == y_in is not (maybe this works, but it's not tested). +#if defined(EC_NISTP_USE_S2N_BIGNUM) +DEFINE_METHOD_FUNCTION(ec_nistp_felem_meth, p521_felem_methods) { + out->add = bignum_add_p521; + out->sub = bignum_sub_p521; + out->mul = bignum_mul_p521_selector; + out->sqr = bignum_sqr_p521_selector; +} +#else +DEFINE_METHOD_FUNCTION(ec_nistp_felem_meth, p521_felem_methods) { + out->add = fiat_secp521r1_carry_add; + out->sub = fiat_secp521r1_carry_sub; + out->mul = fiat_secp521r1_carry_mul; + out->sqr = fiat_secp521r1_carry_square; +} +#endif + static void p521_point_double(p521_felem x_out, p521_felem y_out, p521_felem z_out, const p521_felem x_in, const p521_felem y_in, const p521_felem z_in) { - p521_felem delta, gamma, beta, ftmp, ftmp2, tmptmp, alpha, fourbeta; - // delta = z^2 - p521_felem_sqr(delta, z_in); - // gamma = y^2 - p521_felem_sqr(gamma, y_in); - // beta = x*gamma - p521_felem_mul(beta, x_in, gamma); - - // alpha = 3*(x-delta)*(x+delta) - p521_felem_sub(ftmp, x_in, delta); - p521_felem_add(ftmp2, x_in, delta); - - p521_felem_add(tmptmp, ftmp2, ftmp2); - p521_felem_add(ftmp2, ftmp2, tmptmp); - p521_felem_mul(alpha, ftmp, ftmp2); - - // x' = alpha^2 - 8*beta - p521_felem_sqr(x_out, alpha); - p521_felem_add(fourbeta, beta, beta); - p521_felem_add(fourbeta, fourbeta, fourbeta); - p521_felem_add(tmptmp, fourbeta, fourbeta); - p521_felem_sub(x_out, x_out, tmptmp); - - // z' = (y + z)^2 - gamma - delta - // The following calculation differs from that in p256.c: - // an add is replaced with a sub. This saves us 5 cmovznz operations - // when Fiat-crypto implementation of felem_add and felem_sub is used, - // and also a certain number of intructions when s2n-bignum is used. - p521_felem_add(ftmp, y_in, z_in); - p521_felem_sqr(z_out, ftmp); - p521_felem_sub(z_out, z_out, gamma); - p521_felem_sub(z_out, z_out, delta); - - // y' = alpha*(4*beta - x') - 8*gamma^2 - p521_felem_sub(y_out, fourbeta, x_out); - p521_felem_add(gamma, gamma, gamma); - p521_felem_sqr(gamma, gamma); - p521_felem_mul(y_out, alpha, y_out); - p521_felem_add(gamma, gamma, gamma); - p521_felem_sub(y_out, y_out, gamma); + ec_nistp_point_double(p521_felem_methods(), x_out, y_out, z_out, x_in, y_in, z_in); } // p521_point_add calculates (x1, y1, z1) + (x2, y2, z2) diff --git a/crypto/fipsmodule/ec/p521_table.h b/crypto/fipsmodule/ec/p521_table.h index 0d5e7da1db..029fd82ada 100644 --- a/crypto/fipsmodule/ec/p521_table.h +++ b/crypto/fipsmodule/ec/p521_table.h @@ -25,7 +25,7 @@ // is based on the generation method in: // https://gitlab.com/nisec/ecckiila/-/blob/master/main.py#L296 -#if defined(P521_USE_S2N_BIGNUM_FIELD_ARITH) +#if defined(EC_NISTP_USE_S2N_BIGNUM) static const p521_felem p521_g_pre_comp[27][16][2] = { {{{0xf97e7e31c2e5bd66, 0x3348b3c1856a429b, 0xfe1dc127a2ffa8de, 0xa14b5e77efe75928, 0xf828af606b4d3dba, 0x9c648139053fb521, @@ -2620,7 +2620,7 @@ static const p521_felem p521_g_pre_comp[27][16][2] = { 0xa52d88b032279d4f, 0xcbb5c865dc5e94a4, 0x438dfd2ab800eeb6, 0xca1f3410ea7c4ad8, 0x7753085f3ebf90db, 0x00000000000001d3}}}}; #else -#if defined(P521_USE_64BIT_LIMBS_FELEM) +#if defined(EC_NISTP_USE_64BIT_LIMB) static const p521_felem p521_g_pre_comp[27][16][2] = { {{{0x017e7e31c2e5bd66, 0x022cf0615a90a6fe, 0x00127a2ffa8de334, 0x01dfbf9d64a3f877, 0x006b4d3dbaa14b5e, 0x014fed487e0a2bd8, From ad211a916cc47bb568f527536f0ae65be91ffe08 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 1 Dec 2023 16:21:40 -0500 Subject: [PATCH 02/42] Fix build with -Wmissing-field-initializers Since it's otherwise pretty tedious, let's try this with C99 designated initializers. From testing, I remember they worked pretty reliably in C. (In C++, it's a little trickier because MSVC won't accept them outside C++20. Although I think all our supported MSVCs have a C++20 mode now...) AWS-LC: Changes to CMakeLists were not taken as that flag exists there already. Fixed: 671 Change-Id: Ia29ade8721ecfe2140a2d183ad60c8a730c631f0 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64447 Auto-Submit: David Benjamin Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit a9a4c6dc89aff96f64c6ed93c1c3fc4d0c8e6e74) --- crypto/x509/x509_vpm.c | 47 ++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index 21f64afbf2..8d92a03c3e 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -492,44 +492,27 @@ int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param) { #define vpm_empty_id NULL, 0U, NULL, 0, NULL, 0, 0 static const X509_VERIFY_PARAM kDefaultParam = { - /*check_time=*/0, - /*inh_flags=*/0, - /*flags=*/X509_V_FLAG_TRUSTED_FIRST, - /*purpose=*/0, - /*trust=*/0, - /*depth=*/100, - /*policies=*/NULL, - vpm_empty_id}; + .flags = X509_V_FLAG_TRUSTED_FIRST, + .depth = 100, +}; static const X509_VERIFY_PARAM kSMIMESignParam = { - /*check_time=*/0, - /*inh_flags=*/0, - /*flags=*/0, - /*purpose=*/X509_PURPOSE_SMIME_SIGN, - /*trust=*/X509_TRUST_EMAIL, - /*depth=*/-1, - /*policies=*/NULL, - vpm_empty_id}; + .purpose = X509_PURPOSE_SMIME_SIGN, + .trust = X509_TRUST_EMAIL, + .depth = -1, +}; static const X509_VERIFY_PARAM kSSLClientParam = { - /*check_time=*/0, - /*inh_flags=*/0, - /*flags=*/0, - /*purpose=*/X509_PURPOSE_SSL_CLIENT, - /*trust=*/X509_TRUST_SSL_CLIENT, - /*depth=*/-1, - /*policies=*/NULL, - vpm_empty_id}; + .purpose = X509_PURPOSE_SSL_CLIENT, + .trust = X509_TRUST_SSL_CLIENT, + .depth = -1, +}; static const X509_VERIFY_PARAM kSSLServerParam = { - /*check_time=*/0, - /*inh_flags=*/0, - /*flags=*/0, - /*purpose=*/X509_PURPOSE_SSL_SERVER, - /*trust=*/X509_TRUST_SSL_SERVER, - /*depth=*/-1, - /*policies=*/NULL, - vpm_empty_id}; + .purpose = X509_PURPOSE_SSL_SERVER, + .trust = X509_TRUST_SSL_SERVER, + .depth = -1, +}; const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name) { if (strcmp(name, "default") == 0) { From 13022bb3c180def93415d6be663dc87cf8ef48ed Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 25 Nov 2023 12:06:49 -0500 Subject: [PATCH 03/42] Simplify and document X509_VERIFY_PARAM inheritance X509_VERIFY_PARAM inheritance is unbelievably thorny, and I'm not sure it was ever thought through very well. We can make it slightly less complicated by removing the internal inh_flags value. We don't support X509_VERIFY_PARAM_set_inh_flags (and really should keep it that way!), so there are actually only two possible values, zero and X509_VP_FLAG_DEFAULT, used to implement X509_VERIFY_PARAM_inherit, and X509_VERIFY_PARAM_set1, respectively. This still leaves some weird behaviors that are expected through the public API, which I've documented in the headers. They'll probably need another pass when they're grouped into sections and whatnot. Bug: 441, 426 Change-Id: Ib0a855afd35e597c65c249627addfef76ed7099d Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64253 Commit-Queue: David Benjamin Reviewed-by: Bob Beck (cherry picked from commit 51ae958ff7c99103932e07905c40cbc71a22b389) --- crypto/x509/internal.h | 1 - crypto/x509/x509_lu.c | 2 +- crypto/x509/x509_test.cc | 91 ++++++++++++++++++++++++ crypto/x509/x509_vpm.c | 146 +++++++++++++-------------------------- include/openssl/x509.h | 81 +++++++++++++++++----- 5 files changed, 203 insertions(+), 118 deletions(-) diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 92bcf65166..87a77e56a2 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -248,7 +248,6 @@ struct X509_crl_st { struct X509_VERIFY_PARAM_st { int64_t check_time; // POSIX time to use - unsigned long inh_flags; // Inheritance flags unsigned long flags; // Various verify flags int purpose; // purpose to check untrusted certificates int trust; // trust setting to check diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index 71487c3b64..1629b50a98 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -630,7 +630,7 @@ int X509_STORE_set_trust(X509_STORE *ctx, int trust) { return X509_VERIFY_PARAM_set_trust(ctx->param, trust); } -int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *param) { +int X509_STORE_set1_param(X509_STORE *ctx, const X509_VERIFY_PARAM *param) { return X509_VERIFY_PARAM_set1(ctx->param, param); } diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index ae79e1b50c..cc2a683720 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -7128,3 +7128,94 @@ TEST(X509Test, ExternalData) { EXPECT_EQ(retrieved_data->custom_data, 123); } +TEST(X509Test, ParamInheritance) { + // |X509_VERIFY_PARAM_inherit| with both unset. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); + } + + // |X509_VERIFY_PARAM_inherit| with source set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(src.get(), 5); + ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); + } + + // |X509_VERIFY_PARAM_inherit| with destination set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(dest.get(), 5); + ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); + } + + // |X509_VERIFY_PARAM_inherit| with both set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(dest.get(), 5); + X509_VERIFY_PARAM_set_depth(src.get(), 10); + ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); + // The existing value is used. + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); + } + + // |X509_VERIFY_PARAM_set1| with both unset. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); + } + + // |X509_VERIFY_PARAM_set1| with source set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(src.get(), 5); + ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); + } + + // |X509_VERIFY_PARAM_set1| with destination set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(dest.get(), 5); + ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); + } + + // |X509_VERIFY_PARAM_set1| with both set. + { + bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(dest); + bssl::UniquePtr src(X509_VERIFY_PARAM_new()); + ASSERT_TRUE(src); + X509_VERIFY_PARAM_set_depth(dest.get(), 5); + X509_VERIFY_PARAM_set_depth(src.get(), 10); + ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); + // The new value is used. + EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 10); + } +} diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index 8d92a03c3e..3031ff9a8d 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -129,9 +129,6 @@ X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void) { return NULL; } param->depth = -1; - // TODO(crbug.com/boringssl/441): This line was commented out. Figure out what - // this was for: - // param->inh_flags = X509_VP_FLAG_DEFAULT; return param; } @@ -146,143 +143,98 @@ void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) { OPENSSL_free(param); } -//- -// This function determines how parameters are "inherited" from one structure -// to another. There are several different ways this can happen. -// -// 1. If a child structure needs to have its values initialized from a parent -// they are simply copied across. For example SSL_CTX copied to SSL. -// 2. If the structure should take on values only if they are currently unset. -// For example the values in an SSL structure will take appropriate value -// for SSL servers or clients but only if the application has not set new -// ones. -// -// The "inh_flags" field determines how this function behaves. -// -// Normally any values which are set in the default are not copied from the -// destination and verify flags are ORed together. -// -// If X509_VP_FLAG_DEFAULT is set then anything set in the source is copied -// to the destination. Effectively the values in "to" become default values -// which will be used only if nothing new is set in "from". -// -// If X509_VP_FLAG_OVERWRITE is set then all value are copied across whether -// they are set or not. Flags is still Ored though. -// -// If X509_VP_FLAG_RESET_FLAGS is set then the flags value is copied instead -// of ORed. -// -// If X509_VP_FLAG_LOCKED is set then no values are copied. -// -// If X509_VP_FLAG_ONCE is set then the current inh_flags setting is zeroed -// after the next call. - -// Macro to test if a field should be copied from src to dest - -#define test_x509_verify_param_copy(field, def) \ - (to_overwrite || \ - ((src->field != (def)) && (to_default || (dest->field == (def))))) - -// Macro to test and copy a field if necessary - -#define x509_verify_param_copy(field, def) \ - if (test_x509_verify_param_copy(field, def)) \ - dest->field = src->field - -int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, - const X509_VERIFY_PARAM *src) { - unsigned long inh_flags; - int to_default, to_overwrite; - if (!src) { - return 1; - } - inh_flags = dest->inh_flags | src->inh_flags; - - if (inh_flags & X509_VP_FLAG_ONCE) { - dest->inh_flags = 0; +static int should_copy(int dest_is_set, int src_is_set, int prefer_src) { + if (prefer_src) { + // We prefer the source, so as long as there is a value to copy, copy it. + return src_is_set; } - if (inh_flags & X509_VP_FLAG_LOCKED) { - return 1; - } + // We prefer the destination, so only copy if the destination is unset. + return src_is_set && !dest_is_set; +} - if (inh_flags & X509_VP_FLAG_DEFAULT) { - to_default = 1; - } else { - to_default = 0; +static void copy_int_param(int *dest, const int *src, int default_val, + int prefer_src) { + if (should_copy(*dest != default_val, *src != default_val, prefer_src)) { + *dest = *src; } +} - if (inh_flags & X509_VP_FLAG_OVERWRITE) { - to_overwrite = 1; - } else { - to_overwrite = 0; +// x509_verify_param_copy copies fields from |src| to |dest|. If both |src| and +// |dest| have some field set, |prefer_src| determines whether |src| or |dest|'s +// version is used. +static int x509_verify_param_copy(X509_VERIFY_PARAM *dest, + const X509_VERIFY_PARAM *src, + int prefer_src) { + if (src == NULL) { + return 1; } - x509_verify_param_copy(purpose, 0); - x509_verify_param_copy(trust, 0); - x509_verify_param_copy(depth, -1); - - // If overwrite or check time not set, copy across + copy_int_param(&dest->purpose, &src->purpose, /*default_val=*/0, prefer_src); + copy_int_param(&dest->trust, &src->trust, /*default_val=*/0, prefer_src); + copy_int_param(&dest->depth, &src->depth, /*default_val=*/-1, prefer_src); - if (to_overwrite || !(dest->flags & X509_V_FLAG_USE_CHECK_TIME)) { + // |check_time|, unlike all other parameters, does not honor |prefer_src|. + // This means |X509_VERIFY_PARAM_set1| will not overwrite it. This behavior + // comes from OpenSSL but may have been a bug. + if (!(dest->flags & X509_V_FLAG_USE_CHECK_TIME)) { dest->check_time = src->check_time; - dest->flags &= ~X509_V_FLAG_USE_CHECK_TIME; - // Don't need to copy flag: that is done below - } - - if (inh_flags & X509_VP_FLAG_RESET_FLAGS) { - dest->flags = 0; + // The source |X509_V_FLAG_USE_CHECK_TIME| flag, if set, is copied below. } dest->flags |= src->flags; - if (test_x509_verify_param_copy(policies, NULL)) { + if (should_copy(dest->policies != NULL, src->policies != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_policies(dest, src->policies)) { return 0; } } - // Copy the host flags if and only if we're copying the host list - if (test_x509_verify_param_copy(hosts, NULL)) { - if (dest->hosts) { - sk_OPENSSL_STRING_pop_free(dest->hosts, str_free); - dest->hosts = NULL; - } + if (should_copy(dest->hosts != NULL, src->hosts != NULL, prefer_src)) { + sk_OPENSSL_STRING_pop_free(dest->hosts, str_free); + dest->hosts = NULL; if (src->hosts) { dest->hosts = sk_OPENSSL_STRING_deep_copy(src->hosts, OPENSSL_strdup, str_free); if (dest->hosts == NULL) { return 0; } + // Copy the host flags if and only if we're copying the host list. Note + // this means mechanisms like |X509_STORE_CTX_set_default| cannot be used + // to set host flags. E.g. we cannot change the defaults using + // |kDefaultParam| below. dest->hostflags = src->hostflags; } } - if (test_x509_verify_param_copy(email, NULL)) { + if (should_copy(dest->email != NULL, src->email != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen)) { return 0; } } - if (test_x509_verify_param_copy(ip, NULL)) { + if (should_copy(dest->ip != NULL, src->ip != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen)) { return 0; } } dest->poison = src->poison; - return 1; } +int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, + const X509_VERIFY_PARAM *src) { + // Prefer the destination. That is, this function only changes unset + // parameters in |dest|. + return x509_verify_param_copy(dest, src, /*prefer_src=*/0); +} + int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, const X509_VERIFY_PARAM *from) { - unsigned long save_flags = to->inh_flags; - int ret; - to->inh_flags |= X509_VP_FLAG_DEFAULT; - ret = X509_VERIFY_PARAM_inherit(to, from); - to->inh_flags = save_flags; - return ret; + // Prefer the source. That is, values in |to| are only preserved if they were + // unset in |from|. + return x509_verify_param_copy(to, from, /*prefer_src=*/1); } static int int_x509_param_set1_email(char **pdest, size_t *pdestlen, @@ -365,7 +317,7 @@ int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, return 1; } -unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param) { +unsigned long X509_VERIFY_PARAM_get_flags(const X509_VERIFY_PARAM *param) { return param->flags; } diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 7468a871c0..e8eb4ef4de 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3291,12 +3291,6 @@ OPENSSL_EXPORT int X509_LOOKUP_add_dir(X509_LOOKUP *lookup, const char *path, // verification. #define X509_V_FLAG_NO_CHECK_TIME 0x200000 -#define X509_VP_FLAG_DEFAULT 0x1 -#define X509_VP_FLAG_OVERWRITE 0x2 -#define X509_VP_FLAG_RESET_FLAGS 0x4 -#define X509_VP_FLAG_LOCKED 0x8 -#define X509_VP_FLAG_ONCE 0x10 - // Internal use: mask of policy related options (hidden) #define X509_V_FLAG_POLICY_MASK \ @@ -3364,16 +3358,34 @@ OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *st, // the |X509_STORE|. See discussion in |X509_STORE_get0_param|. OPENSSL_EXPORT int X509_STORE_set_flags(X509_STORE *store, unsigned long flags); -OPENSSL_EXPORT int X509_STORE_set_purpose(X509_STORE *ctx, int purpose); -OPENSSL_EXPORT int X509_STORE_set_trust(X509_STORE *ctx, int trust); -OPENSSL_EXPORT int X509_STORE_set1_param(X509_STORE *ctx, - X509_VERIFY_PARAM *pm); - -// X509_STORE_get0_param returns |store|'s default verification parameters. This -// object is mutable and may be modified by the caller. -// -// TODO(crbug.com/boringssl/441): Discuss the semantics of this notion of -// "default". +OPENSSL_EXPORT int X509_STORE_set_purpose(X509_STORE *store, int purpose); +OPENSSL_EXPORT int X509_STORE_set_trust(X509_STORE *store, int trust); + +// X509_STORE_set1_param copies verification parameters from |param| as in +// |X509_VERIFY_PARAM_set1|. It returns one on success and zero on error. +OPENSSL_EXPORT int X509_STORE_set1_param(X509_STORE *store, + const X509_VERIFY_PARAM *param); + +// X509_STORE_get0_param returns |store|'s verification parameters. This object +// is mutable and may be modified by the caller. For an individual certificate +// verification operation, |X509_STORE_CTX_init| initializes the +// |X509_STORE_CTX|'s parameters with these parameters. +// +// WARNING: |X509_STORE_CTX_init| applies some default parameters (as in +// |X509_VERIFY_PARAM_inherit|) after copying |store|'s parameters. This means +// it is impossible to leave some parameters unset at |store|. They must be +// explicitly unset after creating the |X509_STORE_CTX|. +// +// As of writing these late defaults are a depth limit (see +// |X509_VERIFY_PARAM_set_depth|) and the |X509_V_FLAG_TRUSTED_FIRST| flag. This +// warning does not apply if the parameters were set in |store|. That is, +// callers may safely set a concrete depth limit in |store|, but unlimited depth +// must be configured at |X509_STORE_CTX|. +// +// TODO(crbug.com/boringssl/441): This behavior is very surprising. Can we +// remove this notion of late defaults? A depth limit of 100 can probably be +// applied unconditionally. |X509_V_FLAG_TRUSTED_FIRST| is mostly a workaround +// for poor path-building. OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store); // X509_STORE_set_verify_cb acts like |X509_STORE_CTX_set_verify_cb| but sets @@ -3409,6 +3421,14 @@ OPENSSL_EXPORT int X509_STORE_CTX_get1_issuer(X509 **issuer, // X509_STORE_CTX_free releases memory associated with |ctx|. OPENSSL_EXPORT void X509_STORE_CTX_free(X509_STORE_CTX *ctx); +// X509_STORE_CTX_init initializes |ctx| to verify |x509|, using trusted +// certificates and parameters in |store|. It returns one on success and zero on +// error. |chain| is a list of untrusted intermediate certificates to use in +// verification. +// +// |ctx| stores pointers to |store|, |x509|, and |chain|. Each of these objects +// must outlive |ctx| and may not be mutated for the duration of the certificate +// verification. OPENSSL_EXPORT int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, STACK_OF(X509) *chain); @@ -3535,11 +3555,21 @@ OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_CTX_get0_param( // and takes ownership of |param|. After this function returns, the caller // should not free |param|. // -// TODO(crbug.com/boringssl/441): The bug notes some odd interactions with -// the different notions of default. Discuss this. +// WARNING: This function discards any values which were previously applied in +// |ctx|, including the "default" parameters applied late in +// |X509_STORE_CTX_init|. These late defaults are not applied to parameters +// created standalone by |X509_VERIFY_PARAM_new|. +// +// TODO(crbug.com/boringssl/441): This behavior is very surprising. Should we +// re-apply the late defaults in |param|, or somehow avoid this notion of late +// defaults altogether? OPENSSL_EXPORT void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param); +// X509_STORE_CTX_set_default looks up the set of parameters named |name| and +// applies those default verification parameters for |ctx|. As in +// |X509_VERIFY_PARAM_inherit|, only unset parameters are changed. This function +// returns one on success and zero on error. OPENSSL_EXPORT int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name); @@ -3564,8 +3594,15 @@ OPENSSL_EXPORT X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void); // X509_VERIFY_PARAM_free releases memory associated with |param|. OPENSSL_EXPORT void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param); +// X509_VERIFY_PARAM_inherit applies |from| as the default values for |to|. That +// is, for each parameter that is unset in |to|, it copies the value in |from|. +// This function returns one on success and zero on error. OPENSSL_EXPORT int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *to, const X509_VERIFY_PARAM *from); + +// X509_VERIFY_PARAM_set1 copies parameters from |from| to |to|. If a parameter +// is unset in |from|, the existing value in |to| is preserved. This function +// returns one on success and zero on error. OPENSSL_EXPORT int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, const X509_VERIFY_PARAM *from); @@ -3575,10 +3612,16 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, OPENSSL_EXPORT int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags); +// X509_VERIFY_PARAM_clear_flags disables all values in |flags| in |param|'s +// verification flags and returns one. |flags| should be a combination of +// |X509_V_FLAG_*| constants. OPENSSL_EXPORT int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, unsigned long flags); + +// X509_VERIFY_PARAM_get_flags returns |param|'s verification flags. OPENSSL_EXPORT unsigned long X509_VERIFY_PARAM_get_flags( - X509_VERIFY_PARAM *param); + const X509_VERIFY_PARAM *param); + OPENSSL_EXPORT int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose); OPENSSL_EXPORT int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, From 60aabb4c368dabdc14748be5185ce5f67d3bb3a5 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 22 Nov 2023 02:55:41 -0500 Subject: [PATCH 04/42] Fix the names of some X509_STORE_CTX functions This matches an upstream change. Add macros for the old names. We may be able to unexport these, but for now just pick up the less confusing names. (I found only one user of one of them, goma, which will be replaced next year. Though wanting *some* API to query the dirhash machinery is not completely implausible.) Change-Id: Idd2352c07c294c4f63c4ef12e5d97804f42225b9 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64254 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 33a5e94645787d74593efa0e63200a31372325a2) --- crypto/x509/x509_lu.c | 7 ++++--- crypto/x509/x509_vfy.c | 4 ++-- include/openssl/x509.h | 13 +++++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index 1629b50a98..58fcc696da 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -231,7 +231,7 @@ X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, const X509_LOOKUP_METHOD *m) { } int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name, - X509_OBJECT *ret) { + X509_OBJECT *ret) { X509_STORE *ctx = vs->ctx; X509_OBJECT stmp; CRYPTO_MUTEX_lock_write(&ctx->objs_lock); @@ -445,7 +445,7 @@ STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *st) { return st->objs; } -STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) { +STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) { int cnt; STACK_OF(X509) *sk = sk_X509_new_null(); if (sk == NULL) { @@ -485,7 +485,8 @@ STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) { return sk; } -STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm) { +STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, + X509_NAME *nm) { int cnt; X509_OBJECT xobj; STACK_OF(X509_CRL) *sk = sk_X509_CRL_new_null(); diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 360db6981d..73b2089f14 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -139,7 +139,7 @@ static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) { X509 *xtmp = NULL; size_t i; // Lookup all certs with matching subject name - certs = X509_STORE_get1_certs(ctx, X509_get_subject_name(x)); + certs = X509_STORE_CTX_get1_certs(ctx, X509_get_subject_name(x)); if (certs == NULL) { return NULL; } @@ -1154,7 +1154,7 @@ static int get_crl(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509 *x) { } // Lookup CRLs from store - skcrl = X509_STORE_get1_crls(ctx, nm); + skcrl = X509_STORE_CTX_get1_crls(ctx, nm); // If no CRLs found and a near match from get_crl_sk use that if (!skcrl && crl) { diff --git a/include/openssl/x509.h b/include/openssl/x509.h index e8eb4ef4de..6ab021dfd9 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -2954,6 +2954,11 @@ OPENSSL_EXPORT int X509V3_add_standard_extensions(void); OPENSSL_EXPORT STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line); +// The following symbols are legacy aliases for |X509_STORE_CTX| functions. +#define X509_STORE_get1_certs X509_STORE_CTX_get1_certs +#define X509_STORE_get1_crls X509_STORE_CTX_get1_crls + + // Private structures. struct X509_algor_st { @@ -3345,10 +3350,10 @@ OPENSSL_EXPORT int X509_STORE_up_ref(X509_STORE *store); OPENSSL_EXPORT void X509_STORE_free(X509_STORE *store); OPENSSL_EXPORT STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *st); -OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *st, - X509_NAME *nm); -OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *st, - X509_NAME *nm); +OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *st, + X509_NAME *nm); +OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *st, + X509_NAME *nm); // X509_STORE_set_flags enables all values in |flags| in |store|'s verification // flags. |flags| should be a combination of |X509_V_FLAG_*| constants. From a37695582431cf35f099ce5aef66d3ee2b4d5125 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 25 Nov 2023 13:27:08 -0500 Subject: [PATCH 05/42] Consistently call CRYPTO_free_ex_data first (Or second, if there's a method table.) CRYPTO_EX_free is passed the parent object, so the caller could, in principle, inspect the object. We should pass in an object in a self-consistent state. Also fix up the documentation. I think some bits of a since removed CRYPTO_EX_new got jumbled up in there. Change-Id: I316d00aee61bf544f59d4dac2efcd825e4bfa9b1 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64255 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 59906b3aa8d9f48ad7303edc540912bd588a8e46) --- crypto/fipsmodule/ec/ec_key.c | 4 ++-- crypto/x509/x509_vfy.c | 4 +--- include/openssl/ex_data.h | 9 +++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c index 2373aab38e..46e0d67b4c 100644 --- a/crypto/fipsmodule/ec/ec_key.c +++ b/crypto/fipsmodule/ec/ec_key.c @@ -163,12 +163,12 @@ void EC_KEY_free(EC_KEY *r) { METHOD_unref(r->ecdsa_meth); } + CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), r, &r->ex_data); + EC_GROUP_free(r->group); EC_POINT_free(r->pub_key); ec_wrapped_scalar_free(r->priv_key); - CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), r, &r->ex_data); - OPENSSL_free(r); } diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 73b2089f14..f99d464226 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -1740,11 +1740,9 @@ void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk) { } void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx) { + CRYPTO_free_ex_data(&g_ex_data_class, ctx, &(ctx->ex_data)); X509_VERIFY_PARAM_free(ctx->param); - ctx->param = NULL; sk_X509_pop_free(ctx->chain, X509_free); - ctx->chain = NULL; - CRYPTO_free_ex_data(&g_ex_data_class, ctx, &(ctx->ex_data)); OPENSSL_memset(ctx, 0, sizeof(X509_STORE_CTX)); } diff --git a/include/openssl/ex_data.h b/include/openssl/ex_data.h index 7d03f1766a..5fac7c3d9c 100644 --- a/include/openssl/ex_data.h +++ b/include/openssl/ex_data.h @@ -163,10 +163,11 @@ OPENSSL_EXPORT void *TYPE_get_ex_data(const TYPE *t, int index); // callback has been passed to |SSL_get_ex_new_index| then it may be called each // time an |SSL*| is destroyed. // -// The callback is passed the new object (i.e. the |SSL*|) in |parent|. The -// arguments |argl| and |argp| contain opaque values that were given to -// |CRYPTO_get_ex_new_index|. The callback should return one on success, but -// the value is ignored. +// The callback is passed the to-be-destroyed object (i.e. the |SSL*|) in +// |parent|. As |parent| will shortly be destroyed, callers must not perform +// operations that would increment its reference count, pass ownership, or +// assume the object outlives the function call. The arguments |argl| and |argp| +// contain opaque values that were given to |CRYPTO_get_ex_new_index|. // // This callback may be called with a NULL value for |ptr| if |parent| has no // value set for this index. However, the callbacks may also be skipped entirely From d3470a2e66a9c40aecd7c1b5c35b0882dd874802 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 09:50:54 -0500 Subject: [PATCH 06/42] Add missing include Change-Id: Ifaef253aa82b07d0930dddbd773724132a7724c4 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64587 Reviewed-by: Adam Langley Commit-Queue: Adam Langley Auto-Submit: David Benjamin (cherry picked from commit c41de812877a5ba256ba78e64c06dcbe3c8343d3) --- crypto/fipsmodule/sha/sha_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/fipsmodule/sha/sha_test.cc b/crypto/fipsmodule/sha/sha_test.cc index 651181aace..28ddf92c57 100644 --- a/crypto/fipsmodule/sha/sha_test.cc +++ b/crypto/fipsmodule/sha/sha_test.cc @@ -14,6 +14,8 @@ #include +#include + #include #include "../../test/abi_test.h" From 68b9d6bbb1131d00c5b8c268800a714b716a9535 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 27 Nov 2023 23:26:09 -0500 Subject: [PATCH 07/42] Document or unexport some more of x509.h Get the remaining config APIs, extensions accessors, and the get1_email family. I'm not sure yet whether the various remaining extension-specific functions should get their own sections (probably), in which case, maybe we should move the accessors these into their sections? Put them with the rest of the certificate getters for now. As part of this, deduplicate the X509v3_KU_* and KU_* constants. See https://github.com/openssl/openssl/issues/22955 Bug: 426 Change-Id: I31a9b887eb1e6cfa272f04d2ee80dbb5a9ed98f7 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64256 Reviewed-by: Bob Beck Commit-Queue: Bob Beck (cherry picked from commit 314c2520eab450615d8e78df21169c090d6f51e5) --- crypto/x509/internal.h | 10 + crypto/x509/v3_alt.c | 8 +- crypto/x509/v3_info.c | 8 +- crypto/x509/v3_purp.c | 33 +-- crypto/x509/v3_utl.c | 14 +- crypto/x509/x509_req.c | 8 +- crypto/x509/x509_vfy.c | 2 +- include/openssl/base.h | 1 + include/openssl/x509.h | 461 +++++++++++++++++++++++------------------ 9 files changed, 315 insertions(+), 230 deletions(-) diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 87a77e56a2..e178f0a289 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -543,6 +543,16 @@ OPENSSL_EXPORT int GENERAL_NAME_cmp(const GENERAL_NAME *a, // |name|, or NULL if no such name is defined. const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name); +GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, + const X509V3_CTX *ctx, const CONF_VALUE *cnf); +GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + const X509V3_CTX *ctx, const CONF_VALUE *cnf, + int is_nc); +GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method, + const X509V3_CTX *ctx, + const STACK_OF(CONF_VALUE) *nval); + #if defined(__cplusplus) } // extern C diff --git a/crypto/x509/v3_alt.c b/crypto/x509/v3_alt.c index f61645a77a..61ea4a9371 100644 --- a/crypto/x509/v3_alt.c +++ b/crypto/x509/v3_alt.c @@ -446,10 +446,10 @@ GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, return v2i_GENERAL_NAME_ex(NULL, method, ctx, cnf, 0); } -GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, - const X509V3_EXT_METHOD *method, - const X509V3_CTX *ctx, int gen_type, - const char *value, int is_nc) { +static GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, + const X509V3_EXT_METHOD *method, + const X509V3_CTX *ctx, int gen_type, + const char *value, int is_nc) { if (!value) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_MISSING_VALUE); return NULL; diff --git a/crypto/x509/v3_info.c b/crypto/x509/v3_info.c index eb190de280..ce7e523e11 100644 --- a/crypto/x509/v3_info.c +++ b/crypto/x509/v3_info.c @@ -67,6 +67,9 @@ #include #include +#include "internal.h" + + static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_INFO_ACCESS( const X509V3_EXT_METHOD *method, void *ext, STACK_OF(CONF_VALUE) *ret); static void *v2i_AUTHORITY_INFO_ACCESS(const X509V3_EXT_METHOD *method, @@ -206,8 +209,3 @@ static void *v2i_AUTHORITY_INFO_ACCESS(const X509V3_EXT_METHOD *method, sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free); return NULL; } - -int i2a_ACCESS_DESCRIPTION(BIO *bp, const ACCESS_DESCRIPTION *a) { - i2a_ASN1_OBJECT(bp, a->method); - return 2; -} diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c index 24b59c65ff..6ef3cb1c94 100644 --- a/crypto/x509/v3_purp.c +++ b/crypto/x509/v3_purp.c @@ -500,7 +500,7 @@ int x509v3_cache_extensions(X509 *x) { x->ex_flags |= EXFLAG_SI; // If SKID matches AKID also indicate self signed if (X509_check_akid(x, x->akid) == X509_V_OK && - !ku_reject(x, KU_KEY_CERT_SIGN)) { + !ku_reject(x, X509v3_KU_KEY_CERT_SIGN)) { x->ex_flags |= EXFLAG_SS; } } @@ -539,7 +539,7 @@ int x509v3_cache_extensions(X509 *x) { // otherwise. static int check_ca(const X509 *x) { // keyUsage if present should allow cert signing - if (ku_reject(x, KU_KEY_CERT_SIGN)) { + if (ku_reject(x, X509v3_KU_KEY_CERT_SIGN)) { return 0; } // Version 1 certificates are considered CAs and don't have extensions. @@ -566,7 +566,7 @@ static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, return check_ca(x); } // We need to do digital signatures or key agreement - if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)) { + if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE | X509v3_KU_KEY_AGREEMENT)) { return 0; } // nsCertType if present should allow SSL client use @@ -579,7 +579,9 @@ static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, // Key usage needed for TLS/SSL server: digital signature, encipherment or // key agreement. The ssl code can check this more thoroughly for individual // key types. -#define KU_TLS (KU_DIGITAL_SIGNATURE | KU_KEY_ENCIPHERMENT | KU_KEY_AGREEMENT) +#define X509v3_KU_TLS \ + (X509v3_KU_DIGITAL_SIGNATURE | X509v3_KU_KEY_ENCIPHERMENT | \ + X509v3_KU_KEY_AGREEMENT) static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca) { @@ -593,7 +595,7 @@ static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, if (ns_reject(x, NS_SSL_SERVER)) { return 0; } - if (ku_reject(x, KU_TLS)) { + if (ku_reject(x, X509v3_KU_TLS)) { return 0; } @@ -608,7 +610,7 @@ static int check_purpose_ns_ssl_server(const X509_PURPOSE *xp, const X509 *x, return ret; } // We need to encipher or Netscape complains - if (ku_reject(x, KU_KEY_ENCIPHERMENT)) { + if (ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT)) { return 0; } return ret; @@ -641,7 +643,7 @@ static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, if (!ret || ca) { return ret; } - if (ku_reject(x, KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)) { + if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE | X509v3_KU_NON_REPUDIATION)) { return 0; } return ret; @@ -654,7 +656,7 @@ static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, if (!ret || ca) { return ret; } - if (ku_reject(x, KU_KEY_ENCIPHERMENT)) { + if (ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT)) { return 0; } return ret; @@ -665,7 +667,7 @@ static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, if (ca) { return check_ca(x); } - if (ku_reject(x, KU_CRL_SIGN)) { + if (ku_reject(x, X509v3_KU_CRL_SIGN)) { return 0; } return 1; @@ -696,8 +698,10 @@ static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, // and/or nonRepudiation (other values are not consistent and shall // be rejected). if ((x->ex_flags & EXFLAG_KUSAGE) && - ((x->ex_kusage & ~(KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)) || - !(x->ex_kusage & (KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)))) { + ((x->ex_kusage & + ~(X509v3_KU_NON_REPUDIATION | X509v3_KU_DIGITAL_SIGNATURE)) || + !(x->ex_kusage & + (X509v3_KU_NON_REPUDIATION | X509v3_KU_DIGITAL_SIGNATURE)))) { return 0; } @@ -744,7 +748,7 @@ int X509_check_issued(X509 *issuer, X509 *subject) { } } - if (ku_reject(issuer, KU_KEY_CERT_SIGN)) { + if (ku_reject(issuer, X509v3_KU_KEY_CERT_SIGN)) { return X509_V_ERR_KEYUSAGE_NO_CERTSIGN; } return X509_V_OK; @@ -803,6 +807,9 @@ uint32_t X509_get_key_usage(X509 *x) { if (x->ex_flags & EXFLAG_KUSAGE) { return x->ex_kusage; } + // If there is no extension, key usage is unconstrained, so set all bits to + // one. Note that, although we use |UINT32_MAX|, |ex_kusage| only contains the + // first 16 bits when the extension is present. return UINT32_MAX; } @@ -813,6 +820,8 @@ uint32_t X509_get_extended_key_usage(X509 *x) { if (x->ex_flags & EXFLAG_XKUSAGE) { return x->ex_xkusage; } + // If there is no extension, extended key usage is unconstrained, so set all + // bits to one. return UINT32_MAX; } diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c index dbeabd0f9b..7428014dff 100644 --- a/crypto/x509/v3_utl.c +++ b/crypto/x509/v3_utl.c @@ -555,7 +555,7 @@ static int sk_strcmp(const char *const *a, const char *const *b) { return strcmp(*a, *b); } -STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x) { +STACK_OF(OPENSSL_STRING) *X509_get1_email(const X509 *x) { GENERAL_NAMES *gens; STACK_OF(OPENSSL_STRING) *ret; @@ -565,7 +565,7 @@ STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x) { return ret; } -STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x) { +STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(const X509 *x) { AUTHORITY_INFO_ACCESS *info; STACK_OF(OPENSSL_STRING) *ret = NULL; size_t i; @@ -588,7 +588,7 @@ STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x) { return ret; } -STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x) { +STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(const X509_REQ *x) { GENERAL_NAMES *gens; STACK_OF(X509_EXTENSION) *exts; STACK_OF(OPENSSL_STRING) *ret; @@ -1155,12 +1155,8 @@ ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) { return ret; err: - if (iptmp) { - OPENSSL_free(iptmp); - } - if (ret) { - ASN1_OCTET_STRING_free(ret); - } + OPENSSL_free(iptmp); + ASN1_OCTET_STRING_free(ret); return NULL; } diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c index de11723506..98d561e5f9 100644 --- a/crypto/x509/x509_req.c +++ b/crypto/x509/x509_req.c @@ -123,7 +123,7 @@ int X509_REQ_extension_nid(int req_nid) { return req_nid == NID_ext_req || req_nid == NID_ms_ext_req; } -STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) { +STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(const X509_REQ *req) { if (req == NULL || req->req_info == NULL) { return NULL; } @@ -136,8 +136,10 @@ STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) { return NULL; } - X509_ATTRIBUTE *attr = X509_REQ_get_attr(req, idx); - ASN1_TYPE *ext = X509_ATTRIBUTE_get0_type(attr, 0); + const X509_ATTRIBUTE *attr = X509_REQ_get_attr(req, idx); + // TODO(davidben): |X509_ATTRIBUTE_get0_type| is not const-correct. It should + // take and return a const pointer. + const ASN1_TYPE *ext = X509_ATTRIBUTE_get0_type((X509_ATTRIBUTE *)attr, 0); if (!ext || ext->type != V_ASN1_SEQUENCE) { return NULL; } diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index f99d464226..1cb706c0d2 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -1209,7 +1209,7 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) { if (issuer) { // Check for cRLSign bit if keyUsage present if ((issuer->ex_flags & EXFLAG_KUSAGE) && - !(issuer->ex_kusage & KU_CRL_SIGN)) { + !(issuer->ex_kusage & X509v3_KU_CRL_SIGN)) { ctx->error = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN; ok = ctx->verify_cb(0, ctx); if (!ok) { diff --git a/include/openssl/base.h b/include/openssl/base.h index 20be65f086..ba8e5a082b 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -378,6 +378,7 @@ typedef struct trust_token_client_st TRUST_TOKEN_CLIENT; typedef struct trust_token_issuer_st TRUST_TOKEN_ISSUER; typedef struct trust_token_method_st TRUST_TOKEN_METHOD; typedef struct v3_ext_ctx X509V3_CTX; +typedef struct v3_ext_method X509V3_EXT_METHOD; typedef struct x509_attributes_st X509_ATTRIBUTE; typedef struct x509_lookup_st X509_LOOKUP; typedef struct x509_lookup_method_st X509_LOOKUP_METHOD; diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 6ab021dfd9..6ad3a3190f 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -233,6 +233,45 @@ OPENSSL_EXPORT void X509_get0_uids(const X509 *x509, const ASN1_BIT_STRING **out_issuer_uid, const ASN1_BIT_STRING **out_subject_uid); +// The following bits are returned from |X509_get_extension_flags|. + +// EXFLAG_BCONS indicates the certificate has a basic constraints extension. +#define EXFLAG_BCONS 0x1 +// EXFLAG_KUSAGE indicates the certifcate has a key usage extension. +#define EXFLAG_KUSAGE 0x2 +// EXFLAG_XKUSAGE indicates the certifcate has an extended key usage extension. +#define EXFLAG_XKUSAGE 0x4 +// EXFLAG_NSCERT indicates the certificate has a legacy Netscape certificate +// type extension. +#define EXFLAG_NSCERT 0x8 +// EXFLAG_CA indicates the certificate has a basic constraints extension with +// the CA bit set. +#define EXFLAG_CA 0x10 +// EXFLAG_SI indicates the certificate is self-issued, i.e. its subject and +// issuer names match. +#define EXFLAG_SI 0x20 +// EXFLAG_V1 indicates an X.509v1 certificate. +#define EXFLAG_V1 0x40 +// EXFLAG_INVALID indicates an error processing some extension. The certificate +// should not be accepted. Note the lack of this bit does not imply all +// extensions are valid, only those used to compute extension flags. +#define EXFLAG_INVALID 0x80 +// EXFLAG_SET is an internal bit that indicates extension flags were computed. +#define EXFLAG_SET 0x100 +// EXFLAG_CRITICAL indicates an unsupported critical extension. The certificate +// should not be accepted. +#define EXFLAG_CRITICAL 0x200 +// EXFLAG_SS indicates the certificate is likely self-signed. That is, if it is +// self-issued, its authority key identifer (if any) matches itself, and its key +// usage extension (if any) allows certificate signatures. The signature itself +// is not checked in computing this bit. +#define EXFLAG_SS 0x2000 + +// X509_get_extension_flags decodes a set of extensions from |x509| and returns +// a collection of |EXFLAG_*| bits which reflect |x509|. If there was an error +// in computing this bitmask, the result will include the |EXFLAG_INVALID| bit. +OPENSSL_EXPORT uint32_t X509_get_extension_flags(X509 *x509); + // X509_get_pathlen returns path length constraint from the basic constraints // extension in |x509|. (See RFC 5280, section 4.2.1.9.) It returns -1 if the // constraint is not present, or if some extension in |x509| was invalid. @@ -242,6 +281,101 @@ OPENSSL_EXPORT void X509_get0_uids(const X509 *x509, // |X509_get_extensions_flags| and check the |EXFLAG_INVALID| bit. OPENSSL_EXPORT long X509_get_pathlen(X509 *x509); +// X509v3_KU_* are key usage bits returned from |X509_get_key_usage|. +#define X509v3_KU_DIGITAL_SIGNATURE 0x0080 +#define X509v3_KU_NON_REPUDIATION 0x0040 +#define X509v3_KU_KEY_ENCIPHERMENT 0x0020 +#define X509v3_KU_DATA_ENCIPHERMENT 0x0010 +#define X509v3_KU_KEY_AGREEMENT 0x0008 +#define X509v3_KU_KEY_CERT_SIGN 0x0004 +#define X509v3_KU_CRL_SIGN 0x0002 +#define X509v3_KU_ENCIPHER_ONLY 0x0001 +#define X509v3_KU_DECIPHER_ONLY 0x8000 + +// X509_get_key_usage returns a bitmask of key usages (see Section 4.2.1.3 of +// RFC 5280) which |x509| is valid for. This function only reports the first 16 +// bits, in a little-endian byte order, but big-endian bit order. That is, bits +// 0 though 7 are reported at 1<<7 through 1<<0, and bits 8 through 15 are +// reported at 1<<15 through 1<<8. +// +// Instead of depending on this bit order, callers should compare against the +// |X509v3_KU_*| constants. +// +// If |x509| has no key usage extension, all key usages are valid and this +// function returns |UINT32_MAX|. If there was an error processing |x509|'s +// extensions, or if the first 16 bits in the key usage extension were all zero, +// this function returns zero. +OPENSSL_EXPORT uint32_t X509_get_key_usage(X509 *x509); + +// XKU_* are extended key usage bits returned from +// |X509_get_extended_key_usage|. +#define XKU_SSL_SERVER 0x1 +#define XKU_SSL_CLIENT 0x2 +#define XKU_SMIME 0x4 +#define XKU_CODE_SIGN 0x8 +#define XKU_SGC 0x10 +#define XKU_OCSP_SIGN 0x20 +#define XKU_TIMESTAMP 0x40 +#define XKU_DVCS 0x80 +#define XKU_ANYEKU 0x100 + +// X509_get_extended_key_usage returns a bitmask of extended key usages (see +// Section 4.2.1.12 of RFC 5280) which |x509| is valid for. The result will be +// a combination of |XKU_*| constants. If checking an extended key usage not +// defined above, callers should extract the extended key usage extension +// separately, e.g. via |X509_get_ext_d2i|. +// +// If |x509| has no extended key usage extension, all extended key usages are +// valid and this function returns |UINT32_MAX|. If there was an error +// processing |x509|'s extensions, or if |x509|'s extended key usage extension +// contained no recognized usages, this function returns zero. +OPENSSL_EXPORT uint32_t X509_get_extended_key_usage(X509 *x509); + +// X509_get0_subject_key_id returns |x509|'s subject key identifier, if present. +// (See RFC 5280, section 4.2.1.2.) It returns NULL if the extension is not +// present or if some extension in |x509| was invalid. +// +// TODO(crbug.com/boringssl/381): Decoding an |X509| object will not check for +// invalid extensions. To detect the error case, call +// |X509_get_extensions_flags| and check the |EXFLAG_INVALID| bit. +OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x509); + +// X509_get0_authority_key_id returns keyIdentifier of |x509|'s authority key +// identifier, if the extension and field are present. (See RFC 5280, +// section 4.2.1.1.) It returns NULL if the extension is not present, if it is +// present but lacks a keyIdentifier field, or if some extension in |x509| was +// invalid. +// +// TODO(crbug.com/boringssl/381): Decoding an |X509| object will not check for +// invalid extensions. To detect the error case, call +// |X509_get_extensions_flags| and check the |EXFLAG_INVALID| bit. +OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x509); + +DEFINE_STACK_OF(GENERAL_NAME) +typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES; + +// X509_get0_authority_issuer returns the authorityCertIssuer of |x509|'s +// authority key identifier, if the extension and field are present. (See +// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present, +// if it is present but lacks a authorityCertIssuer field, or if some extension +// in |x509| was invalid. +// +// TODO(crbug.com/boringssl/381): Decoding an |X509| object will not check for +// invalid extensions. To detect the error case, call +// |X509_get_extensions_flags| and check the |EXFLAG_INVALID| bit. +OPENSSL_EXPORT const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x509); + +// X509_get0_authority_serial returns the authorityCertSerialNumber of |x509|'s +// authority key identifier, if the extension and field are present. (See +// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present, +// if it is present but lacks a authorityCertSerialNumber field, or if some +// extension in |x509| was invalid. +// +// TODO(crbug.com/boringssl/381): Decoding an |X509| object will not check for +// invalid extensions. To detect the error case, call +// |X509_get_extensions_flags| and check the |EXFLAG_INVALID| bit. +OPENSSL_EXPORT const ASN1_INTEGER *X509_get0_authority_serial(X509 *x509); + // X509_get0_extensions returns |x509|'s extension list, or NULL if |x509| omits // it. OPENSSL_EXPORT const STACK_OF(X509_EXTENSION) *X509_get0_extensions( @@ -332,6 +466,30 @@ OPENSSL_EXPORT int i2d_X509_tbs(X509 *x509, unsigned char **outp); // validation. OPENSSL_EXPORT int X509_verify(X509 *x509, EVP_PKEY *pkey); +// X509_get1_email returns a newly-allocated list of NUL-terminated strings +// containing all email addresses in |x509|'s subject and all rfc822name names +// in |x509|'s subject alternative names. Email addresses which contain embedded +// NUL bytes are skipped. +// +// On error, or if there are no such email addresses, it returns NULL. When +// done, the caller must release the result with |X509_email_free|. +OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_email(const X509 *x509); + +// X509_get1_ocsp returns a newly-allocated list of NUL-terminated strings +// containing all OCSP URIs in |x509|. That is, it collects all URI +// AccessDescriptions with an accessMethod of id-ad-ocsp in |x509|'s authority +// information access extension. URIs which contain embedded NUL bytes are +// skipped. +// +// On error, or if there are no such URIs, it returns NULL. When done, the +// caller must release the result with |X509_email_free|. +OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(const X509 *x509); + +// X509_email_free releases memory associated with |sk|, including |sk| itself. +// Each |OPENSSL_STRING| in |sk| must be a NUL-terminated string allocated with +// |OPENSSL_malloc|. If |sk| is NULL, no action is taken. +OPENSSL_EXPORT void X509_email_free(STACK_OF(OPENSSL_STRING) *sk); + // Issuing certificates. // @@ -1033,16 +1191,18 @@ OPENSSL_EXPORT int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, // (a Microsoft szOID_CERT_EXTENSIONS variant). OPENSSL_EXPORT int X509_REQ_extension_nid(int nid); -// X509_REQ_get_extensions decodes the list of requested extensions in |req| and -// returns a newly-allocated |STACK_OF(X509_EXTENSION)| containing the result. -// It returns NULL on error, or if |req| did not request extensions. +// X509_REQ_get_extensions decodes the most preferred list of requested +// extensions in |req| and returns a newly-allocated |STACK_OF(X509_EXTENSION)| +// containing the result. It returns NULL on error, or if |req| did not request +// extensions. // // CSRs do not store extensions directly. Instead there are attribute types // which are defined to hold extensions. See |X509_REQ_extension_nid|. This // function supports both pkcs-9-at-extensionRequest from RFC 2985 and the // Microsoft szOID_CERT_EXTENSIONS variant. If both are present, // pkcs-9-at-extensionRequest is preferred. -OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req); +OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions( + const X509_REQ *req); // X509_REQ_get0_signature sets |*out_sig| and |*out_alg| to the signature and // signature algorithm of |req|, respectively. Either output pointer may be NULL @@ -1060,6 +1220,17 @@ OPENSSL_EXPORT int X509_REQ_get_signature_nid(const X509_REQ *req); // one if the signature is valid and zero otherwise. OPENSSL_EXPORT int X509_REQ_verify(X509_REQ *req, EVP_PKEY *pkey); +// X509_REQ_get1_email returns a newly-allocated list of NUL-terminated strings +// containing all email addresses in |req|'s subject and all rfc822name names +// in |req|'s subject alternative names. The subject alternative names extension +// is extracted from the result of |X509_REQ_get_extensions|. Email addresses +// which contain embedded NUL bytes are skipped. +// +// On error, or if there are no such email addresses, it returns NULL. When +// done, the caller must release the result with |X509_email_free|. +OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email( + const X509_REQ *req); + // Issuing certificate requests. // @@ -2852,6 +3023,85 @@ OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_conf(LHASH_OF(CONF_VALUE) *conf, const char *name, const char *value); +// i2s_ASN1_OCTET_STRING returns a human-readable representation of |oct| as a +// newly-allocated, NUL-terminated string, or NULL on error. |method| is +// ignored. The caller must release the result with |OPENSSL_free| when done. +OPENSSL_EXPORT char *i2s_ASN1_OCTET_STRING(const X509V3_EXT_METHOD *method, + const ASN1_OCTET_STRING *oct); + +// s2i_ASN1_OCTET_STRING decodes |str| as a hexdecimal byte string, with +// optional colon separators between bytes. It returns a newly-allocated +// |ASN1_OCTET_STRING| with the result on success, or NULL on error. |method| +// and |ctx| are ignored. +OPENSSL_EXPORT ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING( + const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const char *str); + +// i2s_ASN1_INTEGER returns a human-readable representation of |aint| as a +// newly-allocated, NUL-terminated string, or NULL on error. |method| is +// ignored. The caller must release the result with |OPENSSL_free| when done. +OPENSSL_EXPORT char *i2s_ASN1_INTEGER(const X509V3_EXT_METHOD *method, + const ASN1_INTEGER *aint); + +// s2i_ASN1_INTEGER decodes |value| as the ASCII representation of an integer, +// and returns a newly-allocated |ASN1_INTEGER| containing the result, or NULL +// on error. |method| is ignored. If |value| begins with "0x" or "0X", the input +// is decoded in hexadecimal, otherwise decimal. +OPENSSL_EXPORT ASN1_INTEGER *s2i_ASN1_INTEGER(const X509V3_EXT_METHOD *method, + const char *value); + +// i2s_ASN1_ENUMERATED returns a human-readable representation of |aint| as a +// newly-allocated, NUL-terminated string, or NULL on error. |method| is +// ignored. The caller must release the result with |OPENSSL_free| when done. +OPENSSL_EXPORT char *i2s_ASN1_ENUMERATED(const X509V3_EXT_METHOD *method, + const ASN1_ENUMERATED *aint); + +// X509V3_conf_free releases memory associated with |CONF_VALUE|. +OPENSSL_EXPORT void X509V3_conf_free(CONF_VALUE *val); + +// i2v_GENERAL_NAME serializes |gen| as a |CONF_VALUE|. If |ret| is non-NULL, it +// appends the value to |ret| and returns |ret| on success or NULL on error. If +// it returns NULL, the caller is still responsible for freeing |ret|. If |ret| +// is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| containing the +// result. |method| is ignored. When done, the caller should release the result +// with |sk_CONF_VALUE_pop_free| and |X509V3_conf_free|. +// +// Do not use this function. This is an internal implementation detail of the +// human-readable print functions. If extracting a SAN list from a certificate, +// look at |gen| directly. +OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME( + const X509V3_EXT_METHOD *method, const GENERAL_NAME *gen, + STACK_OF(CONF_VALUE) *ret); + +// i2v_GENERAL_NAMES serializes |gen| as a list of |CONF_VALUE|s. If |ret| is +// non-NULL, it appends the values to |ret| and returns |ret| on success or NULL +// on error. If it returns NULL, the caller is still responsible for freeing +// |ret|. If |ret| is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| +// containing the results. |method| is ignored. +// +// Do not use this function. This is an internal implementation detail of the +// human-readable print functions. If extracting a SAN list from a certificate, +// look at |gen| directly. +OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES( + const X509V3_EXT_METHOD *method, const GENERAL_NAMES *gen, + STACK_OF(CONF_VALUE) *extlist); + +// a2i_IPADDRESS decodes |ipasc| as the textual representation of an IPv4 or +// IPv6 address. On success, it returns a newly-allocated |ASN1_OCTET_STRING| +// containing the decoded IP address. IPv4 addresses are represented as 4-byte +// strings and IPv6 addresses as 16-byte strings. On failure, it returns NULL. +OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); + +// a2i_IPADDRESS_NC decodes |ipasc| as the textual representation of an IPv4 or +// IPv6 address range. On success, it returns a newly-allocated +// |ASN1_OCTET_STRING| containing the decoded IP address, followed by the +// decoded mask. IPv4 ranges are represented as 8-byte strings and IPv6 ranges +// as 32-byte strings. On failure, it returns NULL. +// +// The text format decoded by this function is not the standard CIDR notiation. +// Instead, the mask after the "/" is represented as another IP address. For +// example, "192.168.0.0/16" would be written "192.168.0.0/255.255.0.0". +OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); + // Deprecated functions. @@ -2958,6 +3208,17 @@ OPENSSL_EXPORT STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line); #define X509_STORE_get1_certs X509_STORE_CTX_get1_certs #define X509_STORE_get1_crls X509_STORE_CTX_get1_crls +// The following constants are legacy aliases for |X509v3_KU_*|. +#define KU_DIGITAL_SIGNATURE X509v3_KU_DIGITAL_SIGNATURE +#define KU_NON_REPUDIATION X509v3_KU_NON_REPUDIATION +#define KU_KEY_ENCIPHERMENT X509v3_KU_KEY_ENCIPHERMENT +#define KU_DATA_ENCIPHERMENT X509v3_KU_DATA_ENCIPHERMENT +#define KU_KEY_AGREEMENT X509v3_KU_KEY_AGREEMENT +#define KU_KEY_CERT_SIGN X509v3_KU_KEY_CERT_SIGN +#define KU_CRL_SIGN X509v3_KU_CRL_SIGN +#define KU_ENCIPHER_ONLY X509v3_KU_ENCIPHER_ONLY +#define KU_DECIPHER_ONLY X509v3_KU_DECIPHER_ONLY + // Private structures. @@ -2969,17 +3230,6 @@ struct X509_algor_st { // Functions below this point have not yet been organized into sections. -#define X509v3_KU_DIGITAL_SIGNATURE 0x0080 -#define X509v3_KU_NON_REPUDIATION 0x0040 -#define X509v3_KU_KEY_ENCIPHERMENT 0x0020 -#define X509v3_KU_DATA_ENCIPHERMENT 0x0010 -#define X509v3_KU_KEY_AGREEMENT 0x0008 -#define X509v3_KU_KEY_CERT_SIGN 0x0004 -#define X509v3_KU_CRL_SIGN 0x0002 -#define X509v3_KU_ENCIPHER_ONLY 0x0001 -#define X509v3_KU_DECIPHER_ONLY 0x8000 -#define X509v3_KU_UNDEF 0xffff - // This stuff is certificate "auxiliary info" // it contains details which are useful in certificate // stores and databases. When used this is tagged onto @@ -3717,14 +3967,6 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, OPENSSL_EXPORT int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); -// Forward reference -struct v3_ext_method; -struct v3_ext_ctx; - -// Useful typedefs - -typedef struct v3_ext_method X509V3_EXT_METHOD; - typedef void *(*X509V3_EXT_NEW)(void); typedef void (*X509V3_EXT_FREE)(void *); typedef void *(*X509V3_EXT_D2I)(void *, const unsigned char **, long); @@ -3837,10 +4079,6 @@ struct GENERAL_NAME_st { } d; } /* GENERAL_NAME */; -DEFINE_STACK_OF(GENERAL_NAME) - -typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES; - DEFINE_STACK_OF(GENERAL_NAMES) typedef struct ACCESS_DESCRIPTION_st { @@ -3962,35 +4200,6 @@ struct ISSUING_DIST_POINT_st { // X509_PURPOSE stuff -#define EXFLAG_BCONS 0x1 -#define EXFLAG_KUSAGE 0x2 -#define EXFLAG_XKUSAGE 0x4 -#define EXFLAG_NSCERT 0x8 - -#define EXFLAG_CA 0x10 -// EXFLAG_SI indicates the certificate is self-issued, i.e. its subject and -// issuer names match. -#define EXFLAG_SI 0x20 -#define EXFLAG_V1 0x40 -#define EXFLAG_INVALID 0x80 -#define EXFLAG_SET 0x100 -#define EXFLAG_CRITICAL 0x200 -// EXFLAG_SS indicates the certificate is likely self-signed. That is, if it is -// self-issued, its authority key identifer (if any) matches itself, and its key -// usage extension (if any) allows certificate signatures. The signature itself -// is not checked in computing this bit. -#define EXFLAG_SS 0x2000 - -#define KU_DIGITAL_SIGNATURE 0x0080 -#define KU_NON_REPUDIATION 0x0040 -#define KU_KEY_ENCIPHERMENT 0x0020 -#define KU_DATA_ENCIPHERMENT 0x0010 -#define KU_KEY_AGREEMENT 0x0008 -#define KU_KEY_CERT_SIGN 0x0004 -#define KU_CRL_SIGN 0x0002 -#define KU_ENCIPHER_ONLY 0x0001 -#define KU_DECIPHER_ONLY 0x8000 - #define NS_SSL_CLIENT 0x80 #define NS_SSL_SERVER 0x40 #define NS_SMIME 0x20 @@ -4000,16 +4209,6 @@ struct ISSUING_DIST_POINT_st { #define NS_OBJSIGN_CA 0x01 #define NS_ANY_CA (NS_SSL_CA | NS_SMIME_CA | NS_OBJSIGN_CA) -#define XKU_SSL_SERVER 0x1 -#define XKU_SSL_CLIENT 0x2 -#define XKU_SMIME 0x4 -#define XKU_CODE_SIGN 0x8 -#define XKU_SGC 0x10 -#define XKU_OCSP_SIGN 0x20 -#define XKU_TIMESTAMP 0x40 -#define XKU_DVCS 0x80 -#define XKU_ANYEKU 0x100 - #define X509_PURPOSE_DYNAMIC 0x1 #define X509_PURPOSE_DYNAMIC_NAME 0x2 @@ -4049,39 +4248,10 @@ DECLARE_ASN1_FUNCTIONS(AUTHORITY_KEYID) DECLARE_ASN1_FUNCTIONS(GENERAL_NAME) OPENSSL_EXPORT GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a); -// i2v_GENERAL_NAME serializes |gen| as a |CONF_VALUE|. If |ret| is non-NULL, it -// appends the value to |ret| and returns |ret| on success or NULL on error. If -// it returns NULL, the caller is still responsible for freeing |ret|. If |ret| -// is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| containing the -// result. |method| is ignored. -// -// Do not use this function. This is an internal implementation detail of the -// human-readable print functions. If extracting a SAN list from a certificate, -// look at |gen| directly. -OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME( - const X509V3_EXT_METHOD *method, const GENERAL_NAME *gen, - STACK_OF(CONF_VALUE) *ret); - // TODO(https://crbug.com/boringssl/407): This is not const because it contains // an |X509_NAME|. DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES) -// i2v_GENERAL_NAMES serializes |gen| as a list of |CONF_VALUE|s. If |ret| is -// non-NULL, it appends the values to |ret| and returns |ret| on success or NULL -// on error. If it returns NULL, the caller is still responsible for freeing -// |ret|. If |ret| is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| -// containing the results. |method| is ignored. -// -// Do not use this function. This is an internal implementation detail of the -// human-readable print functions. If extracting a SAN list from a certificate, -// look at |gen| directly. -OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES( - const X509V3_EXT_METHOD *method, const GENERAL_NAMES *gen, - STACK_OF(CONF_VALUE) *extlist); -OPENSSL_EXPORT GENERAL_NAMES *v2i_GENERAL_NAMES( - const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, - const STACK_OF(CONF_VALUE) *nval); - DECLARE_ASN1_FUNCTIONS_const(OTHERNAME) DECLARE_ASN1_FUNCTIONS_const(EDIPARTYNAME) OPENSSL_EXPORT void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, @@ -4094,17 +4264,7 @@ OPENSSL_EXPORT int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, ASN1_OBJECT **poid, ASN1_TYPE **pvalue); -// i2s_ASN1_OCTET_STRING returns a human-readable representation of |oct| as a -// newly-allocated, NUL-terminated string, or NULL on error. |method| is -// ignored. The caller must release the result with |OPENSSL_free| when done. -OPENSSL_EXPORT char *i2s_ASN1_OCTET_STRING(const X509V3_EXT_METHOD *method, - const ASN1_OCTET_STRING *oct); - -OPENSSL_EXPORT ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING( - const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const char *str); - DECLARE_ASN1_FUNCTIONS_const(EXTENDED_KEY_USAGE) -OPENSSL_EXPORT int i2a_ACCESS_DESCRIPTION(BIO *bp, const ACCESS_DESCRIPTION *a); DECLARE_ASN1_FUNCTIONS_const(CERTIFICATEPOLICIES) DECLARE_ASN1_FUNCTIONS_const(POLICYINFO) @@ -4150,27 +4310,6 @@ DECLARE_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) DECLARE_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS) DECLARE_ASN1_ITEM(POLICY_CONSTRAINTS) -OPENSSL_EXPORT GENERAL_NAME *a2i_GENERAL_NAME(GENERAL_NAME *out, - const X509V3_EXT_METHOD *method, - const X509V3_CTX *ctx, - int gen_type, const char *value, - int is_nc); - -OPENSSL_EXPORT GENERAL_NAME *v2i_GENERAL_NAME(const X509V3_EXT_METHOD *method, - const X509V3_CTX *ctx, - const CONF_VALUE *cnf); -OPENSSL_EXPORT GENERAL_NAME *v2i_GENERAL_NAME_ex( - GENERAL_NAME *out, const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, - const CONF_VALUE *cnf, int is_nc); -OPENSSL_EXPORT void X509V3_conf_free(CONF_VALUE *val); - -OPENSSL_EXPORT char *i2s_ASN1_INTEGER(const X509V3_EXT_METHOD *meth, - const ASN1_INTEGER *aint); -OPENSSL_EXPORT ASN1_INTEGER *s2i_ASN1_INTEGER(const X509V3_EXT_METHOD *meth, - const char *value); -OPENSSL_EXPORT char *i2s_ASN1_ENUMERATED(const X509V3_EXT_METHOD *meth, - const ASN1_ENUMERATED *aint); - // X509V3_EXT_add registers |ext| as a custom extension for the extension type // |ext->ext_nid|. |ext| must be valid for the remainder of the address space's // lifetime. It returns one on success and zero on error. @@ -4355,68 +4494,6 @@ OPENSSL_EXPORT int X509_PURPOSE_set(int *p, int purpose); OPENSSL_EXPORT int X509_check_issued(X509 *issuer, X509 *subject); OPENSSL_EXPORT int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid); -OPENSSL_EXPORT uint32_t X509_get_extension_flags(X509 *x509); - -// X509_get_key_usage returns a bitmask of key usages (see Section 4.2.1.3 of -// RFC 5280) which |x509| is valid for. The result will be a combination of -// |KU_*| constants. -// -// If |x509| has no key usage extension, all key usages are valid and this -// function returns |UINT32_MAX|. If there was an error processing |x509|'s -// extensions, this function returns zero. -OPENSSL_EXPORT uint32_t X509_get_key_usage(X509 *x509); - -// X509_get_extended_key_usage returns a bitmask of extended key usages (see -// Section 4.2.1.12 of RFC 5280) which |x509| is valid for. The result will be -// a combination of |XKU_*| constants. -// -// If |x509| has no extended key usage extension, all extended key usages are -// valid and this function returns |UINT32_MAX|. If there was an error -// processing |x509|'s extensions, this function returns zero. -OPENSSL_EXPORT uint32_t X509_get_extended_key_usage(X509 *x509); - -// X509_get0_subject_key_id returns |x509|'s subject key identifier, if present. -// (See RFC 5280, section 4.2.1.2.) It returns NULL if the extension is not -// present or if some extension in |x509| was invalid. -// -// Note that decoding an |X509| object will not check for invalid extensions. To -// detect the error case, call |X509_get_extensions_flags| and check the -// |EXFLAG_INVALID| bit. -OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x509); - -// X509_get0_authority_key_id returns keyIdentifier of |x509|'s authority key -// identifier, if the extension and field are present. (See RFC 5280, -// section 4.2.1.1.) It returns NULL if the extension is not present, if it is -// present but lacks a keyIdentifier field, or if some extension in |x509| was -// invalid. -// -// Note that decoding an |X509| object will not check for invalid extensions. To -// detect the error case, call |X509_get_extensions_flags| and check the -// |EXFLAG_INVALID| bit. -OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x509); - -// X509_get0_authority_issuer returns the authorityCertIssuer of |x509|'s -// authority key identifier, if the extension and field are present. (See -// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present, -// if it is present but lacks a authorityCertIssuer field, or if some extension -// in |x509| was invalid. -// -// Note that decoding an |X509| object will not check for invalid extensions. To -// detect the error case, call |X509_get_extensions_flags| and check the -// |EXFLAG_INVALID| bit. -OPENSSL_EXPORT const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x509); - -// X509_get0_authority_serial returns the authorityCertSerialNumber of |x509|'s -// authority key identifier, if the extension and field are present. (See -// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present, -// if it is present but lacks a authorityCertSerialNumber field, or if some -// extension in |x509| was invalid. -// -// Note that decoding an |X509| object will not check for invalid extensions. To -// detect the error case, call |X509_get_extensions_flags| and check the -// |EXFLAG_INVALID| bit. -OPENSSL_EXPORT const ASN1_INTEGER *X509_get0_authority_serial(X509 *x509); - OPENSSL_EXPORT int X509_PURPOSE_get_count(void); OPENSSL_EXPORT X509_PURPOSE *X509_PURPOSE_get0(int idx); OPENSSL_EXPORT int X509_PURPOSE_get_by_sname(const char *sname); @@ -4432,11 +4509,6 @@ OPENSSL_EXPORT int X509_PURPOSE_get_trust(const X509_PURPOSE *xp); OPENSSL_EXPORT void X509_PURPOSE_cleanup(void); OPENSSL_EXPORT int X509_PURPOSE_get_id(const X509_PURPOSE *); -OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x); -OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x); -OPENSSL_EXPORT void X509_email_free(STACK_OF(OPENSSL_STRING) *sk); -OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); - // X509_check_* functions // @@ -4504,9 +4576,6 @@ OPENSSL_EXPORT int X509_check_ip(X509 *x, const unsigned char *chk, OPENSSL_EXPORT int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags); -OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); -OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); - #if defined(__cplusplus) } // extern C From c2ae13f88279fdb79e39c8dbd1b913f3a9177144 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 13:43:40 -0500 Subject: [PATCH 08/42] Move KU_* back to It turns out NSS and OpenSSL defined the same constants! All this time, Chromium has inadvertently relied on KU_* being defined in and not . Once we merged the two headers, this broke. Since we just deprecated these in favor of X509v3_KU_*, just move these back into . Change-Id: I93453527f30eee6df7630dc68c052c814aaeda02 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64607 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 540fcce9a5a8d92e4686d5d266dc19c91724ea0b) --- include/openssl/x509v3.h | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/include/openssl/x509v3.h b/include/openssl/x509v3.h index c8e52be185..80edd940e2 100644 --- a/include/openssl/x509v3.h +++ b/include/openssl/x509v3.h @@ -12,6 +12,28 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -// This header is provided in order to make compiling against code that expects -// OpenSSL easier. +#ifndef OPENSSL_HEADER_X509V3_H +#define OPENSSL_HEADER_X509V3_H + +// This header primarily exists in order to make compiling against code that +// expects OpenSSL easier. We have merged this header into . +// However, due to conflicts, some deprecated symbols are defined here. #include + +// Deprecated constants. + +// The following constants are legacy aliases for |X509v3_KU_*|. They are +// defined here instead of in because NSS's public headers use +// the same symbols. Some callers have inadvertently relied on the conflicts +// only being defined in this header. +#define KU_DIGITAL_SIGNATURE X509v3_KU_DIGITAL_SIGNATURE +#define KU_NON_REPUDIATION X509v3_KU_NON_REPUDIATION +#define KU_KEY_ENCIPHERMENT X509v3_KU_KEY_ENCIPHERMENT +#define KU_DATA_ENCIPHERMENT X509v3_KU_DATA_ENCIPHERMENT +#define KU_KEY_AGREEMENT X509v3_KU_KEY_AGREEMENT +#define KU_KEY_CERT_SIGN X509v3_KU_KEY_CERT_SIGN +#define KU_CRL_SIGN X509v3_KU_CRL_SIGN +#define KU_ENCIPHER_ONLY X509v3_KU_ENCIPHER_ONLY +#define KU_DECIPHER_ONLY X509v3_KU_DECIPHER_ONLY + +#endif // OPENSSL_HEADER_X509V3_H From 987521dc7ad089cfec3d1d892e20a6bdcaebab7c Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 15:02:10 -0500 Subject: [PATCH 09/42] Actually remove KU_* from x509.h I forgot half of https://boringssl-review.googlesource.com/c/boringssl/+/64607 Change-Id: Idbb827c5298c08bb1344272353ba4740802c55de Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64627 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 85e2f2c655a13e29988df777ed680ddf0969434d) --- include/openssl/x509.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 6ad3a3190f..012da8ee8d 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3208,17 +3208,6 @@ OPENSSL_EXPORT STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line); #define X509_STORE_get1_certs X509_STORE_CTX_get1_certs #define X509_STORE_get1_crls X509_STORE_CTX_get1_crls -// The following constants are legacy aliases for |X509v3_KU_*|. -#define KU_DIGITAL_SIGNATURE X509v3_KU_DIGITAL_SIGNATURE -#define KU_NON_REPUDIATION X509v3_KU_NON_REPUDIATION -#define KU_KEY_ENCIPHERMENT X509v3_KU_KEY_ENCIPHERMENT -#define KU_DATA_ENCIPHERMENT X509v3_KU_DATA_ENCIPHERMENT -#define KU_KEY_AGREEMENT X509v3_KU_KEY_AGREEMENT -#define KU_KEY_CERT_SIGN X509v3_KU_KEY_CERT_SIGN -#define KU_CRL_SIGN X509v3_KU_CRL_SIGN -#define KU_ENCIPHER_ONLY X509v3_KU_ENCIPHER_ONLY -#define KU_DECIPHER_ONLY X509v3_KU_DECIPHER_ONLY - // Private structures. From 74c1f2a2d130d50ca16af53b1e81832961cbb863 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Dec 2023 09:21:37 -0500 Subject: [PATCH 10/42] Remove dynamic X509_TRUST and X509_PURPOSE registration This is not thread-safe. Even if made thread-safe, it involves registering globals, so it's just not a good API. Note this means that there is no longer a way to configure custom trust OIDs or purpose checks. Evidently no one was doing that. Should a use case arise, I don't think it should be met by this API. The things one might want to configure here are: - Which OID to match against X509_add1_trust_object and X509_add1_reject_object - Whether self-signed certificates, if no trust objects are configured, also count as trust anchors - Which EKU OID to look for up the chain - Which legacy Netscape certificate type to look for (can we remove this?) - Which key usage bits to look for in the leaf We can simply add APIs for specifying those if we need them. Interestingly, there's a call to check_ca inside the purpose checks (which gets skipped if you don't configure a purpose!), but I think it may be redundant with the X509_check_ca call in the path verifier. Change-Id: If71ee3d0768b5fc71422852b4fcf7eb23e937dd2 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64507 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 396f2ef0855fee63cd7fd2f60fc77b0a447b1dc7) --- crypto/x509/v3_purp.c | 137 ++--------------------------------------- crypto/x509/x509_trs.c | 126 ++----------------------------------- include/openssl/x509.h | 19 ------ 3 files changed, 11 insertions(+), 271 deletions(-) diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c index 6ef3cb1c94..7ec3ad1733 100644 --- a/crypto/x509/v3_purp.c +++ b/crypto/x509/v3_purp.c @@ -94,9 +94,6 @@ static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca); static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca); -static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b); -static void xptable_free(X509_PURPOSE *p); - static X509_PURPOSE xstandard[] = { {X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0, check_purpose_ssl_client, (char *)"SSL client", (char *)"sslclient", NULL}, @@ -121,14 +118,6 @@ static X509_PURPOSE xstandard[] = { (char *)"timestampsign", NULL}, }; -#define X509_PURPOSE_COUNT (sizeof(xstandard) / sizeof(X509_PURPOSE)) - -static STACK_OF(X509_PURPOSE) *xptable = NULL; - -static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b) { - return (*a)->purpose - (*b)->purpose; -} - // As much as I'd like to make X509_check_purpose use a "const" X509* I // really can't because it does recalculate hashes and do other non-const // things. If |id| is -1 it just calls |x509v3_cache_extensions| for its @@ -161,21 +150,13 @@ int X509_PURPOSE_set(int *p, int purpose) { return 1; } -int X509_PURPOSE_get_count(void) { - if (!xptable) { - return X509_PURPOSE_COUNT; - } - return sk_X509_PURPOSE_num(xptable) + X509_PURPOSE_COUNT; -} +int X509_PURPOSE_get_count(void) { return OPENSSL_ARRAY_SIZE(xstandard); } X509_PURPOSE *X509_PURPOSE_get0(int idx) { - if (idx < 0) { + if (idx < 0 || (size_t)idx >= OPENSSL_ARRAY_SIZE(xstandard)) { return NULL; } - if (idx < (int)X509_PURPOSE_COUNT) { - return xstandard + idx; - } - return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT); + return xstandard + idx; } int X509_PURPOSE_get_by_sname(const char *sname) { @@ -190,118 +171,10 @@ int X509_PURPOSE_get_by_sname(const char *sname) { } int X509_PURPOSE_get_by_id(int purpose) { - X509_PURPOSE tmp; - size_t idx; - - if ((purpose >= X509_PURPOSE_MIN) && (purpose <= X509_PURPOSE_MAX)) { + if (purpose >= X509_PURPOSE_MIN && purpose <= X509_PURPOSE_MAX) { return purpose - X509_PURPOSE_MIN; } - tmp.purpose = purpose; - if (!xptable) { - return -1; - } - - if (!sk_X509_PURPOSE_find_awslc(xptable, &idx, &tmp)) { - return -1; - } - return idx + X509_PURPOSE_COUNT; -} - -int X509_PURPOSE_add(int id, int trust, int flags, - int (*ck)(const X509_PURPOSE *, const X509 *, int), - const char *name, const char *sname, void *arg) { - X509_PURPOSE *ptmp; - char *name_dup, *sname_dup; - - // This is set according to what we change: application can't set it - flags &= ~X509_PURPOSE_DYNAMIC; - // This will always be set for application modified trust entries - flags |= X509_PURPOSE_DYNAMIC_NAME; - // Get existing entry if any - int idx = X509_PURPOSE_get_by_id(id); - // Need a new entry - if (idx == -1) { - if (!(ptmp = OPENSSL_malloc(sizeof(X509_PURPOSE)))) { - return 0; - } - ptmp->flags = X509_PURPOSE_DYNAMIC; - } else { - ptmp = X509_PURPOSE_get0(idx); - } - - // Duplicate the supplied names. - name_dup = OPENSSL_strdup(name); - sname_dup = OPENSSL_strdup(sname); - if (name_dup == NULL || sname_dup == NULL) { - if (name_dup != NULL) { - OPENSSL_free(name_dup); - } - if (sname_dup != NULL) { - OPENSSL_free(sname_dup); - } - if (idx == -1) { - OPENSSL_free(ptmp); - } - return 0; - } - - // OPENSSL_free existing name if dynamic - if (ptmp->flags & X509_PURPOSE_DYNAMIC_NAME) { - OPENSSL_free(ptmp->name); - OPENSSL_free(ptmp->sname); - } - // dup supplied name - ptmp->name = name_dup; - ptmp->sname = sname_dup; - // Keep the dynamic flag of existing entry - ptmp->flags &= X509_PURPOSE_DYNAMIC; - // Set all other flags - ptmp->flags |= flags; - - ptmp->purpose = id; - ptmp->trust = trust; - ptmp->check_purpose = ck; - ptmp->usr_data = arg; - - // If its a new entry manage the dynamic table - if (idx == -1) { - // TODO(davidben): This should be locked. Alternatively, remove the dynamic - // registration mechanism entirely. The trouble is there no way to pass in - // the various parameters into an |X509_VERIFY_PARAM| directly. You can only - // register it in the global table and get an ID. - if (!xptable && !(xptable = sk_X509_PURPOSE_new(xp_cmp))) { - xptable_free(ptmp); - return 0; - } - if (!sk_X509_PURPOSE_push(xptable, ptmp)) { - xptable_free(ptmp); - return 0; - } - sk_X509_PURPOSE_sort(xptable); - } - return 1; -} - -static void xptable_free(X509_PURPOSE *p) { - if (!p) { - return; - } - if (p->flags & X509_PURPOSE_DYNAMIC) { - if (p->flags & X509_PURPOSE_DYNAMIC_NAME) { - OPENSSL_free(p->name); - OPENSSL_free(p->sname); - } - OPENSSL_free(p); - } -} - -void X509_PURPOSE_cleanup(void) { - unsigned int i; - sk_X509_PURPOSE_pop_free(xptable, xptable_free); - for (i = 0; i < X509_PURPOSE_COUNT; i++) { - xptable_free(xstandard + i); - } - xptable = NULL; + return -1; } int X509_PURPOSE_get_id(const X509_PURPOSE *xp) { return xp->purpose; } diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c index d8ab1dc229..ce4194b542 100644 --- a/crypto/x509/x509_trs.c +++ b/crypto/x509/x509_trs.c @@ -59,12 +59,10 @@ #include #include +#include "../internal.h" #include "internal.h" -static int tr_cmp(const X509_TRUST *const *a, const X509_TRUST *const *b); -static void trtable_free(X509_TRUST *p); - static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags); static int trust_1oid(X509_TRUST *trust, X509 *x, int flags); static int trust_compat(X509_TRUST *trust, X509 *x, int flags); @@ -92,14 +90,6 @@ static X509_TRUST trstandard[] = { {X509_TRUST_TSA, 0, trust_1oidany, (char *)"TSA server", NID_time_stamp, NULL}}; -#define X509_TRUST_COUNT (sizeof(trstandard) / sizeof(X509_TRUST)) - -static STACK_OF(X509_TRUST) *trtable = NULL; - -static int tr_cmp(const X509_TRUST *const *a, const X509_TRUST *const *b) { - return (*a)->trust - (*b)->trust; -} - int X509_check_trust(X509 *x, int id, int flags) { X509_TRUST *pt; int idx; @@ -123,38 +113,20 @@ int X509_check_trust(X509 *x, int id, int flags) { return pt->check_trust(pt, x, flags); } -int X509_TRUST_get_count(void) { - if (!trtable) { - return X509_TRUST_COUNT; - } - return sk_X509_TRUST_num(trtable) + X509_TRUST_COUNT; -} +int X509_TRUST_get_count(void) { return OPENSSL_ARRAY_SIZE(trstandard); } X509_TRUST *X509_TRUST_get0(int idx) { - if (idx < 0) { + if (idx < 0 || (size_t)idx >= OPENSSL_ARRAY_SIZE(trstandard)) { return NULL; } - if (idx < (int)X509_TRUST_COUNT) { - return trstandard + idx; - } - return sk_X509_TRUST_value(trtable, idx - X509_TRUST_COUNT); + return trstandard + idx; } int X509_TRUST_get_by_id(int id) { - X509_TRUST tmp; - size_t idx; - - if ((id >= X509_TRUST_MIN) && (id <= X509_TRUST_MAX)) { + if (id >= X509_TRUST_MIN && id <= X509_TRUST_MAX) { return id - X509_TRUST_MIN; } - tmp.trust = id; - if (!trtable) { - return -1; - } - if (!sk_X509_TRUST_find_awslc(trtable, &idx, &tmp)) { - return -1; - } - return idx + X509_TRUST_COUNT; + return -1; } int X509_TRUST_set(int *t, int trust) { @@ -166,92 +138,6 @@ int X509_TRUST_set(int *t, int trust) { return 1; } -int X509_TRUST_add(int id, int flags, int (*ck)(X509_TRUST *, X509 *, int), - const char *name, int arg1, void *arg2) { - int idx; - X509_TRUST *trtmp; - char *name_dup; - - // This is set according to what we change: application can't set it - flags &= ~X509_TRUST_DYNAMIC; - // This will always be set for application modified trust entries - flags |= X509_TRUST_DYNAMIC_NAME; - // Get existing entry if any - idx = X509_TRUST_get_by_id(id); - // Need a new entry - if (idx == -1) { - if (!(trtmp = OPENSSL_malloc(sizeof(X509_TRUST)))) { - return 0; - } - trtmp->flags = X509_TRUST_DYNAMIC; - } else { - trtmp = X509_TRUST_get0(idx); - } - - // Duplicate the supplied name. - name_dup = OPENSSL_strdup(name); - if (name_dup == NULL) { - if (idx == -1) { - OPENSSL_free(trtmp); - } - return 0; - } - - // OPENSSL_free existing name if dynamic - if (trtmp->flags & X509_TRUST_DYNAMIC_NAME) { - OPENSSL_free(trtmp->name); - } - trtmp->name = name_dup; - // Keep the dynamic flag of existing entry - trtmp->flags &= X509_TRUST_DYNAMIC; - // Set all other flags - trtmp->flags |= flags; - - trtmp->trust = id; - trtmp->check_trust = ck; - trtmp->arg1 = arg1; - trtmp->arg2 = arg2; - - // If its a new entry manage the dynamic table - if (idx == -1) { - // TODO(davidben): This should be locked. Alternatively, remove the dynamic - // registration mechanism entirely. The trouble is there no way to pass in - // the various parameters into an |X509_VERIFY_PARAM| directly. You can only - // register it in the global table and get an ID. - if (!trtable && !(trtable = sk_X509_TRUST_new(tr_cmp))) { - trtable_free(trtmp); - return 0; - } - if (!sk_X509_TRUST_push(trtable, trtmp)) { - trtable_free(trtmp); - return 0; - } - sk_X509_TRUST_sort(trtable); - } - return 1; -} - -static void trtable_free(X509_TRUST *p) { - if (!p) { - return; - } - if (p->flags & X509_TRUST_DYNAMIC) { - if (p->flags & X509_TRUST_DYNAMIC_NAME) { - OPENSSL_free(p->name); - } - OPENSSL_free(p); - } -} - -void X509_TRUST_cleanup(void) { - unsigned int i; - for (i = 0; i < X509_TRUST_COUNT; i++) { - trtable_free(trstandard + i); - } - sk_X509_TRUST_pop_free(trtable, trtable_free); - trtable = NULL; -} - int X509_TRUST_get_flags(const X509_TRUST *xp) { return xp->flags; } char *X509_TRUST_get0_name(const X509_TRUST *xp) { return xp->name; } diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 012da8ee8d..bb68109f60 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3258,12 +3258,6 @@ DEFINE_STACK_OF(X509_TRUST) #define X509_TRUST_MIN 1 #define X509_TRUST_MAX 8 - -// trust_flags values - -#define X509_TRUST_DYNAMIC 1 -#define X509_TRUST_DYNAMIC_NAME 2 - // check_trust return codes #define X509_TRUST_TRUSTED 1 @@ -3329,10 +3323,6 @@ OPENSSL_EXPORT int X509_check_trust(X509 *x, int id, int flags); OPENSSL_EXPORT int X509_TRUST_get_count(void); OPENSSL_EXPORT X509_TRUST *X509_TRUST_get0(int idx); OPENSSL_EXPORT int X509_TRUST_get_by_id(int id); -OPENSSL_EXPORT int X509_TRUST_add(int id, int flags, - int (*ck)(X509_TRUST *, X509 *, int), - const char *name, int arg1, void *arg2); -OPENSSL_EXPORT void X509_TRUST_cleanup(void); OPENSSL_EXPORT int X509_TRUST_get_flags(const X509_TRUST *xp); OPENSSL_EXPORT char *X509_TRUST_get0_name(const X509_TRUST *xp); OPENSSL_EXPORT int X509_TRUST_get_trust(const X509_TRUST *xp); @@ -4198,9 +4188,6 @@ struct ISSUING_DIST_POINT_st { #define NS_OBJSIGN_CA 0x01 #define NS_ANY_CA (NS_SSL_CA | NS_SMIME_CA | NS_OBJSIGN_CA) -#define X509_PURPOSE_DYNAMIC 0x1 -#define X509_PURPOSE_DYNAMIC_NAME 0x2 - typedef struct x509_purpose_st { int purpose; int trust; // Default trust ID @@ -4487,15 +4474,9 @@ OPENSSL_EXPORT int X509_PURPOSE_get_count(void); OPENSSL_EXPORT X509_PURPOSE *X509_PURPOSE_get0(int idx); OPENSSL_EXPORT int X509_PURPOSE_get_by_sname(const char *sname); OPENSSL_EXPORT int X509_PURPOSE_get_by_id(int id); -OPENSSL_EXPORT int X509_PURPOSE_add(int id, int trust, int flags, - int (*ck)(const X509_PURPOSE *, - const X509 *, int), - const char *name, const char *sname, - void *arg); OPENSSL_EXPORT char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp); OPENSSL_EXPORT char *X509_PURPOSE_get0_sname(const X509_PURPOSE *xp); OPENSSL_EXPORT int X509_PURPOSE_get_trust(const X509_PURPOSE *xp); -OPENSSL_EXPORT void X509_PURPOSE_cleanup(void); OPENSSL_EXPORT int X509_PURPOSE_get_id(const X509_PURPOSE *); From 01a46be3ce64e447adc0ef6d7685c60ab5208b59 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Dec 2023 17:20:35 -0500 Subject: [PATCH 11/42] Const-correct X509_TRUST and X509_PURPOSE This gets the built-in tables out of mutable data. Update-Note: No one uses these APIs except for rust-openssl. rust-openssl may need fixes because they seem to not quite handle C const correctly. Change-Id: Ia23c61e2fe6a5637e59044e10ea2048cfe46e502 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64508 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 6099ab92c2447d41ab9e95f5c1bc8cfbc331439f) --- crypto/x509/v3_purp.c | 6 +++--- crypto/x509/x509_trs.c | 19 +++++++++---------- crypto/x509/x509_vfy.c | 3 +-- include/openssl/x509.h | 6 +++--- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c index 7ec3ad1733..4eb4ee830d 100644 --- a/crypto/x509/v3_purp.c +++ b/crypto/x509/v3_purp.c @@ -94,7 +94,7 @@ static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca); static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca); -static X509_PURPOSE xstandard[] = { +static const X509_PURPOSE xstandard[] = { {X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0, check_purpose_ssl_client, (char *)"SSL client", (char *)"sslclient", NULL}, {X509_PURPOSE_SSL_SERVER, X509_TRUST_SSL_SERVER, 0, @@ -152,7 +152,7 @@ int X509_PURPOSE_set(int *p, int purpose) { int X509_PURPOSE_get_count(void) { return OPENSSL_ARRAY_SIZE(xstandard); } -X509_PURPOSE *X509_PURPOSE_get0(int idx) { +const X509_PURPOSE *X509_PURPOSE_get0(int idx) { if (idx < 0 || (size_t)idx >= OPENSSL_ARRAY_SIZE(xstandard)) { return NULL; } @@ -160,7 +160,7 @@ X509_PURPOSE *X509_PURPOSE_get0(int idx) { } int X509_PURPOSE_get_by_sname(const char *sname) { - X509_PURPOSE *xptmp; + const X509_PURPOSE *xptmp; for (int i = 0; i < X509_PURPOSE_get_count(); i++) { xptmp = X509_PURPOSE_get0(i); if (!strcmp(xptmp->sname, sname)) { diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c index ce4194b542..122c20e775 100644 --- a/crypto/x509/x509_trs.c +++ b/crypto/x509/x509_trs.c @@ -63,9 +63,9 @@ #include "internal.h" -static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags); -static int trust_1oid(X509_TRUST *trust, X509 *x, int flags); -static int trust_compat(X509_TRUST *trust, X509 *x, int flags); +static int trust_1oidany(const X509_TRUST *trust, X509 *x, int flags); +static int trust_1oid(const X509_TRUST *trust, X509 *x, int flags); +static int trust_compat(const X509_TRUST *trust, X509 *x, int flags); static int obj_trust(int id, X509 *x, int flags); @@ -73,7 +73,7 @@ static int obj_trust(int id, X509 *x, int flags); // any gaps so we can just subtract the minimum trust value to get an index // into the table -static X509_TRUST trstandard[] = { +static const X509_TRUST trstandard[] = { {X509_TRUST_COMPAT, 0, trust_compat, (char *)"compatible", 0, NULL}, {X509_TRUST_SSL_CLIENT, 0, trust_1oidany, (char *)"SSL Client", NID_client_auth, NULL}, @@ -91,7 +91,6 @@ static X509_TRUST trstandard[] = { NULL}}; int X509_check_trust(X509 *x, int id, int flags) { - X509_TRUST *pt; int idx; if (id == -1) { return 1; @@ -109,13 +108,13 @@ int X509_check_trust(X509 *x, int id, int flags) { if (idx == -1) { return obj_trust(id, x, flags); } - pt = X509_TRUST_get0(idx); + const X509_TRUST *pt = X509_TRUST_get0(idx); return pt->check_trust(pt, x, flags); } int X509_TRUST_get_count(void) { return OPENSSL_ARRAY_SIZE(trstandard); } -X509_TRUST *X509_TRUST_get0(int idx) { +const X509_TRUST *X509_TRUST_get0(int idx) { if (idx < 0 || (size_t)idx >= OPENSSL_ARRAY_SIZE(trstandard)) { return NULL; } @@ -144,7 +143,7 @@ char *X509_TRUST_get0_name(const X509_TRUST *xp) { return xp->name; } int X509_TRUST_get_trust(const X509_TRUST *xp) { return xp->trust; } -static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags) { +static int trust_1oidany(const X509_TRUST *trust, X509 *x, int flags) { if (x->aux && (x->aux->trust || x->aux->reject)) { return obj_trust(trust->arg1, x, flags); } @@ -153,14 +152,14 @@ static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags) { return trust_compat(trust, x, flags); } -static int trust_1oid(X509_TRUST *trust, X509 *x, int flags) { +static int trust_1oid(const X509_TRUST *trust, X509 *x, int flags) { if (x->aux) { return obj_trust(trust->arg1, x, flags); } return X509_TRUST_UNTRUSTED; } -static int trust_compat(X509_TRUST *trust, X509 *x, int flags) { +static int trust_compat(const X509_TRUST *trust, X509 *x, int flags) { if (!x509v3_cache_extensions(x)) { return X509_TRUST_UNTRUSTED; } diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 1cb706c0d2..19698f06cf 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -1611,13 +1611,12 @@ int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose, } // If we have a purpose then check it is valid if (purpose) { - X509_PURPOSE *ptmp; idx = X509_PURPOSE_get_by_id(purpose); if (idx == -1) { OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_PURPOSE_ID); return 0; } - ptmp = X509_PURPOSE_get0(idx); + const X509_PURPOSE *ptmp = X509_PURPOSE_get0(idx); if (ptmp->trust == X509_TRUST_DEFAULT) { idx = X509_PURPOSE_get_by_id(def_purpose); if (idx == -1) { diff --git a/include/openssl/x509.h b/include/openssl/x509.h index bb68109f60..d724dfd657 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3232,7 +3232,7 @@ DECLARE_STACK_OF(GENERAL_NAME) struct x509_trust_st { int trust; int flags; - int (*check_trust)(struct x509_trust_st *, X509 *, int); + int (*check_trust)(const X509_TRUST *, X509 *, int); char *name; int arg1; void *arg2; @@ -3321,7 +3321,7 @@ OPENSSL_EXPORT int X509_verify_cert(X509_STORE_CTX *ctx); OPENSSL_EXPORT int X509_check_trust(X509 *x, int id, int flags); OPENSSL_EXPORT int X509_TRUST_get_count(void); -OPENSSL_EXPORT X509_TRUST *X509_TRUST_get0(int idx); +OPENSSL_EXPORT const X509_TRUST *X509_TRUST_get0(int idx); OPENSSL_EXPORT int X509_TRUST_get_by_id(int id); OPENSSL_EXPORT int X509_TRUST_get_flags(const X509_TRUST *xp); OPENSSL_EXPORT char *X509_TRUST_get0_name(const X509_TRUST *xp); @@ -4471,7 +4471,7 @@ OPENSSL_EXPORT int X509_check_issued(X509 *issuer, X509 *subject); OPENSSL_EXPORT int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid); OPENSSL_EXPORT int X509_PURPOSE_get_count(void); -OPENSSL_EXPORT X509_PURPOSE *X509_PURPOSE_get0(int idx); +OPENSSL_EXPORT const X509_PURPOSE *X509_PURPOSE_get0(int idx); OPENSSL_EXPORT int X509_PURPOSE_get_by_sname(const char *sname); OPENSSL_EXPORT int X509_PURPOSE_get_by_id(int id); OPENSSL_EXPORT char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp); From 41039db4ee9f9d9a4b96a6ccdb07cb371d41ffe9 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 4 Dec 2023 11:39:59 -0500 Subject: [PATCH 12/42] Unexport some STACK_OF types. STACK_OF(GENERAL_NAMES) is STACK_OF(STACK_OF(GENERAL_NAMES)). Nothing uses this. It appears to be a remnant of CMS and indirect CRL support. May as well trim the header slightly. STACK_OF(X509_VERIFY_PARAM) is a remnant of (non-thread-safe) global registration of X509_VERIFY_PARAMs. STACK_OF(X509_LOOKUP) is only used internally. May as well prune them from the header so the file expands to be a bit less code. Update-Note: A few obscure STACK_OF(T) types are unexported. This is not expected to impact anyone. Change-Id: I03757c8522531132a31270b6dab055966b6e9070 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64527 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit e6489902b7fb692875341b8ab5e57f0515f47bc1) --- crypto/x509/internal.h | 2 ++ include/openssl/x509.h | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index e178f0a289..4a0c5de9cd 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -284,6 +284,8 @@ struct x509_lookup_method_st { X509_OBJECT *ret); } /* X509_LOOKUP_METHOD */; +DEFINE_STACK_OF(X509_LOOKUP) + // This is used to hold everything. It is used for all certificate // validation. Once we have a certificate chain, the 'verify' // function is then called to actually check the cert chain. diff --git a/include/openssl/x509.h b/include/openssl/x509.h index d724dfd657..86c2f87562 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3264,8 +3264,6 @@ DEFINE_STACK_OF(X509_TRUST) #define X509_TRUST_REJECTED 2 #define X509_TRUST_UNTRUSTED 3 -DECLARE_STACK_OF(GENERAL_NAMES) - // X509_verify_cert_error_string returns |err| as a human-readable string, where // |err| should be one of the |X509_V_*| values. If |err| is unknown, it returns // a default description. @@ -3350,9 +3348,7 @@ certificate chain. #define X509_LU_CRL 2 #define X509_LU_PKEY 3 -DEFINE_STACK_OF(X509_LOOKUP) DEFINE_STACK_OF(X509_OBJECT) -DEFINE_STACK_OF(X509_VERIFY_PARAM) typedef int (*X509_STORE_CTX_verify_cb)(int, X509_STORE_CTX *); typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer, X509_STORE_CTX *ctx, @@ -4058,8 +4054,6 @@ struct GENERAL_NAME_st { } d; } /* GENERAL_NAME */; -DEFINE_STACK_OF(GENERAL_NAMES) - typedef struct ACCESS_DESCRIPTION_st { ASN1_OBJECT *method; GENERAL_NAME *location; From ec57e901aa2dfda5cfb5bf7cd8658a34f40086e5 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 16:23:50 -0500 Subject: [PATCH 13/42] Document GENERAL_NAME-related APIs Update-Note: In the process, unexport the ASN1_ITEMs, and the d2i/i2d functions for OTHERNAME and EDIPARTYNAME. These do not appear to be used and removing them will cut down on the amount of compatibility glue needed when we rewrite the parsers with a safer calling convention. Bug: 426 Change-Id: Ifc45867c0a0c832e5ef72deaec5a2c88b8d8ac6a Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64628 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit e89d99af0e4d7fb1df4d961d7aafdfed30d08d41) --- crypto/ocsp/ocsp_asn.c | 1 + crypto/x509/internal.h | 8 ++ crypto/x509/v3_akeya.c | 2 + crypto/x509/v3_genn.c | 22 ++-- include/openssl/x509.h | 246 +++++++++++++++++++++++++++++------------ 5 files changed, 199 insertions(+), 80 deletions(-) diff --git a/crypto/ocsp/ocsp_asn.c b/crypto/ocsp/ocsp_asn.c index b013eb24f3..6236ed1e0d 100644 --- a/crypto/ocsp/ocsp_asn.c +++ b/crypto/ocsp/ocsp_asn.c @@ -11,6 +11,7 @@ // https://tools.ietf.org/html/rfc6960#section-4.2.1 #include "internal.h" +#include "../x509/internal.h" ASN1_SEQUENCE(OCSP_SIGNATURE) = { ASN1_SIMPLE(OCSP_SIGNATURE, signatureAlgorithm, X509_ALGOR), diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 4a0c5de9cd..54f2c1f438 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -246,6 +246,14 @@ struct X509_crl_st { unsigned char crl_hash[SHA256_DIGEST_LENGTH]; } /* X509_CRL */; +// GENERAL_NAME is an |ASN1_ITEM| whose ASN.1 type is GeneralName and C type is +// |GENERAL_NAME*|. +DECLARE_ASN1_ITEM(GENERAL_NAME) + +// GENERAL_NAMES is an |ASN1_ITEM| whose ASN.1 type is SEQUENCE OF GeneralName +// and C type is |GENERAL_NAMES*|, aka |STACK_OF(GENERAL_NAME)*|. +DECLARE_ASN1_ITEM(GENERAL_NAMES) + struct X509_VERIFY_PARAM_st { int64_t check_time; // POSIX time to use unsigned long flags; // Various verify flags diff --git a/crypto/x509/v3_akeya.c b/crypto/x509/v3_akeya.c index 4a05aae630..8614b86c80 100644 --- a/crypto/x509/v3_akeya.c +++ b/crypto/x509/v3_akeya.c @@ -61,6 +61,8 @@ #include #include +#include "internal.h" + ASN1_SEQUENCE(AUTHORITY_KEYID) = { ASN1_IMP_OPT(AUTHORITY_KEYID, keyid, ASN1_OCTET_STRING, 0), diff --git a/crypto/x509/v3_genn.c b/crypto/x509/v3_genn.c index 42cd787906..ec3a282d64 100644 --- a/crypto/x509/v3_genn.c +++ b/crypto/x509/v3_genn.c @@ -70,7 +70,7 @@ ASN1_SEQUENCE(OTHERNAME) = { ASN1_EXP(OTHERNAME, value, ASN1_ANY, 0), } ASN1_SEQUENCE_END(OTHERNAME) -IMPLEMENT_ASN1_FUNCTIONS_const(OTHERNAME) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(OTHERNAME) ASN1_SEQUENCE(EDIPARTYNAME) = { // DirectoryString is a CHOICE type, so use explicit tagging. @@ -78,7 +78,7 @@ ASN1_SEQUENCE(EDIPARTYNAME) = { ASN1_EXP(EDIPARTYNAME, partyName, DIRECTORYSTRING, 1), } ASN1_SEQUENCE_END(EDIPARTYNAME) -IMPLEMENT_ASN1_FUNCTIONS_const(EDIPARTYNAME) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(EDIPARTYNAME) ASN1_CHOICE(GENERAL_NAME) = { ASN1_IMP(GENERAL_NAME, d.otherName, OTHERNAME, GEN_OTHERNAME), @@ -208,9 +208,9 @@ void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, void *value) { a->type = type; } -void *GENERAL_NAME_get0_value(const GENERAL_NAME *a, int *ptype) { - if (ptype) { - *ptype = a->type; +void *GENERAL_NAME_get0_value(const GENERAL_NAME *a, int *out_type) { + if (out_type) { + *out_type = a->type; } switch (a->type) { case GEN_X400: @@ -255,16 +255,16 @@ int GENERAL_NAME_set0_othername(GENERAL_NAME *gen, ASN1_OBJECT *oid, return 1; } -int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, ASN1_OBJECT **poid, - ASN1_TYPE **pvalue) { +int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, ASN1_OBJECT **out_oid, + ASN1_TYPE **out_value) { if (gen->type != GEN_OTHERNAME) { return 0; } - if (poid) { - *poid = gen->d.otherName->type_id; + if (out_oid != NULL) { + *out_oid = gen->d.otherName->type_id; } - if (pvalue) { - *pvalue = gen->d.otherName->value; + if (out_value != NULL) { + *out_value = gen->d.otherName->value; } return 1; } diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 86c2f87562..02e9fc87f4 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -1381,8 +1381,7 @@ DEFINE_STACK_OF(X509_NAME) // type is |X509_NAME*|. DECLARE_ASN1_ITEM(X509_NAME) -// X509_NAME_new returns a new, empty |X509_NAME_new|, or NULL on -// error. +// X509_NAME_new returns a new, empty |X509_NAME|, or NULL on error. OPENSSL_EXPORT X509_NAME *X509_NAME_new(void); // X509_NAME_free releases memory associated with |name|. @@ -1517,8 +1516,7 @@ OPENSSL_EXPORT int X509_NAME_add_entry_by_txt(X509_NAME *name, // (RFC 5280) and C type is |X509_NAME_ENTRY*|. DECLARE_ASN1_ITEM(X509_NAME_ENTRY) -// X509_NAME_ENTRY_new returns a new, empty |X509_NAME_ENTRY_new|, or NULL on -// error. +// X509_NAME_ENTRY_new returns a new, empty |X509_NAME_ENTRY|, or NULL on error. OPENSSL_EXPORT X509_NAME_ENTRY *X509_NAME_ENTRY_new(void); // X509_NAME_ENTRY_free releases memory associated with |entry|. @@ -1841,6 +1839,181 @@ OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509v3_add_ext( STACK_OF(X509_EXTENSION) **x, const X509_EXTENSION *ex, int loc); +// General names. +// +// A |GENERAL_NAME| represents an X.509 GeneralName structure, defined in RFC +// 5280, Section 4.2.1.6. General names are distinct from names (|X509_NAME|). A +// general name is a CHOICE type which may contain one of several name types, +// most commonly a DNS name or an IP address. General names most commonly appear +// in the subject alternative name (SAN) extension, though they are also used in +// other extensions. +// +// Many extensions contain a SEQUENCE OF GeneralName, or GeneralNames, so +// |STACK_OF(GENERAL_NAME)| is defined and aliased to |GENERAL_NAMES|. + +typedef struct otherName_st { + ASN1_OBJECT *type_id; + ASN1_TYPE *value; +} OTHERNAME; + +typedef struct EDIPartyName_st { + ASN1_STRING *nameAssigner; + ASN1_STRING *partyName; +} EDIPARTYNAME; + +// GEN_* are constants for the |type| field of |GENERAL_NAME|, defined below. +#define GEN_OTHERNAME 0 +#define GEN_EMAIL 1 +#define GEN_DNS 2 +#define GEN_X400 3 +#define GEN_DIRNAME 4 +#define GEN_EDIPARTY 5 +#define GEN_URI 6 +#define GEN_IPADD 7 +#define GEN_RID 8 + +// A GENERAL_NAME_st, aka |GENERAL_NAME|, represents an X.509 GeneralName. The +// |type| field determines which member of |d| is active. A |GENERAL_NAME| may +// also be empty, in which case |type| is -1 and |d| is NULL. Empty +// |GENERAL_NAME|s are invalid and will never be returned from the parser, but +// may be created temporarily, e.g. by |GENERAL_NAME_new|. +struct GENERAL_NAME_st { + int type; + union { + char *ptr; + OTHERNAME *otherName; + ASN1_IA5STRING *rfc822Name; + ASN1_IA5STRING *dNSName; + ASN1_STRING *x400Address; + X509_NAME *directoryName; + EDIPARTYNAME *ediPartyName; + ASN1_IA5STRING *uniformResourceIdentifier; + ASN1_OCTET_STRING *iPAddress; + ASN1_OBJECT *registeredID; + + // Old names + ASN1_OCTET_STRING *ip; // iPAddress + X509_NAME *dirn; // dirn + ASN1_IA5STRING *ia5; // rfc822Name, dNSName, uniformResourceIdentifier + ASN1_OBJECT *rid; // registeredID + } d; +} /* GENERAL_NAME */; + +// GENERAL_NAME_new returns a new, empty |GENERAL_NAME|, or NULL on error. +OPENSSL_EXPORT GENERAL_NAME *GENERAL_NAME_new(void); + +// GENERAL_NAME_free releases memory associated with |gen|. +OPENSSL_EXPORT void GENERAL_NAME_free(GENERAL_NAME *gen); + +// d2i_GENERAL_NAME parses up to |len| bytes from |*inp| as a DER-encoded X.509 +// GeneralName (RFC 5280), as described in |d2i_SAMPLE|. +OPENSSL_EXPORT GENERAL_NAME *d2i_GENERAL_NAME(GENERAL_NAME **out, + const uint8_t **inp, long len); + +// i2d_GENERAL_NAME marshals |in| as a DER-encoded X.509 GeneralName (RFC 5280), +// as described in |i2d_SAMPLE|. +// +// TODO(https://crbug.com/boringssl/407): This function should be const and +// thread-safe but is currently neither in some cases, notably if |in| is an +// directoryName and the |X509_NAME| has been modified. +OPENSSL_EXPORT int i2d_GENERAL_NAME(GENERAL_NAME *in, uint8_t **outp); + +// GENERAL_NAME_dup returns a newly-allocated copy of |gen|, or NULL on error. +// This function works by serializing the structure, so it will fail if |gen| is +// empty. +// +// TODO(https://crbug.com/boringssl/407): This function should be const and +// thread-safe but is currently neither in some cases, notably if |gen| is an +// directoryName and the |X509_NAME| has been modified. +OPENSSL_EXPORT GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *gen); + +// GENERAL_NAMES_new returns a new, empty |GENERAL_NAMES|, or NULL on error. +OPENSSL_EXPORT GENERAL_NAMES *GENERAL_NAMES_new(void); + +// GENERAL_NAMES_free releases memory associated with |gens|. +OPENSSL_EXPORT void GENERAL_NAMES_free(GENERAL_NAMES *gens); + +// d2i_GENERAL_NAMES parses up to |len| bytes from |*inp| as a DER-encoded +// SEQUENCE OF GeneralName, as described in |d2i_SAMPLE|. +OPENSSL_EXPORT GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **out, + const uint8_t **inp, long len); + +// i2d_GENERAL_NAMES marshals |in| as a DER-encoded SEQUENCE OF GeneralName, as +// described in |i2d_SAMPLE|. +// +// TODO(https://crbug.com/boringssl/407): This function should be const and +// thread-safe but is currently neither in some cases, notably if some element +// of |in| is an directoryName and the |X509_NAME| has been modified. +OPENSSL_EXPORT int i2d_GENERAL_NAMES(GENERAL_NAMES *in, uint8_t **outp); + +// OTHERNAME_new returns a new, empty |OTHERNAME|, or NULL on error. +OPENSSL_EXPORT OTHERNAME *OTHERNAME_new(void); + +// OTHERNAME_free releases memory associated with |name|. +OPENSSL_EXPORT void OTHERNAME_free(OTHERNAME *name); + +// EDIPARTYNAME_new returns a new, empty |EDIPARTYNAME|, or NULL on error. +// EDIPartyName is rarely used in practice, so callers are unlikely to need this +// function. +OPENSSL_EXPORT EDIPARTYNAME *EDIPARTYNAME_new(void); + +// EDIPARTYNAME_free releases memory associated with |name|. EDIPartyName is +// rarely used in practice, so callers are unlikely to need this function. +OPENSSL_EXPORT void EDIPARTYNAME_free(EDIPARTYNAME *name); + +// GENERAL_NAME_set0_value set |gen|'s type and value to |type| and |value|. +// |type| must be a |GEN_*| constant and |value| must be an object of the +// corresponding type. |gen| takes ownership of |value|, so |value| must have +// been an allocated object. +// +// WARNING: |gen| must be empty (typically as returned from |GENERAL_NAME_new|) +// before calling this function. If |gen| already contained a value, the +// previous contents will be leaked. +OPENSSL_EXPORT void GENERAL_NAME_set0_value(GENERAL_NAME *gen, int type, + void *value); + +// GENERAL_NAME_get0_value returns the in-memory representation of |gen|'s +// contents and, |out_type| is not NULL, sets |*out_type| to the type of |gen|, +// which will be a |GEN_*| constant. If |gen| is incomplete, the return value +// will be NULL and the type will be -1. +// +// WARNING: Casting the result of this function to the wrong type is a +// potentially exploitable memory error. Callers must check |gen|'s type, either +// via |*out_type| or checking |gen->type| directly, before inspecting the +// result. +// +// WARNING: This function is not const-correct. The return value should be +// const. Callers shoudl not mutate the returned object. +OPENSSL_EXPORT void *GENERAL_NAME_get0_value(const GENERAL_NAME *gen, + int *out_type); + +// GENERAL_NAME_set0_othername sets |gen| to be an OtherName with type |oid| and +// value |value|. On success, it returns one and takes ownership of |oid| and +// |value|, which must be created in a way compatible with |ASN1_OBJECT_free| +// and |ASN1_TYPE_free|, respectively. On allocation failure, it returns zero. +// In the failure case, the caller retains ownership of |oid| and |value| and +// must release them when done. +// +// WARNING: |gen| must be empty (typically as returned from |GENERAL_NAME_new|) +// before calling this function. If |gen| already contained a value, the +// previously contents will be leaked. +OPENSSL_EXPORT int GENERAL_NAME_set0_othername(GENERAL_NAME *gen, + ASN1_OBJECT *oid, + ASN1_TYPE *value); + +// GENERAL_NAME_get0_otherName, if |gen| is an OtherName, sets |*out_oid| and +// |*out_value| to the OtherName's type-id and value, respectively, and returns +// one. If |gen| is not an OtherName, it returns zero and leaves |*out_oid| and +// |*out_value| unmodified. Either of |out_oid| or |out_value| may be NULL to +// ignore the value. +// +// WARNING: This function is not const-correct. |out_oid| and |out_value| are +// not const, but callers should not mutate the resulting objects. +OPENSSL_EXPORT int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, + ASN1_OBJECT **out_oid, + ASN1_TYPE **out_value); + + // Algorithm identifiers. // // An |X509_ALGOR| represents an AlgorithmIdentifier structure, used in X.509 @@ -3225,7 +3398,6 @@ struct X509_algor_st { // the end of the certificate itself DECLARE_STACK_OF(DIST_POINT) -DECLARE_STACK_OF(GENERAL_NAME) // This is used for a table of trust checking functions @@ -4011,49 +4183,6 @@ struct BASIC_CONSTRAINTS_st { ASN1_INTEGER *pathlen; }; - -typedef struct otherName_st { - ASN1_OBJECT *type_id; - ASN1_TYPE *value; -} OTHERNAME; - -typedef struct EDIPartyName_st { - ASN1_STRING *nameAssigner; - ASN1_STRING *partyName; -} EDIPARTYNAME; - -struct GENERAL_NAME_st { -#define GEN_OTHERNAME 0 -#define GEN_EMAIL 1 -#define GEN_DNS 2 -#define GEN_X400 3 -#define GEN_DIRNAME 4 -#define GEN_EDIPARTY 5 -#define GEN_URI 6 -#define GEN_IPADD 7 -#define GEN_RID 8 - - int type; - union { - char *ptr; - OTHERNAME *otherName; // otherName - ASN1_IA5STRING *rfc822Name; - ASN1_IA5STRING *dNSName; - ASN1_STRING *x400Address; - X509_NAME *directoryName; - EDIPARTYNAME *ediPartyName; - ASN1_IA5STRING *uniformResourceIdentifier; - ASN1_OCTET_STRING *iPAddress; - ASN1_OBJECT *registeredID; - - // Old names - ASN1_OCTET_STRING *ip; // iPAddress - X509_NAME *dirn; // dirn - ASN1_IA5STRING *ia5; // rfc822Name, dNSName, uniformResourceIdentifier - ASN1_OBJECT *rid; // registeredID - } d; -} /* GENERAL_NAME */; - typedef struct ACCESS_DESCRIPTION_st { ASN1_OBJECT *method; GENERAL_NAME *location; @@ -4213,27 +4342,6 @@ DECLARE_ASN1_FUNCTIONS_const(BASIC_CONSTRAINTS) // an |X509_NAME|. DECLARE_ASN1_FUNCTIONS(AUTHORITY_KEYID) -// TODO(https://crbug.com/boringssl/407): This is not const because it contains -// an |X509_NAME|. -DECLARE_ASN1_FUNCTIONS(GENERAL_NAME) -OPENSSL_EXPORT GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a); - -// TODO(https://crbug.com/boringssl/407): This is not const because it contains -// an |X509_NAME|. -DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES) - -DECLARE_ASN1_FUNCTIONS_const(OTHERNAME) -DECLARE_ASN1_FUNCTIONS_const(EDIPARTYNAME) -OPENSSL_EXPORT void GENERAL_NAME_set0_value(GENERAL_NAME *a, int type, - void *value); -OPENSSL_EXPORT void *GENERAL_NAME_get0_value(const GENERAL_NAME *a, int *ptype); -OPENSSL_EXPORT int GENERAL_NAME_set0_othername(GENERAL_NAME *gen, - ASN1_OBJECT *oid, - ASN1_TYPE *value); -OPENSSL_EXPORT int GENERAL_NAME_get0_otherName(const GENERAL_NAME *gen, - ASN1_OBJECT **poid, - ASN1_TYPE **pvalue); - DECLARE_ASN1_FUNCTIONS_const(EXTENDED_KEY_USAGE) DECLARE_ASN1_FUNCTIONS_const(CERTIFICATEPOLICIES) From badb9bfe0c864d446f752c03fc0835ea8a902f84 Mon Sep 17 00:00:00 2001 From: Yan Peng <112029182+pennyannn@users.noreply.github.com> Date: Thu, 23 May 2024 11:31:54 -0700 Subject: [PATCH 14/42] Enable x86_64 AES-GCM proof in AWS-LC CI (#1592) This PR enables unbounded SAW proofs for AES-GCM on x86_64 (without AVX-512). Specifically, this PR: 1. Adds building and pushing of a new docker image: `ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm` 2. Updates relative files to add a new build `ubuntu2004_clang10x_formal_verification_saw_x86_64_aes_gcm` in linux-x86 runs The AES-GCM proof requires using a version of SAW that is incompatible with existing SAW proofs. We solve this problem by using a separate workflow for the AES-GCM proof. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- .../github_ci_linux_x86_omnibus.yaml | 23 ++++++++++++++----- tests/ci/codebuild/common/run_fv_target.yml | 13 +++++++++++ .../docker_images/linux-x86/build_images.sh | 1 + .../ci/docker_images/linux-x86/push_images.sh | 1 + .../create_image.sh | 17 ++++++++++++++ tests/ci/run_formal_verification.sh | 4 ++-- .../run_formal_verification_nsym_aarch64.sh | 7 ------ .../ci/run_formal_verification_saw_aarch64.sh | 7 ------ .../ci/run_formal_verification_saw_x86_64.sh | 7 ------ 9 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 tests/ci/codebuild/common/run_fv_target.yml create mode 100755 tests/ci/docker_images/linux-x86/ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm/create_image.sh delete mode 100755 tests/ci/run_formal_verification_nsym_aarch64.sh delete mode 100755 tests/ci/run_formal_verification_saw_aarch64.sh delete mode 100755 tests/ci/run_formal_verification_saw_x86_64.sh diff --git a/tests/ci/cdk/cdk/codebuild/github_ci_linux_x86_omnibus.yaml b/tests/ci/cdk/cdk/codebuild/github_ci_linux_x86_omnibus.yaml index 2aaddf0fc5..ff2c84a34c 100644 --- a/tests/ci/cdk/cdk/codebuild/github_ci_linux_x86_omnibus.yaml +++ b/tests/ci/cdk/cdk/codebuild/github_ci_linux_x86_omnibus.yaml @@ -475,36 +475,47 @@ batch: # Therefore, BUILD_GENERAL1_2XLARGE (72 vCPUs, 145 GB memory) is selected for quick check. # SAW proofs on platform X86_64 - identifier: ubuntu2004_clang10x_formal_verification_saw_x86_64 - buildspec: ./tests/ci/codebuild/common/run_simple_target.yml + buildspec: ./tests/ci/codebuild/common/run_fv_target.yml env: type: LINUX_CONTAINER privileged-mode: false compute-type: BUILD_GENERAL1_2XLARGE image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-10x_formal-verification-saw-x86_64_latest variables: - AWS_LC_CI_TARGET: "tests/ci/run_formal_verification_saw_x86_64.sh" + DOCKER_ENTRYPOINT: SAW/scripts/x86_64/docker_entrypoint.sh + + # SAW proof for AES-GCM on platform x86 + - identifier: ubuntu2004_clang10x_formal_verification_saw_x86_64_aes_gcm + buildspec: ./tests/ci/codebuild/common/run_fv_target.yml + env: + type: LINUX_CONTAINER + privileged-mode: false + compute-type: BUILD_GENERAL1_XLARGE + image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm_latest + variables: + DOCKER_ENTRYPOINT: SAW/scripts/x86_64/docker_entrypoint_aes_gcm.sh # SAW proofs on platform AArch64 - identifier: ubuntu2004_clang10x_formal_verification_saw_aarch64 - buildspec: ./tests/ci/codebuild/common/run_simple_target.yml + buildspec: ./tests/ci/codebuild/common/run_fv_target.yml env: type: LINUX_CONTAINER privileged-mode: false compute-type: BUILD_GENERAL1_2XLARGE image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-10x_formal-verification-saw-aarch64_latest variables: - AWS_LC_CI_TARGET: "tests/ci/run_formal_verification_saw_aarch64.sh" + DOCKER_ENTRYPOINT: SAW/scripts/aarch64/docker_entrypoint.sh # NSym proofs on platform AArch64 - identifier: ubuntu2204_clang14x_formal_verification_nsym_aarch64 - buildspec: ./tests/ci/codebuild/common/run_simple_target.yml + buildspec: ./tests/ci/codebuild/common/run_fv_target.yml env: type: LINUX_CONTAINER privileged-mode: false compute-type: BUILD_GENERAL1_2XLARGE image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-22.04_clang-14x_formal-verification-nsym-aarch64_latest variables: - AWS_LC_CI_TARGET: "tests/ci/run_formal_verification_nsym_aarch64.sh" + DOCKER_ENTRYPOINT: NSym/scripts/docker_entrypoint.sh # Build and test aws-lc without Perl/Go. - identifier: amazonlinux2_gcc7x_x86_64_minimal diff --git a/tests/ci/codebuild/common/run_fv_target.yml b/tests/ci/codebuild/common/run_fv_target.yml new file mode 100644 index 0000000000..e4d8e92c4d --- /dev/null +++ b/tests/ci/codebuild/common/run_fv_target.yml @@ -0,0 +1,13 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +version: 0.2 + +env: + variables: + GOPROXY: https://proxy.golang.org,direct + +phases: + build: + commands: + - ./tests/ci/run_formal_verification.sh "${DOCKER_ENTRYPOINT}" diff --git a/tests/ci/docker_images/linux-x86/build_images.sh b/tests/ci/docker_images/linux-x86/build_images.sh index 301013810a..57d4435fd9 100755 --- a/tests/ci/docker_images/linux-x86/build_images.sh +++ b/tests/ci/docker_images/linux-x86/build_images.sh @@ -49,6 +49,7 @@ docker build -t fedora-31:clang-9x -f fedora-31_clang-9x/Dockerfile ../dependenc ########################################################### ./ubuntu-20.04_clang-10x_formal-verification-saw-x86_64/create_image.sh ubuntu-20.04:clang-10x_formal-verification-saw-x86_64 +./ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm/create_image.sh ubuntu-20.04:clang-10x_formal-verification-saw-x86_64-aes-gcm ./ubuntu-20.04_clang-10x_formal-verification-saw-aarch64/create_image.sh ubuntu-20.04:clang-10x_formal-verification-saw-aarch64 ./ubuntu-22.04_clang-14x_formal-verification-nsym-aarch64/create_image.sh ubuntu-22.04:clang-14x_formal-verification-nsym-aarch64 diff --git a/tests/ci/docker_images/linux-x86/push_images.sh b/tests/ci/docker_images/linux-x86/push_images.sh index 4b142e9751..2b19b37753 100755 --- a/tests/ci/docker_images/linux-x86/push_images.sh +++ b/tests/ci/docker_images/linux-x86/push_images.sh @@ -27,6 +27,7 @@ tag_and_push_img 'ubuntu-20.04:clang-10x' "${ECS_REPO}:ubuntu-20.04_clang-10x" tag_and_push_img 'ubuntu-20.04:android' "${ECS_REPO}:ubuntu-20.04_android" tag_and_push_img 'ubuntu-20.04:clang-7x-bm-framework' "${ECS_REPO}:ubuntu-20.04_clang-7x-bm-framework" tag_and_push_img 'ubuntu-20.04:clang-10x_formal-verification-saw-x86_64' "${ECS_REPO}:ubuntu-20.04_clang-10x_formal-verification-saw-x86_64" +tag_and_push_img 'ubuntu-20.04:clang-10x_formal-verification-saw-x86_64-aes-gcm' "${ECS_REPO}:ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm" tag_and_push_img 'ubuntu-20.04:clang-10x_formal-verification-saw-aarch64' "${ECS_REPO}:ubuntu-20.04_clang-10x_formal-verification-saw-aarch64" tag_and_push_img 'ubuntu-20.04:gcc-7x' "${ECS_REPO}:ubuntu-20.04_gcc-7x" tag_and_push_img 'ubuntu-20.04:gcc-8x' "${ECS_REPO}:ubuntu-20.04_gcc-8x" diff --git a/tests/ci/docker_images/linux-x86/ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm/create_image.sh b/tests/ci/docker_images/linux-x86/ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm/create_image.sh new file mode 100755 index 0000000000..736fd56fd5 --- /dev/null +++ b/tests/ci/docker_images/linux-x86/ubuntu-20.04_clang-10x_formal-verification-saw-x86_64-aes-gcm/create_image.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +set -ex + +if [ -n "$1" ]; then + docker_tag="$1" +else + docker_tag='ubuntu-20.04:clang-10x_formal-verification-saw-x86_64-aes-gcm' +fi +rm -rf aws-lc-verification +git clone https://github.com/awslabs/aws-lc-verification.git +cd aws-lc-verification +docker build --pull --no-cache -f Dockerfile.saw_x86_aes_gcm -t ${docker_tag} . +cd .. +rm -rf aws-lc-verification diff --git a/tests/ci/run_formal_verification.sh b/tests/ci/run_formal_verification.sh index bc0e1405ad..64db1e2f16 100755 --- a/tests/ci/run_formal_verification.sh +++ b/tests/ci/run_formal_verification.sh @@ -4,7 +4,7 @@ set -ex -ENTRYDIR=$1 +ENTRYPOINT=$1 AWS_LC_DIR=${PWD##*/} cd ../ ROOT=$(pwd) @@ -19,6 +19,6 @@ git submodule update --init # Below is to copy code of **target** aws-lc to 'src' dir. rm -rf ./src/* && cp -r "${ROOT}/${AWS_LC_DIR}/"* ./src # execute the entry to saw scripts. -./$ENTRYDIR/docker_entrypoint.sh +./$ENTRYPOINT cd .. rm -rf aws-lc-verification-build diff --git a/tests/ci/run_formal_verification_nsym_aarch64.sh b/tests/ci/run_formal_verification_nsym_aarch64.sh deleted file mode 100755 index 0ee7a94dbe..0000000000 --- a/tests/ci/run_formal_verification_nsym_aarch64.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 OR ISC - -set -ex - -./tests/ci/run_formal_verification.sh NSym/scripts diff --git a/tests/ci/run_formal_verification_saw_aarch64.sh b/tests/ci/run_formal_verification_saw_aarch64.sh deleted file mode 100755 index 8ee0f83267..0000000000 --- a/tests/ci/run_formal_verification_saw_aarch64.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 OR ISC - -set -ex - -./tests/ci/run_formal_verification.sh SAW/scripts/aarch64 diff --git a/tests/ci/run_formal_verification_saw_x86_64.sh b/tests/ci/run_formal_verification_saw_x86_64.sh deleted file mode 100755 index a758214a89..0000000000 --- a/tests/ci/run_formal_verification_saw_x86_64.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 OR ISC - -set -ex - -./tests/ci/run_formal_verification.sh SAW/scripts/x86_64 From 6ced83cc05a7114d5a6b031323ea94c30ad9cb35 Mon Sep 17 00:00:00 2001 From: Yan Peng <112029182+pennyannn@users.noreply.github.com> Date: Thu, 23 May 2024 11:36:20 -0700 Subject: [PATCH 15/42] Update the formal verification section in README (#1570) --- README.md | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2ab69dc048..b7c221c177 100644 --- a/README.md +++ b/README.md @@ -135,12 +135,37 @@ positive and negative unit tests, fuzz tests, Sanitizers Portions of AWS-LC have been formally verified in [AWS-LC Formal Verification](https://github.com/awslabs/aws-lc-verification), the checks are run in AWS-LC’s CI on every change. The algorithms that have been -verified on certain platforms with caveats include: -* SHA-2 -* HMAC -* AES-KWP -* ECDH & ECDSA with curve P-384 -* HKDF +verified on certain CPUs with caveats include: +| Algorithm | Parameters | CPUs | +| ----------| ------------| ----------- +| SHA-2 | 384, 512 | SandyBridge+ | +| SHA-2 | 384 | neoverse-n1, neoverse-v1 | +| HMAC | with SHA-384 | SandyBridge+ | +| AES-KW(P) | 256 | SandyBridge+ | +| Elliptic Curve Keys and Parameters | with P-384 | SandyBridge+ | +| ECDSA | with P-384, SHA-384 | SandyBridge+ | +| ECDH | with P-384 | SandyBridge+ | +| HKDF | with HMAC-SHA384 | SandyBridge+ | + +The CPUs for which code is verified are defined in the following table. + +| CPUs | Description | +| --------------- | ------------| +| SandyBridge+ | x86-64 with AES-NI, CLMUL, and AVX. +| neoverse-n1 | aarch64 without SHA512. +| neoverse-v1 | aarch64 with SHA512. + +For more details on verified API functions, caveats and technology used, check the [AWS-LC Formal Verification](https://github.com/awslabs/aws-lc-verification) repository. + +In addition, we use assembly from [s2n-bignum](https://github.com/awslabs/s2n-bignum) to implement algorithms or sub-routines for x86_64 and aarch64. The following table shows the assembly routines that are formally verified using HOL Light. + +| Algorithms | Routines | CPUs | +| ----------| ------------| ------------| +| RSA | Montgomery multiplication | aarch64 | +| P-384 | field operations | aarch64, x86_64 | +| P-521 | field operations | aarch64, x86_64 | +| X25519 | field operations, group operations, scalar point multiplication | aarch64, x86_64 | +| Ed25519 | encode, decode, scalar point multiplication | aarch64, x86_64 | ## Have a Question? From 308dca9be4b326ccf166b3113de821aade70652b Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Thu, 23 May 2024 16:09:42 -0700 Subject: [PATCH 16/42] fix X509V3_EXT_METHODs for ocsp nonce extension (#1603) This backports more of the changes done in 736a283 to reintroduce the "old-style" `X509V3_EXT_METHOD`s. Although we're reintroducing the methods we discourage using, I've added checks to ensure that these are only used with `NID_id_pkix_OCSP_Nonce`. The assertion in `X509V3_EXT_add` is kept so that we continue to only allow |ASN1_ITEM|-based extensions for consumers that add their own. I've abstracted out the freeing logic to `x509v3_ext_free_with_method`. This used to be all over the place. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- crypto/asn1/tasn_dec.c | 2 +- crypto/asn1/tasn_fre.c | 2 +- crypto/x509/internal.h | 4 +++ crypto/x509/v3_conf.c | 15 ++++----- crypto/x509/v3_lib.c | 72 ++++++++++++++++++++++++++++++++++------ crypto/x509/v3_prn.c | 16 +++++---- crypto/x509/x509_test.cc | 31 +++++++++++++++++ 7 files changed, 116 insertions(+), 26 deletions(-) diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c index bc0edaf3fc..e7baf635e3 100644 --- a/crypto/asn1/tasn_dec.c +++ b/crypto/asn1/tasn_dec.c @@ -170,7 +170,7 @@ static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, ASN1_VALUE **pchptr; int combine = aclass & ASN1_TFLG_COMBINE; aclass &= ~ASN1_TFLG_COMBINE; - if (!pval) { + if (pval == NULL || it == NULL) { return 0; } diff --git a/crypto/asn1/tasn_fre.c b/crypto/asn1/tasn_fre.c index 8c6dab50a1..3cf0f7b327 100644 --- a/crypto/asn1/tasn_fre.c +++ b/crypto/asn1/tasn_fre.c @@ -78,7 +78,7 @@ void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, const ASN1_TEMPLATE *tt = NULL, *seqtt; const ASN1_EXTERN_FUNCS *ef; int i; - if (!pval) { + if (pval == NULL || it == NULL) { return; } if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) { diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 92bcf65166..ed0b8ad339 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -480,6 +480,10 @@ typedef struct { int x509V3_add_value_asn1_string(const char *name, const ASN1_STRING *value, STACK_OF(CONF_VALUE) **extlist); +// x509v3_ext_free_with_method frees |ext_data| with |ext_method|. +int x509v3_ext_free_with_method(const X509V3_EXT_METHOD *ext_method, + void *ext_data); + // X509V3_NAME_from_section adds attributes to |nm| by interpreting the // key/value pairs in |dn_sk|. It returns one on success and zero on error. // |chtype|, which should be one of |MBSTRING_*| constants, determines the diff --git a/crypto/x509/v3_conf.c b/crypto/x509/v3_conf.c index e086c195d7..f62e632fef 100644 --- a/crypto/x509/v3_conf.c +++ b/crypto/x509/v3_conf.c @@ -205,20 +205,19 @@ static X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, int ext_nid, if (ext_len < 0) { return NULL; } - } else { - // This is using the "old-style" ASN.1 callbacks. The only X509v3 extension - // that's still dependent on this code internally are OCSP nonce extensions. - // We can't easily migrate OCSP nonce extensions to use the "new" callbacks - // either, since OCSP nonces are handled differently in the code (according - // to OpenSSL and us having to maintain backwards compatibility with them). - // Every other |X509V3_EXT_METHOD|, both inside and outside the library, has - // and should have an |ASN1_ITEM|. + } else if (method->ext_nid == NID_id_pkix_OCSP_Nonce && method->i2d != NULL) { + // |NID_id_pkix_OCSP_Nonce| is the only extension using the "old-style" + // ASN.1 callbacks for backwards compatibility reasons. + // Note: See |v3_ext_method| under "include/openssl/x509.h". ext_len = method->i2d(ext_struc, NULL); if (!(ext_der = OPENSSL_malloc(ext_len))) { return NULL; } unsigned char *p = ext_der; method->i2d(ext_struc, &p); + } else { + OPENSSL_PUT_ERROR(X509, X509V3_R_OPERATION_NOT_DEFINED); + return NULL; } ASN1_OCTET_STRING *ext_oct = ASN1_OCTET_STRING_new(); diff --git a/crypto/x509/v3_lib.c b/crypto/x509/v3_lib.c index 7ada228e96..6b12975761 100644 --- a/crypto/x509/v3_lib.c +++ b/crypto/x509/v3_lib.c @@ -98,6 +98,27 @@ static int ext_cmp(const void *void_a, const void *void_b) { return ext_stack_cmp(a, b); } +static int x509v3_ext_method_validate(const X509V3_EXT_METHOD *ext_method) { + if (ext_method == NULL) { + return 0; + } + + if (ext_method->ext_nid == NID_id_pkix_OCSP_Nonce && + ext_method->d2i != NULL && ext_method->i2d != NULL && + ext_method->ext_new != NULL && ext_method->ext_free != NULL) { + // |NID_id_pkix_OCSP_Nonce| is the only extension using the "old-style" + // ASN.1 callbacks for backwards compatibility reasons. + // Note: See |v3_ext_method| under "include/openssl/x509.h". + return 1; + } + + if (ext_method->it == NULL) { + OPENSSL_PUT_ERROR(X509V3, X509V3_R_OPERATION_NOT_DEFINED); + return 0; + } + return 1; +} + const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) { X509V3_EXT_METHOD tmp; const X509V3_EXT_METHOD *t = &tmp, *const * ret; @@ -109,7 +130,7 @@ const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) { tmp.ext_nid = nid; ret = bsearch(&t, standard_exts, STANDARD_EXTENSION_COUNT, sizeof(X509V3_EXT_METHOD *), ext_cmp); - if (ret) { + if (ret != NULL && x509v3_ext_method_validate(*ret)) { return *ret; } if (!ext_list) { @@ -119,7 +140,12 @@ const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) { if (!sk_X509V3_EXT_METHOD_find_awslc(ext_list, &idx, &tmp)) { return NULL; } - return sk_X509V3_EXT_METHOD_value(ext_list, idx); + + const X509V3_EXT_METHOD *method = sk_X509V3_EXT_METHOD_value(ext_list, idx); + if (method != NULL && x509v3_ext_method_validate(method)) { + return method; + } + return NULL; } const X509V3_EXT_METHOD *X509V3_EXT_get(const X509_EXTENSION *ext) { @@ -130,19 +156,34 @@ const X509V3_EXT_METHOD *X509V3_EXT_get(const X509_EXTENSION *ext) { return X509V3_EXT_get_nid(nid); } -int X509V3_EXT_free(int nid, void *ext_data) { - const X509V3_EXT_METHOD *ext_method = X509V3_EXT_get_nid(nid); +int x509v3_ext_free_with_method(const X509V3_EXT_METHOD *ext_method, + void *ext_data) { if (ext_method == NULL) { OPENSSL_PUT_ERROR(X509V3, X509V3_R_CANNOT_FIND_FREE_FUNCTION); return 0; } - ASN1_item_free(ext_data, ASN1_ITEM_ptr(ext_method->it)); + if (ext_method->it != NULL) { + ASN1_item_free(ext_data, ASN1_ITEM_ptr(ext_method->it)); + } else if (ext_method->ext_nid == NID_id_pkix_OCSP_Nonce && + ext_method->ext_free != NULL) { + // |NID_id_pkix_OCSP_Nonce| is the only extension using the "old-style" + // ASN.1 callbacks for backwards compatibility reasons. + // Note: See |v3_ext_method| under "include/openssl/x509.h". + ext_method->ext_free(ext_data); + } else { + OPENSSL_PUT_ERROR(X509V3, X509V3_R_CANNOT_FIND_FREE_FUNCTION); + return 0; + } return 1; } +int X509V3_EXT_free(int nid, void *ext_data) { + return x509v3_ext_free_with_method(X509V3_EXT_get_nid(nid), ext_data); +} + int X509V3_EXT_add_alias(int nid_to, int nid_from) { -OPENSSL_BEGIN_ALLOW_DEPRECATED + OPENSSL_BEGIN_ALLOW_DEPRECATED const X509V3_EXT_METHOD *ext; X509V3_EXT_METHOD *tmpext; @@ -161,7 +202,7 @@ OPENSSL_BEGIN_ALLOW_DEPRECATED return 0; } return 1; -OPENSSL_END_ALLOW_DEPRECATED + OPENSSL_END_ALLOW_DEPRECATED } // Legacy function: we don't need to add standard extensions any more because @@ -179,14 +220,25 @@ void *X509V3_EXT_d2i(const X509_EXTENSION *ext) { return NULL; } p = ext->value->data; - void *ret = - ASN1_item_d2i(NULL, &p, ext->value->length, ASN1_ITEM_ptr(method->it)); + void *ret = NULL; + if (method->it) { + ret = + ASN1_item_d2i(NULL, &p, ext->value->length, ASN1_ITEM_ptr(method->it)); + } else if (method->ext_nid == NID_id_pkix_OCSP_Nonce && method->d2i != NULL) { + // |NID_id_pkix_OCSP_Nonce| is the only extension using the "old-style" + // ASN.1 callbacks for backwards compatibility reasons. + // Note: See |v3_ext_method| under "include/openssl/x509.h". + ret = method->d2i(NULL, &p, ext->value->length); + } else { + assert(0); + } + if (ret == NULL) { return NULL; } // Check for trailing data. if (p != ext->value->data + ext->value->length) { - ASN1_item_free(ret, ASN1_ITEM_ptr(method->it)); + x509v3_ext_free_with_method(method, ret); OPENSSL_PUT_ERROR(X509V3, X509V3_R_TRAILING_DATA_IN_EXTENSION); return NULL; } diff --git a/crypto/x509/v3_prn.c b/crypto/x509/v3_prn.c index 503ee13419..64f530b1fb 100644 --- a/crypto/x509/v3_prn.c +++ b/crypto/x509/v3_prn.c @@ -63,6 +63,8 @@ #include #include +#include "internal.h" + // Extension printing routines static int unknown_ext_print(BIO *out, const X509_EXTENSION *ext, @@ -114,8 +116,14 @@ int X509V3_EXT_print(BIO *out, const X509_EXTENSION *ext, unsigned long flag, if (method->it) { ext_str = ASN1_item_d2i(NULL, &p, ASN1_STRING_length(ext_data), ASN1_ITEM_ptr(method->it)); - } else { + } else if (method->ext_nid == NID_id_pkix_OCSP_Nonce && method->d2i != NULL) { + // |NID_id_pkix_OCSP_Nonce| is the only extension using the "old-style" + // ASN.1 callbacks for backwards compatibility reasons. + // Note: See |v3_ext_method| under "include/openssl/x509.h". ext_str = method->d2i(NULL, &p, ASN1_STRING_length(ext_data)); + } else { + OPENSSL_PUT_ERROR(X509V3, X509V3_R_OPERATION_NOT_DEFINED); + return 0; } if (!ext_str) { @@ -150,11 +158,7 @@ int X509V3_EXT_print(BIO *out, const X509_EXTENSION *ext, unsigned long flag, err: sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); OPENSSL_free(value); - if (method->it) { - ASN1_item_free(ext_str, ASN1_ITEM_ptr(method->it)); - } else { - method->ext_free(ext_str); - } + x509v3_ext_free_with_method(method, ext_str); return ok; } diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index ae79e1b50c..04b130ff0b 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -42,6 +42,23 @@ #include #endif +static const char kX509ExtensionsCert[] = R"( +-----BEGIN CERTIFICATE----- +MIICwDCCAimgAwIBAgIEAJNOazANBgkqhkiG9w0BAQEFADAQMQ4wDAYDVQQKEwVjYXNzYTAeFw0xMTAz +MzAxMTEwMzZaFw0xNDAzMzAxNTQwMzZaMC0xDjAMBgNVBAoMBWNhc9ijMRswGQYDVQQDExJlbXMuZ3J1 +cG9jYXNzYdeckJIwgZ8wDQYJKoZIhvcNAQEJBQADgY0AMIGJAoGBAFUEB/i0583rnah0hLRk1hleI5T0 +xw+naVIxs/h4ZHu19xva671kvXfN97PZBmzAwFAWXmfs5MUrviy6RjZjhc0Ad2510cmqWy9FUx4D7kjy +iuZZIaA0xL0AAfvJbDkXrGpHZWz8BkANFZ/RHl961BRB3fTGvPWiZK6C4mCwPgIDaQABjwGjggEIMIIB +BDALBgNVHRAEBAMCBaAwKwYDVR0VBCQKCf8O1s/Ozk3MzM8xNTo7MzZaMIIBBDALBgNVHRAEBAMCBaAw +KwYDVR0VBCQKCf8O1s/OKoUDBwExNTo7MzZagQ8BBDALBgNVHRAEBAMCBaAwKwYDVR0VBCQKAYPxKTDO +zs3MzM8xNTo7MzZagQ8yMDEzMDAxMzAxKjUwNlowEQYJKwYBBQUHMAECBAQDAgEHMBEGCWCGSAGG+EJZ +AQQEAwIGQDAbBgNVHQ0EFDASMBAGCSqGSIb2fQcQBAQDAgWgMCsGA1UdFQQkCiFD8Skwzs7NzMzPMTU6 +OzM2WoEPAQQwCwYDVR0QBAQDAgWgMA0GCSsGAQUFBzABBQUAA4GBAKTNw5fTOnPCcOFMARmAD1RtaAN8 +0TBKIy2A0hG/2dlNeI6s0dqZe6juverYmC5sOResakdlbPwGQA0Vn9EeX8Yr67/d9Ma89aJkroLiXbA+ +j2kCAwG+LLpGNmNwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBw5lmgITTEvXIj+8ls +-----END CERTIFICATE----- +)"; + std::string GetTestData(const char *path); @@ -1550,6 +1567,20 @@ static int Verify( return X509_V_OK; } +TEST(X509Test, X509Extensions) { + bssl::UniquePtr cert(CertFromPEM(kX509ExtensionsCert)); + ASSERT_TRUE(cert); + + for (int i = 0; i < X509_get_ext_count(cert.get()); i++) { + const X509_EXTENSION *ext = X509_get_ext(cert.get(), i); + void *parsed = X509V3_EXT_d2i(ext); + if (parsed != nullptr) { + int nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + ASSERT_TRUE(X509V3_EXT_free(nid, parsed)); + } + } +} + TEST(X509Test, TestVerify) { // cross_signing_root // | From fe068515cbfc39831bae47e7f98c6a68f4b521af Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Thu, 23 May 2024 18:00:24 -0700 Subject: [PATCH 17/42] Prepare for release v1.28.0 (#1604) --- crypto/fipsmodule/service_indicator/service_indicator_test.cc | 4 ++-- include/openssl/base.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/fipsmodule/service_indicator/service_indicator_test.cc b/crypto/fipsmodule/service_indicator/service_indicator_test.cc index c0695fe276..883fdb20af 100644 --- a/crypto/fipsmodule/service_indicator/service_indicator_test.cc +++ b/crypto/fipsmodule/service_indicator/service_indicator_test.cc @@ -4280,7 +4280,7 @@ TEST(ServiceIndicatorTest, DRBG) { // Since this is running in FIPS mode it should end in FIPS // Update this when the AWS-LC version number is modified TEST(ServiceIndicatorTest, AWSLCVersionString) { - ASSERT_STREQ(awslc_version_string(), "AWS-LC FIPS 1.27.0"); + ASSERT_STREQ(awslc_version_string(), "AWS-LC FIPS 1.28.0"); } #else @@ -4323,6 +4323,6 @@ TEST(ServiceIndicatorTest, BasicTest) { // Since this is not running in FIPS mode it shouldn't end in FIPS // Update this when the AWS-LC version number is modified TEST(ServiceIndicatorTest, AWSLCVersionString) { - ASSERT_STREQ(awslc_version_string(), "AWS-LC 1.27.0"); + ASSERT_STREQ(awslc_version_string(), "AWS-LC 1.28.0"); } #endif // AWSLC_FIPS diff --git a/include/openssl/base.h b/include/openssl/base.h index 20be65f086..383479d7e0 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -122,7 +122,7 @@ extern "C" { // ServiceIndicatorTest.AWSLCVersionString // Note: there are two versions of this test. Only one test is compiled // depending on FIPS mode. -#define AWSLC_VERSION_NUMBER_STRING "1.27.0" +#define AWSLC_VERSION_NUMBER_STRING "1.28.0" #if defined(BORINGSSL_SHARED_LIBRARY) From dde8d51131aace2fd9c12b1c6bba9cba69032d4e Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Fri, 24 May 2024 11:24:59 -0400 Subject: [PATCH 18/42] CI update for ubuntu 24.04 (#1599) ### Description of changes: * Fixes previous CI failures due to gcc-13 not being present. * Updates CI compilation "sanity test" to included compilers on Ubuntu 24.04 * Cherry-picked recent [commit from BoringSSL](https://github.com/google/boringssl/commit/c70190368c7040c37c1d655f0690bcde2b109a0d) that fixes gcc-14 build error. * Add CI cross-compilation test for `loongaarch64`. ### Callout * The gcc-14 FIPS build failure is related to the "delocator" and is still being researched. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --------- Co-authored-by: David Benjamin --- .github/workflows/actions-ci.yml | 115 ++++++++++++++++++++++++++----- .github/workflows/cross-test.yml | 37 +++++----- crypto/internal.h | 7 ++ 3 files changed, 123 insertions(+), 36 deletions(-) diff --git a/.github/workflows/actions-ci.yml b/.github/workflows/actions-ci.yml index e8a4d96868..21ea696785 100644 --- a/.github/workflows/actions-ci.yml +++ b/.github/workflows/actions-ci.yml @@ -168,24 +168,80 @@ jobs: echo ${env:SDEROOT} .\tests\ci\run_windows_tests.bat "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 true - gcc-9-13-sanity: + gcc-ubuntu-2004-sanity: + if: github.repository_owner == 'aws' + needs: [sanity-test-run] + strategy: + fail-fast: false + matrix: + gccversion: + - "10" + fips: + - "0" + - "1" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '>=1.18' + - name: Setup CMake + uses: threeal/cmake-action@v1.3.0 + with: + generator: Ninja + c-compiler: gcc-${{ matrix.gccversion }} + cxx-compiler: g++-${{ matrix.gccversion }} + options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release + - name: Build Project + run: cmake --build ./build --target all + - name: Run tests + run: cmake --build ./build --target run_tests + + gcc-ubuntu-2204-sanity: if: github.repository_owner == 'aws' needs: [sanity-test-run] strategy: fail-fast: false matrix: gccversion: - - "9" - "10" - "11" - "12" + fips: + - "0" + - "1" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '>=1.18' + - name: Setup CMake + uses: threeal/cmake-action@v1.3.0 + with: + generator: Ninja + c-compiler: gcc-${{ matrix.gccversion }} + cxx-compiler: g++-${{ matrix.gccversion }} + options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release + - name: Build Project + run: cmake --build ./build --target all + - name: Run tests + run: cmake --build ./build --target run_tests + + gcc-ubuntu-2404-sanity: + if: github.repository_owner == 'aws' + needs: [ sanity-test-run ] + strategy: + fail-fast: false + matrix: + gccversion: + - "12" - "13" - os: - - "ubuntu-latest" + - "14" fips: - "0" - "1" - runs-on: ${{ matrix.os }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 @@ -203,7 +259,38 @@ jobs: - name: Run tests run: cmake --build ./build --target run_tests - clang-13-15-sanity: + clang-ubuntu-2004-sanity: + if: github.repository_owner == 'aws' + needs: [sanity-test-run] + strategy: + fail-fast: false + matrix: + gccversion: + - "10" + - "11" + - "12" + fips: + - "0" + - "1" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '>=1.18' + - name: Setup CMake + uses: threeal/cmake-action@v1.3.0 + with: + generator: Ninja + c-compiler: clang-${{ matrix.gccversion }} + cxx-compiler: clang++-${{ matrix.gccversion }} + options: FIPS=${{ matrix.fips }} CMAKE_BUILD_TYPE=Release + - name: Build Project + run: cmake --build ./build --target all + - name: Run tests + run: cmake --build ./build --target run_tests + + clang-ubuntu-2204-sanity: if: github.repository_owner == 'aws' needs: [sanity-test-run] strategy: @@ -213,12 +300,10 @@ jobs: - "13" - "14" - "15" - os: - - "ubuntu-latest" fips: - "0" - "1" - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 @@ -236,22 +321,20 @@ jobs: - name: Run tests run: cmake --build ./build --target run_tests - clang-10-12-sanity: + clang-ubuntu-2404-sanity: if: github.repository_owner == 'aws' needs: [sanity-test-run] strategy: fail-fast: false matrix: gccversion: - - "10" - - "11" - - "12" - os: - - "ubuntu-20.04" + - "16" + - "17" + - "18" fips: - "0" - "1" - runs-on: ${{ matrix.os }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 diff --git a/.github/workflows/cross-test.yml b/.github/workflows/cross-test.yml index dc48877e80..4fb81a8ac2 100644 --- a/.github/workflows/cross-test.yml +++ b/.github/workflows/cross-test.yml @@ -10,7 +10,7 @@ concurrency: jobs: ppc64-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -21,7 +21,7 @@ jobs: run: tests/ci/run_cross_tests.sh ppc64 powerpc64-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_BUILD_TYPE=Release -DFIPS=1 -DBUILD_SHARED_LIBS=1" ppc32-non-fips-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -32,7 +32,7 @@ jobs: run: tests/ci/run_cross_tests.sh ppc powerpc-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" ppc32-fips-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -43,7 +43,7 @@ jobs: run: tests/ci/run_cross_tests.sh ppc powerpc-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release -DFIPS=1 -DBUILD_SHARED_LIBS=1" ppc64le-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -54,7 +54,7 @@ jobs: run: tests/ci/run_cross_tests.sh ppc64le powerpc64le-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_BUILD_TYPE=Release -DFIPS=1 -DBUILD_SHARED_LIBS=1" riscv64-non-fips-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -72,7 +72,7 @@ jobs: run: tests/ci/run_cross_tests.sh riscv riscv64-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" armv6-non-fips-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | @@ -81,22 +81,19 @@ jobs: - uses: actions/checkout@v4 - name: armv6 Build/Test run: tests/ci/run_cross_tests.sh armv6 armv6-unknown-linux-gnueabi "-DCMAKE_BUILD_TYPE=Release" -# TODO: enable once qemu-user-binfmt in ubuntu-latest supports loongarch64 -# * QEMU added support for loongarch64 in 7.1: https://www.qemu.org/2022/08/30/qemu-7-1-0/ -# * The next Ubuntu LTS should have a QEMU 8.x: https://packages.ubuntu.com/noble/qemu-user-binfmt -# loongarch64-non-fips-build-test: -# runs-on: ubuntu-latest -# steps: -# - name: Install qemu -# run: | -# sudo apt-get update -# sudo apt-get -y install qemu-user qemu-user-binfmt -# - uses: actions/checkout@v4 -# - name: armv6 Build/Test -# run: tests/ci/run_cross_tests.sh loongarch64 loongarch64-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" + loongarch64-non-fips-build-test: + runs-on: ubuntu-24.04 + steps: + - name: Install qemu + run: | + sudo apt-get update + sudo apt-get -y install qemu-user qemu-user-binfmt + - uses: actions/checkout@v4 + - name: loongarch64 Build/Test + run: tests/ci/run_cross_tests.sh loongarch64 loongarch64-unknown-linux-gnu "-DCMAKE_BUILD_TYPE=Release" s390x-non-fips-build-test: if: github.repository_owner == 'aws' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Install qemu run: | diff --git a/crypto/internal.h b/crypto/internal.h index 89e352d358..767a6a9258 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -1086,6 +1086,11 @@ static inline uint64_t CRYPTO_rotr_u64(uint64_t value, int shift) { // Arithmetic functions. +// The most efficient versions of these functions on GCC and Clang depend on C11 +// |_Generic|. If we ever need to call these from C++, we'll need to add a +// variant that uses C++ overloads instead. +#if !defined(__cplusplus) + // CRYPTO_addc_* returns |x + y + carry|, and sets |*out_carry| to the carry // bit. |carry| must be zero or one. #if OPENSSL_HAS_BUILTIN(__builtin_addc) @@ -1187,6 +1192,8 @@ static inline uint64_t CRYPTO_subc_u64(uint64_t x, uint64_t y, uint64_t borrow, #define CRYPTO_subc_w CRYPTO_subc_u32 #endif +#endif // !__cplusplus + // FIPS functions. From 8eb5656d78e3554bed3dced6d1f7770446a73318 Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Thu, 30 May 2024 08:04:02 -0700 Subject: [PATCH 19/42] Fix mariadb ssl_crl patch (#1606) MariaDB updated their patch with this commit: MariaDB/server@d8368ae. This updates our patch to be applicable with the new changes. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- tests/ci/integration/mariadb_patch/ssl_crl_expect.patch | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ci/integration/mariadb_patch/ssl_crl_expect.patch b/tests/ci/integration/mariadb_patch/ssl_crl_expect.patch index 948e60bfe1..94894e1515 100644 --- a/tests/ci/integration/mariadb_patch/ssl_crl_expect.patch +++ b/tests/ci/integration/mariadb_patch/ssl_crl_expect.patch @@ -1,12 +1,12 @@ diff --git a/mysql-test/main/ssl_crl.test b/mysql-test/main/ssl_crl.test -index 9b475857..58d23087 100644 +index a09490f2..2e71138b 100644 --- a/mysql-test/main/ssl_crl.test +++ b/mysql-test/main/ssl_crl.test @@ -8,6 +8,6 @@ --echo # try logging in with a certificate in the server's --ssl-crl : should fail - # OpenSSL 1.1.1a correctly rejects the certificate, but the error message is different ----replace_regex /ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+/ERROR 2026 (HY000): TLS\/SSL error: sslv3 alert certificate revoked/ -+--replace_regex /ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+/ERROR 2026 (HY000): TLS\/SSL error: sslv3 alert certificate revoked/ /SSLV3_ALERT_CERTIFICATE_REVOKED/sslv3 alert certificate revoked/ + # OpenSSL 1.1.1a and later releases correctly rejects the certificate, but the error message is different +---replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: sslv3 alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/ ++--replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: sslv3 alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/ /SSLV3_ALERT_CERTIFICATE_REVOKED/ssl\/tls alert certificate revoked/ --error 1 --exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_version'" 2>&1 From 7572352d5aad3ff5135283729e6942359249e93e Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 30 May 2024 11:06:57 -0400 Subject: [PATCH 20/42] Add `all_fuzz_tests` build target (#1605) ### Description of changes: * Add build target for compiling all fuzz tests By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- fuzz/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index a8314044c0..f10022ad78 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -1,3 +1,6 @@ +# Declare a dummy target to build all fuzz tests. +add_custom_target(all_fuzz_tests) + macro(fuzzer name) add_executable(${name} ${name}.cc) target_compile_options(${name} PRIVATE "-Wno-missing-prototypes") @@ -9,6 +12,7 @@ macro(fuzzer name) else() set_target_properties(${name} PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") endif() + add_dependencies(all_fuzz_tests ${name}) endmacro() fuzzer(arm_cpuinfo) From ffbf2dab9cfa51eaff87551e9e063680c5123731 Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Thu, 30 May 2024 08:07:45 -0700 Subject: [PATCH 21/42] add support for X509_CRL_http_nbio (#1596) ### Issues: Resolves `CryptoAlg-1648` ### Description of changes: `X509_CRL_http_nbio` is the last API needed to support the AzureSDK after implementation of https://github.com/aws/aws-lc/commit/7ef93cb8b9305405f619ab7639e1e7d0d3d4614c. It's a simple wrapper around an existing API, so might as well add support so we can resolve the ticket. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- crypto/ocsp/internal.h | 2 +- crypto/x509/x_all.c | 6 ++++++ include/openssl/base.h | 1 + include/openssl/ocsp.h | 1 - include/openssl/x509.h | 5 +++++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/crypto/ocsp/internal.h b/crypto/ocsp/internal.h index 1b1d86e7bb..875f809d85 100644 --- a/crypto/ocsp/internal.h +++ b/crypto/ocsp/internal.h @@ -258,7 +258,7 @@ DECLARE_ASN1_FUNCTIONS(OCSP_SIGNATURE) // Try exchanging request and response via HTTP on (non-)blocking BIO in rctx. OPENSSL_EXPORT int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx); -// Tries to exchange the request and response with OCSP_REQ_CTX_nbio(), but on +// Tries to exchange the request and response with |OCSP_REQ_CTX_nbio|, but on // success, it additionally parses the response, which must be a // DER-encoded ASN.1 structure. int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, ASN1_VALUE **pval, diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 23508c07f4..5ea030c256 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -67,6 +67,7 @@ #include #include "../asn1/internal.h" +#include "../ocsp/internal.h" #include "internal.h" @@ -120,6 +121,11 @@ int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) { x->sig_alg, x->signature, x->crl, ctx); } +int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl) { + return OCSP_REQ_CTX_nbio_d2i(rctx, (ASN1_VALUE **)pcrl, + ASN1_ITEM_rptr(X509_CRL)); +} + int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) { return (ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), x->sig_algor, NULL, x->signature, x->spkac, pkey, md)); diff --git a/include/openssl/base.h b/include/openssl/base.h index 9f9f67209f..a759dfa9b7 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -347,6 +347,7 @@ typedef struct evp_pkey_st EVP_PKEY; typedef struct hmac_ctx_st HMAC_CTX; typedef struct md4_state_st MD4_CTX; typedef struct md5_state_st MD5_CTX; +typedef struct ocsp_req_ctx_st OCSP_REQ_CTX; typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS; typedef struct pkcs12_st PKCS12; typedef struct pkcs8_priv_key_info_st PKCS8_PRIV_KEY_INFO; diff --git a/include/openssl/ocsp.h b/include/openssl/ocsp.h index dc6cbd38e1..ed82af5c18 100644 --- a/include/openssl/ocsp.h +++ b/include/openssl/ocsp.h @@ -50,7 +50,6 @@ extern "C" { typedef struct ocsp_cert_id_st OCSP_CERTID; typedef struct ocsp_one_request_st OCSP_ONEREQ; typedef struct ocsp_req_info_st OCSP_REQINFO; -typedef struct ocsp_req_ctx_st OCSP_REQ_CTX; typedef struct ocsp_signature_st OCSP_SIGNATURE; typedef struct ocsp_request_st OCSP_REQUEST; typedef struct ocsp_resp_bytes_st OCSP_RESPBYTES; diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 02e9fc87f4..c47d624869 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -966,6 +966,11 @@ OPENSSL_EXPORT int X509_CRL_set1_signature_value(X509_CRL *crl, const uint8_t *sig, size_t sig_len); +// X509_CRL_http_nbio calls |OCSP_REQ_CTX_nbio_d2i| to exchange the request +// via http. On success, it parses the response as a DER-encoded |X509_CRL| +// ASN.1 structure. +OPENSSL_EXPORT int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl); + // CRL entries. // From 3324473943d14bc97ffb450e8e146f8ae42a488f Mon Sep 17 00:00:00 2001 From: Nevine Ebeid <66388554+nebeid@users.noreply.github.com> Date: Fri, 31 May 2024 16:38:23 -0400 Subject: [PATCH 22/42] Cleanse the right amount of bytes in HMAC. (#1613) EVP_MAX_MD_BLOCK_SIZE is the block size in bytes. This commit partially reverts "Zeroize data immediately after use for FIPS (#911)", commit c7a9fd0dd20c0e35a5b7b98f22b78f16c8c34567. Prior to it, EVP_MAX_MD_BLOCK_SIZE was divided by 8 in a 64-bit word array initialisation in hmac.c --- crypto/fipsmodule/digest/internal.h | 1 - crypto/fipsmodule/hmac/hmac.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crypto/fipsmodule/digest/internal.h b/crypto/fipsmodule/digest/internal.h index 7b93754f5a..148e467077 100644 --- a/crypto/fipsmodule/digest/internal.h +++ b/crypto/fipsmodule/digest/internal.h @@ -63,7 +63,6 @@ extern "C" { #endif -#define EVP_MAX_MD_BLOCK_SIZE_BYTES (EVP_MAX_MD_BLOCK_SIZE / 8) struct env_md_st { // type contains a NID identifing the digest function. (For example, diff --git a/crypto/fipsmodule/hmac/hmac.c b/crypto/fipsmodule/hmac/hmac.c index 00edf495c9..0e576c026a 100644 --- a/crypto/fipsmodule/hmac/hmac.c +++ b/crypto/fipsmodule/hmac/hmac.c @@ -289,8 +289,8 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len, FIPS_service_indicator_lock_state(); int result = 0; - uint64_t pad[EVP_MAX_MD_BLOCK_SIZE_BYTES] = {0}; - uint64_t key_block[EVP_MAX_MD_BLOCK_SIZE_BYTES] = {0}; + uint64_t pad[EVP_MAX_MD_BLOCK_SIZE / sizeof(uint64_t)] = {0}; + uint64_t key_block[EVP_MAX_MD_BLOCK_SIZE / sizeof(uint64_t)] = {0}; if (block_size < key_len) { // Long keys are hashed. if (!methods->init(&ctx->md_ctx) || @@ -322,8 +322,8 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len, result = 1; end: - OPENSSL_cleanse(pad, EVP_MAX_MD_BLOCK_SIZE_BYTES); - OPENSSL_cleanse(key_block, EVP_MAX_MD_BLOCK_SIZE_BYTES); + OPENSSL_cleanse(pad, EVP_MAX_MD_BLOCK_SIZE); + OPENSSL_cleanse(key_block, EVP_MAX_MD_BLOCK_SIZE); FIPS_service_indicator_unlock_state(); if (result != 1) { // We're in some error state, so return our context to a known and well defined zero state. From 94e91d96d311404a0eaaca6039bfd7e6577c6dff Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Fri, 31 May 2024 16:38:41 -0400 Subject: [PATCH 23/42] Pin aws-lc-rs integ to nightly-2024-05-22 (#1612) ### Description of changes: * Pin to a working Rust nightly toolchain. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- .github/workflows/aws-lc-rs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aws-lc-rs.yml b/.github/workflows/aws-lc-rs.yml index ac68f8df2a..51f18012c0 100644 --- a/.github/workflows/aws-lc-rs.yml +++ b/.github/workflows/aws-lc-rs.yml @@ -10,6 +10,7 @@ concurrency: env: GOPROXY: https://proxy.golang.org,direct AWS_LC_SYS_CMAKE_BUILDER: 1 + RUST_NIGHTLY_TOOLCHAIN: nightly-2024-05-22 jobs: standard: if: github.repository_owner == 'aws' @@ -20,11 +21,11 @@ jobs: repository: awslabs/aws-lc-rs path: ./aws-lc-rs submodules: false - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: # Our aws-lc-sys generation scripts require nightly. - toolchain: nightly - override: true + toolchain: ${{ env.RUST_NIGHTLY_TOOLCHAIN }} + - run: rustup override set $RUST_NIGHTLY_TOOLCHAIN - uses: actions-rs/cargo@v1 with: command: install From a8ed881cbf918cdbdb2a97805242d274898f6754 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:22:18 -0400 Subject: [PATCH 24/42] Fix NTP integ test (#1616) ### Issues: Resolves #P132890351 ### Description of changes: * Fix NTP integration test By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- .../0001-Fix-MD5-and-Shake128-usage.patch | 74 +++++++++++++++++++ tests/ci/integration/ntp_patch/digests.patch | 11 --- tests/ci/integration/run_ntp_integration.sh | 2 +- 3 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 tests/ci/integration/ntp_patch/0001-Fix-MD5-and-Shake128-usage.patch delete mode 100644 tests/ci/integration/ntp_patch/digests.patch diff --git a/tests/ci/integration/ntp_patch/0001-Fix-MD5-and-Shake128-usage.patch b/tests/ci/integration/ntp_patch/0001-Fix-MD5-and-Shake128-usage.patch new file mode 100644 index 0000000000..820e8cde62 --- /dev/null +++ b/tests/ci/integration/ntp_patch/0001-Fix-MD5-and-Shake128-usage.patch @@ -0,0 +1,74 @@ +From 96ed539aad785b12756cd8513309eff631d39951 Mon Sep 17 00:00:00 2001 +From: Justin Smith +Date: Mon, 3 Jun 2024 06:59:44 -0400 +Subject: [PATCH] Fix MD5 and Shake128 usage + +--- + include/ntp_md5.h | 7 ++++++- + sntp/crypto.c | 19 ++++++++++++++----- + 2 files changed, 20 insertions(+), 6 deletions(-) + +diff --git a/include/ntp_md5.h b/include/ntp_md5.h +index 22caff3..29a4235 100644 +--- a/include/ntp_md5.h ++++ b/include/ntp_md5.h +@@ -9,13 +9,18 @@ + /* Use the system MD5 or fall back on libisc's */ + # if defined HAVE_MD5_H && defined HAVE_MD5INIT + # include +-# else ++# elif !defined(OPENSSL) + # include "isc/md5.h" + typedef isc_md5_t MD5_CTX; + # define MD5_DIGEST_LENGTH ISC_MD5_DIGESTLENGTH + # define MD5Init(c) isc_md5_init(c) + # define MD5Update(c, p, s) isc_md5_update(c, (const void *)p, s) + # define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */ ++# else ++#include ++# define MD5Init(c) MD5_Init(c) ++# define MD5Update(c, p, s) MD5_Update(c, p, s) ++# define MD5Final(d, c) MD5_Final((d), (c)) + # endif + + # define KEY_TYPE_MD5 NID_md5 +diff --git a/sntp/crypto.c b/sntp/crypto.c +index 1be2ea3..ea3f7e0 100644 +--- a/sntp/crypto.c ++++ b/sntp/crypto.c +@@ -10,6 +10,7 @@ + #include "crypto.h" + #include + #include "isc/string.h" ++#include "openssl/md5.h" + + struct key *key_ptr; + size_t key_cnt = 0; +@@ -101,11 +102,19 @@ compute_mac( + macname); + goto mac_fail; + } +- if (!EVP_DigestFinal(ctx, digest, &len)) { +- msyslog(LOG_ERR, "make_mac: MAC %s Digest Final failed.", +- macname); +- len = 0; +- } ++ if (EVP_MD_flags(ctx->digest) & EVP_MD_FLAG_XOF) { ++ // The callers expect the hash to always contain 16 bytes ++ len = MD5_DIGEST_LENGTH; ++ if (!EVP_DigestFinalXOF(ctx, digest, len)) { ++ msyslog(LOG_ERR, "make_mac: MAC %s Digest Final failed.", macname); ++ len = 0; ++ } ++ } else { ++ if (!EVP_DigestFinal(ctx, digest, &len)) { ++ msyslog(LOG_ERR, "make_mac: MAC %s Digest Final failed.", macname); ++ len = 0; ++ } ++ } + #else /* !OPENSSL */ + (void)key_type; /* unused, so try to prevent compiler from croaks */ + if (!EVP_DigestInit(ctx, EVP_get_digestbynid(key_type))) { +-- +2.39.3 (Apple Git-145) + diff --git a/tests/ci/integration/ntp_patch/digests.patch b/tests/ci/integration/ntp_patch/digests.patch deleted file mode 100644 index a0d71403f6..0000000000 --- a/tests/ci/integration/ntp_patch/digests.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/tests/libntp/digests.c -+++ b/tests/libntp/digests.c -@@ -238,7 +238,7 @@ - void test_Digest_MDC2(void); - void test_Digest_MDC2(void) - { --#ifdef OPENSSL -+#if defined(OPENSSL) && !defined(OPENSSL_NO_MDC2) - u_char expectedA[MAX_MAC_LEN] = - { - 0, 0, 0, KEYID_A, diff --git a/tests/ci/integration/run_ntp_integration.sh b/tests/ci/integration/run_ntp_integration.sh index 4b7c11ed68..eb0b9f2857 100755 --- a/tests/ci/integration/run_ntp_integration.sh +++ b/tests/ci/integration/run_ntp_integration.sh @@ -16,7 +16,7 @@ source tests/ci/common_posix_setup.sh # - AWS_LC_INSTALL_FOLDER # Assumes script is executed from the root of aws-lc directory -SCRATCH_FOLDER="${SRC_ROOT}/NTP_BUILD_ROOT" +SCRATCH_FOLDER="${SRC_ROOT}/../NTP_BUILD_ROOT" NTP_WEBSITE_URL="https://downloads.nwtime.org/ntp/" # - curl fetches the HTML content of the website, From bb4082699ed2f0d68d08d76b134a08265963c96b Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:15:32 -0400 Subject: [PATCH 25/42] Remove special aarch64 valgrind logic (#1618) ### Issues: Resolves #P132890850 ### Description of changes: * Fix Valgrind test. The constants to configure static CPU capabilities on aarch64 are no longer needed. ### Testing: * Tested on Ubuntu 24.04 and Amazon Linux 2023 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --- tests/ci/common_posix_setup.sh | 40 ++++------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/tests/ci/common_posix_setup.sh b/tests/ci/common_posix_setup.sh index c80ddbb9e4..5b7ce7f7a6 100644 --- a/tests/ci/common_posix_setup.sh +++ b/tests/ci/common_posix_setup.sh @@ -29,22 +29,6 @@ if [[ "${KERNEL_NAME}" == "Darwin" || "${KERNEL_NAME}" =~ .*BSD ]]; then else # Assume KERNEL_NAME is Linux. NUM_CPU_THREADS=$(grep -c ^processor /proc/cpuinfo) - if [[ $PLATFORM == "aarch64" ]]; then - CPU_PART=$(grep -Po -m 1 'CPU part.*:\s\K.*' /proc/cpuinfo) - NUM_CPU_PART=$(grep -c $CPU_PART /proc/cpuinfo) - # Set capabilities via the static flags for valgrind tests. - # This is because valgrind reports the instruction - # mrs %0, MIDR_EL1 - # which fetches the CPU part number, as illegal. - # For some reason, valgrind also reports SHA512 instructions illegal, - # so the SHA512 capability is not included below. - VALGRIND_STATIC_CAP_FLAGS="-DOPENSSL_STATIC_ARMCAP -DOPENSSL_STATIC_ARMCAP_NEON" - VALGRIND_STATIC_CAP_FLAGS+=" -DOPENSSL_STATIC_ARMCAP_AES -DOPENSSL_STATIC_ARMCAP_PMULL " - VALGRIND_STATIC_CAP_FLAGS+=" -DOPENSSL_STATIC_ARMCAP_SHA1 -DOPENSSL_STATIC_ARMCAP_SHA256 " - if [[ $NUM_CPU_PART == $NUM_CPU_THREADS ]] && [[ ${CPU_PART} =~ 0x[dD]40 ]]; then - VALGRIND_STATIC_CAP_FLAGS+=" -DOPENSSL_STATIC_ARMCAP_SHA3 -DOPENSSL_STATIC_ARMCAP_NEOVERSE_V1" - fi - fi fi # Pick cmake3 if possible. We don't know of any OS that installs a cmake3 @@ -160,31 +144,15 @@ function fips_build_and_test { } function build_and_test_valgrind { - if [[ $PLATFORM == "aarch64" ]]; then - run_build "$@" -DCMAKE_C_FLAGS="$VALGRIND_STATIC_CAP_FLAGS" - run_cmake_custom_target 'run_tests_valgrind' - - # Disable all capabilities and run again - # (We don't use the env. variable OPENSSL_armcap because it is currently - # restricted to the case of runtime discovery of capabilities - # in cpu_aarch64_linux.c) - run_build "$@" -DCMAKE_C_FLAGS="-DOPENSSL_STATIC_ARMCAP" - run_cmake_custom_target 'run_tests_valgrind' - else - run_build "$@" - run_cmake_custom_target 'run_tests_valgrind' - fi + run_build "$@" + run_cmake_custom_target 'run_tests_valgrind' } function build_and_test_ssl_runner_valgrind { export AWS_LC_GO_TEST_TIMEOUT="60m" - if [[ $PLATFORM == "aarch64" ]]; then - run_build "$@" -DCMAKE_C_FLAGS="$VALGRIND_STATIC_CAP_FLAGS" - else - run_build "$@" - fi - run_cmake_custom_target 'run_ssl_runner_tests_valgrind' + run_build "$@" + run_cmake_custom_target 'run_ssl_runner_tests_valgrind' } function build_and_test_with_sde { From 8258d73d5e56d764456663af10c414c118436071 Mon Sep 17 00:00:00 2001 From: Samuel Chiang Date: Mon, 3 Jun 2024 13:22:35 -0700 Subject: [PATCH 26/42] add back ASN1_dup with tests (#1591) `ASN1_dup` was removed in 419144a in favor of `ASN1_Item_dup`. This shouldn't normally be called directly, but Ruby happens to consume the API in several instances. I've optimized the function to allocate memory with `i2d` directly, instead of the ancient OpenSSL allocate and pass into behavior. Also added some tests for verification along with a do-not-use warning. --- crypto/asn1/a_dup.c | 20 +++++++++++++ crypto/asn1/asn1_test.cc | 64 ++++++++++++++++++++++++++++++++++++++++ include/openssl/asn1.h | 15 ++++++++-- 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/crypto/asn1/a_dup.c b/crypto/asn1/a_dup.c index b37a5c61b9..d052a44a8c 100644 --- a/crypto/asn1/a_dup.c +++ b/crypto/asn1/a_dup.c @@ -59,6 +59,26 @@ #include #include +void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *input) { + if (i2d == NULL || d2i == NULL || input == NULL) { + OPENSSL_PUT_ERROR(ASN1, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + // Size and allocate |buf|. + unsigned char *buf = NULL; + int buf_len = i2d(input, &buf); + if (buf == NULL || buf_len < 0) { + return NULL; + } + + // |buf| needs to be converted to |const| to be passed in. + const unsigned char *temp_input = buf; + char *ret = d2i(NULL, &temp_input, buf_len); + OPENSSL_free(buf); + return ret; +} + // ASN1_ITEM version of dup: this follows the model above except we don't // need to allocate the buffer. At some point this could be rewritten to // directly dup the underlying structure instead of doing and encode and diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index 1e0f453cc1..c8cea4613c 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -2412,6 +2412,70 @@ TEST(ASN1Test, LargeString) { #endif } + +// Wrapper functions are needed to get around Control Flow Integrity Sanitizers. +static int i2d_ASN1_TYPE_void(const void *a, unsigned char **out) { + return i2d_ASN1_TYPE((ASN1_TYPE *)a, out); +} +static void *d2i_ASN1_TYPE_void(void **a, const unsigned char **in, long len) { + return d2i_ASN1_TYPE((ASN1_TYPE **)a, in, len); +} +static int i2d_ECPrivateKey_void(const void *a, unsigned char **out) { + return i2d_ECPrivateKey((EC_KEY *)a, out); +} +static void *d2i_ECPrivateKey_void(void **a, const unsigned char **in, long len) { + return d2i_ECPrivateKey((EC_KEY **)a, in, len); +} +static int i2d_X509_PUBKEY_void(const void *a, unsigned char **out) { + return i2d_X509_PUBKEY((X509_PUBKEY *)a, out); +} +static void *d2i_X509_PUBKEY_void(void **a, const unsigned char **in, long len) { + return d2i_X509_PUBKEY((X509_PUBKEY **)a, in, len); +} + +TEST(ASN1Test, ASN1Dup) { + const uint8_t *tag = kTag128; + bssl::UniquePtr asn1( + d2i_ASN1_TYPE(nullptr, &tag, sizeof(kTag128))); + ASSERT_TRUE(asn1); + EXPECT_EQ(128, asn1->type); + bssl::UniquePtr asn1_copy((ASN1_TYPE *)ASN1_dup( + i2d_ASN1_TYPE_void, d2i_ASN1_TYPE_void, asn1.get())); + ASSERT_TRUE(asn1_copy); + EXPECT_EQ(ASN1_TYPE_cmp(asn1.get(), asn1_copy.get()), 0); + + bssl::UniquePtr key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + ASSERT_TRUE(key); + ASSERT_TRUE(EC_KEY_generate_key(key.get())); + bssl::UniquePtr key_copy((EC_KEY *)ASN1_dup( + i2d_ECPrivateKey_void, d2i_ECPrivateKey_void, key.get())); + ASSERT_TRUE(key_copy); + EXPECT_EQ(BN_cmp(EC_KEY_get0_private_key(key.get()), + EC_KEY_get0_private_key(key_copy.get())), + 0); + EXPECT_EQ(EC_GROUP_cmp(EC_KEY_get0_group(key.get()), + EC_KEY_get0_group(key_copy.get()), nullptr), + 0); + EXPECT_EQ(EC_POINT_cmp(EC_KEY_get0_group(key_copy.get()), + EC_KEY_get0_public_key(key.get()), + EC_KEY_get0_public_key(key_copy.get()), nullptr), + 0); + + bssl::UniquePtr evp_pkey(EVP_PKEY_new()); + X509_PUBKEY *tmp_key = nullptr; + ASSERT_TRUE(evp_pkey); + ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(evp_pkey.get(), key.get())); + ASSERT_TRUE(X509_PUBKEY_set(&tmp_key, evp_pkey.get())); + bssl::UniquePtr x509_pubkey(tmp_key); + bssl::UniquePtr x509_pubkey_copy((X509_PUBKEY *)ASN1_dup( + i2d_X509_PUBKEY_void, d2i_X509_PUBKEY_void, x509_pubkey.get())); + ASSERT_TRUE(x509_pubkey_copy); + EXPECT_EQ( + ASN1_STRING_cmp(X509_PUBKEY_get0_public_key(x509_pubkey.get()), + X509_PUBKEY_get0_public_key(x509_pubkey_copy.get())), + 0); +} + // The ASN.1 macros do not work on Windows shared library builds, where usage of // |OPENSSL_EXPORT| is a bit stricter. #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY) diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index 581b87369b..69bb666a0d 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h @@ -275,8 +275,7 @@ int i2d_SAMPLE(const SAMPLE *in, uint8_t **outp); // CHECKED_I2D_OF casts a given pointer to i2d_of_void* and statically checks // that it was a pointer to |type|'s |i2d| function. -#define CHECKED_I2D_OF(type, i2d) \ - ((i2d_of_void*) (1 ? i2d : ((I2D_OF(type))0))) +#define CHECKED_I2D_OF(type, i2d) ((i2d_of_void *)(1 ? i2d : ((I2D_OF(type))0))) // The following typedefs are sometimes used for pointers to functions like // |d2i_SAMPLE| and |i2d_SAMPLE|. Note, however, that these act on |void*|. @@ -391,6 +390,16 @@ OPENSSL_EXPORT ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **out, OPENSSL_EXPORT int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **outp, const ASN1_ITEM *it); +// ASN1_dup returns a newly-allocated copy of |x| by re-encoding with |i2d| and +// |d2i|. |i2d| and |d2i| must be the corresponding type functions of |x|. NULL +// is returned on error. +// +// WARNING: DO NOT USE. Casting the result of this function to the wrong type, +// or passing a pointer of the wrong type into this function, are potentially +// exploitable memory errors. Prefer directly calling |i2d| and |d2i| or other +// type-specific functions. +OPENSSL_EXPORT void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x); + // ASN1_item_dup returns a newly-allocated copy of |x|, or NULL on error. |x| // must be an object of |it|'s C type. // @@ -443,7 +452,7 @@ OPENSSL_EXPORT int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, void *in); // forces the user to use undefined C behavior and will cause failures when // running against undefined behavior sanitizers in clang. #define ASN1_i2d_bio_of(type, i2d, out, in) \ - (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), out, CHECKED_PTR_OF(type, in))) + (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), out, CHECKED_PTR_OF(type, in))) // ASN1_item_unpack parses |oct|'s contents as |it|'s ASN.1 type. It returns a // newly-allocated instance of |it|'s C type on success, or NULL on error. From a98f017dd829929135d146b43de839df1eee00de Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 22:18:48 -0500 Subject: [PATCH 27/42] Fuzz more extension parsers in the cert parser If we're going to rewrite the parsers later, let's cover them more thoroughly. Change-Id: Iab4bbb886da5e42caf4a6eff77cfedca8a33f085 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64629 Reviewed-by: Bob Beck Commit-Queue: David Benjamin Auto-Submit: David Benjamin (cherry picked from commit 811de7adb2b1742fe10ebcaf3b8dda66301c3cc1) --- fuzz/cert.cc | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/fuzz/cert.cc b/fuzz/cert.cc index e433450c34..2f4a54702c 100644 --- a/fuzz/cert.cc +++ b/fuzz/cert.cc @@ -19,24 +19,56 @@ #include "../crypto/x509/internal.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { - X509 *x509 = d2i_X509(NULL, &buf, len); - if (x509 != NULL) { + bssl::UniquePtr x509(d2i_X509(nullptr, &buf, len)); + if (x509 != nullptr) { // Extract the public key. - EVP_PKEY_free(X509_get_pubkey(x509)); + EVP_PKEY_free(X509_get_pubkey(x509.get())); // Fuzz some deferred parsing. - x509v3_cache_extensions(x509); + x509v3_cache_extensions(x509.get()); - // Reserialize the structure. - uint8_t *der = NULL; - i2d_X509(x509, &der); + // Fuzz every supported extension. + for (int i = 0; i < X509_get_ext_count(x509.get()); i++) { + const X509_EXTENSION *ext = X509_get_ext(x509.get(), i); + void *parsed = X509V3_EXT_d2i(ext); + if (parsed != nullptr) { + int nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); + BSSL_CHECK(nid != NID_undef); + + // Reserialize the extension. This should succeed if we were able to + // parse it. + // TODO(crbug.com/boringssl/352): Ideally we would also assert that + // |new_ext| is identical to |ext|, but our parser is not strict enough. + bssl::UniquePtr new_ext( + X509V3_EXT_i2d(nid, X509_EXTENSION_get_critical(ext), parsed)); + BSSL_CHECK(new_ext != nullptr); + + // This can only fail if |ext| was not a supported type, but then + // |X509V3_EXT_d2i| should have failed. + BSSL_CHECK(X509V3_EXT_free(nid, parsed)); + } + } + + // Reserialize |x509|. This should succeed if we were able to parse it. + // TODO(crbug.com/boringssl/352): Ideally we would also assert the output + // matches the input, but our parser is not strict enough. + uint8_t *der = nullptr; + int der_len = i2d_X509(x509.get(), &der); + BSSL_CHECK(der_len > 0); + OPENSSL_free(der); + + // Reserialize |x509|'s TBSCertificate without reusing the cached encoding. + // TODO(crbug.com/boringssl/352): Ideally we would also assert the output + // matches the input TBSCertificate, but our parser is not strict enough. + der = nullptr; + der_len = i2d_re_X509_tbs(x509.get(), &der); + BSSL_CHECK(der_len > 0); OPENSSL_free(der); BIO *bio = BIO_new(BIO_s_mem()); - X509_print(bio, x509); + X509_print(bio, x509.get()); BIO_free(bio); } - X509_free(x509); ERR_clear_error(); return 0; } From 5aefe649cb511916457f8635011980bb5ee5ca8f Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 22:33:21 -0500 Subject: [PATCH 28/42] Document functions that export verification internals It is a little silly to have documented obscure verification internals and not verification itself yet, but these were pretty easy. X509_check_purpose and X509_check_trust should also go here, but I'll do those when I've tackled more of X509_PURPOSE and X509_TRUST. I haven't moved X509_CHECK_FLAG_* because those should go with X509_VERIFY_PARAM_set_set_hostflags. X509_check_akid seems to have no callers, so I unexported that one. Bug: 426 Change-Id: I5af1824346db27fd52773ae27e943df89c1d5a87 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64630 Auto-Submit: David Benjamin Commit-Queue: Bob Beck Reviewed-by: Bob Beck (cherry picked from commit 9e40481d5bfe08e3e6daa5335c2cf076e1187ec1) --- crypto/x509/internal.h | 4 + crypto/x509/v3_purp.c | 10 +-- crypto/x509/v3_utl.c | 22 ++++-- include/openssl/ssl.h | 25 +++--- include/openssl/x509.h | 173 +++++++++++++++++++++++++++++++---------- 5 files changed, 164 insertions(+), 70 deletions(-) diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 7830d0872a..cc963005cf 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -567,6 +567,10 @@ GENERAL_NAMES *v2i_GENERAL_NAMES(const X509V3_EXT_METHOD *method, const X509V3_CTX *ctx, const STACK_OF(CONF_VALUE) *nval); +// TODO(https://crbug.com/boringssl/407): Make |issuer| const once the +// |X509_NAME| issue is resolved. +int X509_check_akid(X509 *issuer, const AUTHORITY_KEYID *akid); + #if defined(__cplusplus) } // extern C diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c index 4eb4ee830d..eaf9ff9479 100644 --- a/crypto/x509/v3_purp.c +++ b/crypto/x509/v3_purp.c @@ -597,14 +597,6 @@ static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca) { return 1; } -// Various checks to see if one certificate issued the second. This can be -// used to prune a set of possible issuer certificates which have been looked -// up using some simple method such as by subject name. These are: 1. Check -// issuer_name(subject) == subject_name(issuer) 2. If akid(subject) exists -// check it matches issuer 3. If key_usage(issuer) exists check it supports -// certificate signing returns 0 for OK, positive for reason for mismatch, -// reasons match codes for X509_verify_cert() - int X509_check_issued(X509 *issuer, X509 *subject) { if (X509_NAME_cmp(X509_get_subject_name(issuer), X509_get_issuer_name(subject))) { @@ -627,7 +619,7 @@ int X509_check_issued(X509 *issuer, X509 *subject) { return X509_V_OK; } -int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid) { +int X509_check_akid(X509 *issuer, const AUTHORITY_KEYID *akid) { if (!akid) { return X509_V_OK; } diff --git a/crypto/x509/v3_utl.c b/crypto/x509/v3_utl.c index 7428014dff..47f412993e 100644 --- a/crypto/x509/v3_utl.c +++ b/crypto/x509/v3_utl.c @@ -942,6 +942,9 @@ static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal, } if (rv > 0 && peername) { *peername = OPENSSL_strndup((char *)a->data, a->length); + if (*peername == NULL) { + return -1; + } } } else { int astrlen; @@ -960,13 +963,16 @@ static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal, } if (rv > 0 && peername) { *peername = OPENSSL_strndup((char *)astr, astrlen); + if (*peername == NULL) { + return -1; + } } OPENSSL_free(astr); } return rv; } -static int do_x509_check(X509 *x, const char *chk, size_t chklen, +static int do_x509_check(const X509 *x, const char *chk, size_t chklen, unsigned int flags, int check_type, char **peername) { int cnid = NID_undef; int alt_type; @@ -1033,8 +1039,8 @@ static int do_x509_check(X509 *x, const char *chk, size_t chklen, return 0; } -int X509_check_host(X509 *x, const char *chk, size_t chklen, unsigned int flags, - char **peername) { +int X509_check_host(const X509 *x, const char *chk, size_t chklen, + unsigned int flags, char **peername) { if (chk == NULL) { return -2; } @@ -1050,7 +1056,7 @@ int X509_check_host(X509 *x, const char *chk, size_t chklen, unsigned int flags, return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername); } -int X509_check_email(X509 *x, const char *chk, size_t chklen, +int X509_check_email(const X509 *x, const char *chk, size_t chklen, unsigned int flags) { if (chk == NULL) { return -2; @@ -1067,15 +1073,15 @@ int X509_check_email(X509 *x, const char *chk, size_t chklen, return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL); } -int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, +int X509_check_ip(const X509 *x, const unsigned char *chk, size_t chklen, unsigned int flags) { if (chk == NULL) { return -2; } - return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL); + return do_x509_check(x, (const char *)chk, chklen, flags, GEN_IPADD, NULL); } -int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) { +int X509_check_ip_asc(const X509 *x, const char *ipasc, unsigned int flags) { unsigned char ipout[16]; size_t iplen; @@ -1086,7 +1092,7 @@ int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) { if (iplen == 0) { return -2; } - return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL); + return do_x509_check(x, (const char *)ipout, iplen, flags, GEN_IPADD, NULL); } // Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index e85894e46d..b4dbbd3889 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2915,8 +2915,23 @@ OPENSSL_EXPORT int (*SSL_get_verify_callback(const SSL *ssl))( // ineffective. Simply checking that a host has some certificate from a CA is // rarely meaningful—you have to check that the CA believed that the host was // who you expect to be talking to. +// +// By default, both subject alternative names and the subject's common name +// attribute are checked. The latter has long been deprecated, so callers should +// call |SSL_set_hostflags| with |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| to use +// the standard behavior. https://crbug.com/boringssl/464 tracks fixing the +// default. OPENSSL_EXPORT int SSL_set1_host(SSL *ssl, const char *hostname); +// SSL_set_hostflags calls |X509_VERIFY_PARAM_set_hostflags| on the +// |X509_VERIFY_PARAM| associated with this |SSL*|. |flags| should be some +// combination of the |X509_CHECK_*| constants. +// +// |X509_V_FLAG_X509_STRICT| is always ON by default and +// |X509_V_FLAG_ALLOW_PROXY_CERTS| is always OFF. Both are non-configurable. +// See |x509.h| for more details. +OPENSSL_EXPORT void SSL_set_hostflags(SSL *ssl, unsigned flags); + // SSL_CTX_set_verify_depth sets the maximum depth of a certificate chain // accepted in verification. This number does not include the leaf, so a depth // of 1 allows the leaf and one CA certificate. @@ -3093,16 +3108,6 @@ OPENSSL_EXPORT int SSL_set_verify_algorithm_prefs(SSL *ssl, const uint16_t *prefs, size_t num_prefs); -// SSL_set_hostflags calls |X509_VERIFY_PARAM_set_hostflags| on the -// |X509_VERIFY_PARAM| associated with this |SSL*|. The |flags| argument -// should be one of the |X509_CHECK_*| constants. -// -// |X509_V_FLAG_X509_STRICT| is always ON by default and -// |X509_V_FLAG_ALLOW_PROXY_CERTS| is always OFF. Both are non-configurable. -// See |x509.h| for more details. -OPENSSL_EXPORT void SSL_set_hostflags(SSL *ssl, unsigned flags); - - // Client certificate CA list. // // When requesting a client certificate, a server may advertise a list of diff --git a/include/openssl/x509.h b/include/openssl/x509.h index c47d624869..852c1dc78d 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3041,6 +3041,119 @@ OPENSSL_EXPORT int ASN1_item_sign_ctx(const ASN1_ITEM *it, X509_ALGOR *algor1, EVP_MD_CTX *ctx); +// Verification internals. +// +// The following functions expose portions of certificate validation. They are +// exported for compatibility with existing callers, or to support some obscure +// use cases. Most callers, however, will not need these functions and should +// instead use |X509_STORE_CTX| APIs. + +// X509_supported_extension returns one if |ex| is a critical X.509 certificate +// extension, supported by |X509_verify_cert|, and zero otherwise. +// +// Note this function only reports certificate extensions (as opposed to CRL or +// CRL extensions), and only extensions that are expected to be marked critical. +// Additionally, |X509_verify_cert| checks for unsupported critical extensions +// internally, so most callers will not need to call this function separately. +OPENSSL_EXPORT int X509_supported_extension(const X509_EXTENSION *ex); + +// X509_check_ca returns one if |x509| may be considered a CA certificate, +// according to basic constraints and key usage extensions. Otherwise, it +// returns zero. If |x509| is an X509v1 certificate, and thus has no extensions, +// it is considered eligible. +// +// This function returning one does not indicate that |x509| is trusted, only +// that it is eligible to be a CA. +// +// TODO(crbug.com/boringssl/407): |x509| should be const. +OPENSSL_EXPORT int X509_check_ca(X509 *x509); + +// X509_check_issued checks if |issuer| and |subject|'s name, authority key +// identifier, and key usage fields allow |issuer| to have issued |subject|. It +// returns |X509_V_OK| on success and an |X509_V_ERR_*| value otherwise. +// +// This function does not check the signature on |subject|. Rather, it is +// intended to prune the set of possible issuer certificates during +// path-building. +// +// TODO(crbug.com/boringssl/407): Both parameters should be const. +OPENSSL_EXPORT int X509_check_issued(X509 *issuer, X509 *subject); + +// NAME_CONSTRAINTS_check checks if |x509| satisfies name constraints in |nc|. +// It returns |X509_V_OK| on success and some |X509_V_ERR_*| constant on error. +// +// TODO(crbug.com/boringssl/407): Both parameters should be const. +OPENSSL_EXPORT int NAME_CONSTRAINTS_check(X509 *x509, NAME_CONSTRAINTS *nc); + +// X509_check_host checks if |x509| matches the DNS name |chk|. It returns one +// on match, zero on mismatch, or a negative number on error. |flags| should be +// some combination of |X509_CHECK_FLAG_*| and modifies the behavior. On match, +// if |out_peername| is non-NULL, it additionally sets |*out_peername| to a +// newly-allocated, NUL-terminated string containing the DNS name or wildcard in +// the certificate which matched. The caller must then free |*out_peername| with +// |OPENSSL_free| when done. +// +// By default, both subject alternative names and the subject's common name +// attribute are checked. The latter has long been deprecated, so callers should +// include |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| in |flags| to use the standard +// behavior. https://crbug.com/boringssl/464 tracks fixing the default. +// +// This function does not check if |x509| is a trusted certificate, only if, +// were it trusted, it would match |chk|. +// +// WARNING: This function differs from the usual calling convention and may +// return either 0 or a negative number on error. +// +// TODO(davidben): Make the error case also return zero. +OPENSSL_EXPORT int X509_check_host(const X509 *x509, const char *chk, + size_t chklen, unsigned int flags, + char **out_peername); + +// X509_check_email checks if |x509| matches the email address |chk|. It returns +// one on match, zero on mismatch, or a negative number on error. |flags| should +// be some combination of |X509_CHECK_FLAG_*| and modifies the behavior. +// +// By default, both subject alternative names and the subject's email address +// attribute are checked. The |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| flag may be +// used to change this behavior. +// +// This function does not check if |x509| is a trusted certificate, only if, +// were it trusted, it would match |chk|. +// +// WARNING: This function differs from the usual calling convention and may +// return either 0 or a negative number on error. +// +// TODO(davidben): Make the error case also return zero. +OPENSSL_EXPORT int X509_check_email(const X509 *x509, const char *chk, + size_t chklen, unsigned int flags); + +// X509_check_ip checks if |x509| matches the IP address |chk|. The IP address +// is represented in byte form and should be 4 bytes for an IPv4 address and 16 +// bytes for an IPv6 address. It returns one on match, zero on mismatch, or a +// negative number on error. |flags| should be some combination of +// |X509_CHECK_FLAG_*| and modifies the behavior. +// +// This function does not check if |x509| is a trusted certificate, only if, +// were it trusted, it would match |chk|. +// +// WARNING: This function differs from the usual calling convention and may +// return either 0 or a negative number on error. +// +// TODO(davidben): Make the error case also return zero. +OPENSSL_EXPORT int X509_check_ip(const X509 *x509, const uint8_t *chk, + size_t chklen, unsigned int flags); + +// X509_check_ip_asc behaves like |X509_check_ip| except the IP address is +// specified in textual form in |ipasc|. +// +// WARNING: This function differs from the usual calling convention and may +// return either 0 or a negative number on error. +// +// TODO(davidben): Make the error case also return zero. +OPENSSL_EXPORT int X509_check_ip_asc(const X509 *x509, const char *ipasc, + unsigned int flags); + + // X.509 information. // // |X509_INFO| is the return type for |PEM_X509_INFO_read_bio|, defined in @@ -4068,11 +4181,17 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_policies( // |namelen| should be set to the length of |name|. It may be zero if |name| is // NUL-terminated, but this is only maintained for backwards compatibility with // OpenSSL. +// +// By default, both subject alternative names and the subject's common name +// attribute are checked. The latter has long been deprecated, so callers should +// call |X509_VERIFY_PARAM_set_hostflags| with +// |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| to use the standard behavior. +// https://crbug.com/boringssl/464 tracks fixing the default. OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen); -// X509_VERIFY_PARAM_add1_host |name| to the list of names checked by +// X509_VERIFY_PARAM_add1_host adds |name| to the list of names checked by // |param|. If any configured DNS name matches the certificate, verification // succeeds. Any previous names set via |X509_VERIFY_PARAM_set1_host| or // |X509_VERIFY_PARAM_add1_host| are retained, no change is made if |name| is @@ -4081,6 +4200,12 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, // |namelen| should be set to the length of |name|. It may be zero if |name| is // NUL-terminated, but this is only maintained for backwards compatibility with // OpenSSL. +// +// By default, both subject alternative names and the subject's common name +// attribute are checked. The latter has long been deprecated, so callers should +// call |X509_VERIFY_PARAM_set_hostflags| with +// |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| to use the standard behavior. +// https://crbug.com/boringssl/464 tracks fixing the default. OPENSSL_EXPORT int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param, const char *name, size_t name_len); @@ -4095,6 +4220,10 @@ OPENSSL_EXPORT void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, // |emaillen| should be set to the length of |email|. It may be zero if |email| // is NUL-terminated, but this is only maintained for backwards compatibility // with OpenSSL. +// +// By default, both subject alternative names and the subject's email address +// attribute are checked. The |X509_CHECK_FLAG_NEVER_CHECK_SUBJECT| flag may be +// used to change this behavior. OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, const char *email, size_t emaillen); @@ -4371,8 +4500,6 @@ DECLARE_ASN1_FUNCTIONS(ISSUING_DIST_POINT) OPENSSL_EXPORT int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname); -OPENSSL_EXPORT int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc); - // TODO(https://crbug.com/boringssl/407): This is not const because it contains // an |X509_NAME|. DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION) @@ -4561,21 +4688,9 @@ OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, OPENSSL_EXPORT int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, int crit, unsigned long flags); -OPENSSL_EXPORT int X509_check_ca(X509 *x); OPENSSL_EXPORT int X509_check_purpose(X509 *x, int id, int ca); -// X509_supported_extension returns one if |ex| is a critical X.509 certificate -// extension, supported by |X509_verify_cert|, and zero otherwise. -// -// Note this function only reports certificate extensions (as opposed to CRL or -// CRL extensions), and only extensions that are expected to be marked critical. -// Additionally, |X509_verify_cert| checks for unsupported critical extensions -// internally, so most callers will not need to call this function separately. -OPENSSL_EXPORT int X509_supported_extension(const X509_EXTENSION *ex); - OPENSSL_EXPORT int X509_PURPOSE_set(int *p, int purpose); -OPENSSL_EXPORT int X509_check_issued(X509 *issuer, X509 *subject); -OPENSSL_EXPORT int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid); OPENSSL_EXPORT int X509_PURPOSE_get_count(void); OPENSSL_EXPORT const X509_PURPOSE *X509_PURPOSE_get0(int idx); @@ -4625,34 +4740,6 @@ OPENSSL_EXPORT int X509_PURPOSE_get_id(const X509_PURPOSE *); // sub-domains. #define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 -// X509_check_host checks if |x| has a Common Name or Subject Alternate name -// that matches the |chk| string up to |chklen|. If |chklen| is 0 -// X509_check_host will calculate the length using strlen. It is encouraged to -// always pass in the length of |chk| and rely on higher level parsing to ensure -// strlen is not called on a string that does not contain a null terminator. -// If a match is found X509_check_host returns 1, if |peername| is not null -// it is updated to point to the matching name in |x|. -OPENSSL_EXPORT int X509_check_host(X509 *x, const char *chk, size_t chklen, - unsigned int flags, char **peername); - -// X509_check_email checks if the email address in |x| matches |chk| string up -// to |chklen|. If |chklen| is 0 X509_check_email will calculate the length -// using strlen. It is encouraged to always pass in the length of |chk| and rely -// on higher level parsing to ensure strlen is not called on a string that does -// not contain a null terminator. If the certificate matches X509_check_email -// returns 1. -OPENSSL_EXPORT int X509_check_email(X509 *x, const char *chk, size_t chklen, - unsigned int flags); - -// X509_check_ip checks if the IPv4 or IPv6 address in |x| matches |chk| up -// to |chklen|. X509_check_ip does not attempt to determine the length of |chk| -// if 0 is passed in for |chklen|. If the certificate matches X509_check_ip -// returns 1. -OPENSSL_EXPORT int X509_check_ip(X509 *x, const unsigned char *chk, - size_t chklen, unsigned int flags); -OPENSSL_EXPORT int X509_check_ip_asc(X509 *x, const char *ipasc, - unsigned int flags); - #if defined(__cplusplus) } // extern C From b012876f1eb17e4189a3af646fdd8564175b4297 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Dec 2023 23:07:11 -0500 Subject: [PATCH 29/42] Document and fix up name hashing functions These return 32-bit hashes, so they should return a platform-independent uint32_t. I've categorized X509_issuer_name_hash and friends under "convenience" functions. X509_NAME_hash and X509_NAME_hash_old are as yet unclassified. Since the hash function is only relevant to X509_LOOKUP_hash_dir, I'm thinking I'll put them with that, once that's organized. While I'm here, simplify the implementations of these functions. The hash operation itself can be made infallible and allocation-free easily. However the function itself is still fallible (and non-const, and not thread-safe) due to the cached encoding mess. X509Test.NameHash captures existing hash values, so we'd notice if this changed the output. Update-Note: This is source-compatible for C/C++, including with -Wconversion, but some bindings need a patch in cl/588632028 to be compatible. Bug: 426 Change-Id: I9bfd3f1093ab15c44d8cb2d81d53aeb3d6e49fc9 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64647 Commit-Queue: David Benjamin Reviewed-by: Bob Beck (cherry picked from commit c77d0f8c776c89195d4095d51f2eaebb621887a6) --- crypto/x509/by_dir.c | 10 ++++--- crypto/x509/internal.h | 1 - crypto/x509/x509_cmp.c | 57 ++++++++++++++++------------------------ crypto/x509/x509_test.cc | 4 +-- include/openssl/x509.h | 40 ++++++++++++++++++++++------ 5 files changed, 62 insertions(+), 50 deletions(-) diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c index 3807f1f80b..28bdc70271 100644 --- a/crypto/x509/by_dir.c +++ b/crypto/x509/by_dir.c @@ -54,6 +54,7 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include #include #include #include @@ -68,7 +69,7 @@ #include "internal.h" typedef struct lookup_dir_hashes_st { - unsigned long hash; + uint32_t hash; int suffix; } BY_DIR_HASH; @@ -246,8 +247,8 @@ static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, int ok = 0; size_t i; int j, k; - unsigned long h; - unsigned long hash_array[2]; + uint32_t h; + uint32_t hash_array[2]; int hash_index; BUF_MEM *b = NULL; X509_OBJECT stmp, *tmp; @@ -309,7 +310,8 @@ static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name, hent = NULL; } for (;;) { - snprintf(b->data, b->max, "%s/%08lx.%s%d", ent->dir, h, postfix, k); + snprintf(b->data, b->max, "%s/%08" PRIx32 ".%s%d", ent->dir, h, postfix, + k); #ifndef OPENSSL_NO_POSIX_IO #if defined(_WIN32) && !defined(stat) #define stat _stat diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index cc963005cf..7e3cb21faf 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -96,7 +96,6 @@ struct X509_name_st { STACK_OF(X509_NAME_ENTRY) *entries; int modified; // true if 'bytes' needs to be built BUF_MEM *bytes; - // unsigned long hash; Keep the hash around for lookups unsigned char *canon_enc; int canon_enclen; } /* X509_NAME */; diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c index d598d6f98b..ac24a13d11 100644 --- a/crypto/x509/x509_cmp.c +++ b/crypto/x509/x509_cmp.c @@ -60,7 +60,9 @@ #include #include #include +#include #include +#include #include #include @@ -88,11 +90,11 @@ X509_NAME *X509_get_issuer_name(const X509 *a) { return a->cert_info->issuer; } -unsigned long X509_issuer_name_hash(X509 *x) { - return (X509_NAME_hash(x->cert_info->issuer)); +uint32_t X509_issuer_name_hash(X509 *x) { + return X509_NAME_hash(x->cert_info->issuer); } -unsigned long X509_issuer_name_hash_old(X509 *x) { +uint32_t X509_issuer_name_hash_old(X509 *x) { return (X509_NAME_hash_old(x->cert_info->issuer)); } @@ -108,12 +110,12 @@ const ASN1_INTEGER *X509_get0_serialNumber(const X509 *x509) { return x509->cert_info->serialNumber; } -unsigned long X509_subject_name_hash(X509 *x) { - return (X509_NAME_hash(x->cert_info->subject)); +uint32_t X509_subject_name_hash(X509 *x) { + return X509_NAME_hash(x->cert_info->subject); } -unsigned long X509_subject_name_hash_old(X509 *x) { - return (X509_NAME_hash_old(x->cert_info->subject)); +uint32_t X509_subject_name_hash_old(X509 *x) { + return X509_NAME_hash_old(x->cert_info->subject); } // Compare two certificates: they must be identical for this to work. NB: @@ -165,44 +167,29 @@ int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b) { return OPENSSL_memcmp(a->canon_enc, b->canon_enc, a->canon_enclen); } -unsigned long X509_NAME_hash(X509_NAME *x) { - unsigned long ret = 0; - unsigned char md[SHA_DIGEST_LENGTH]; - - // Make sure X509_NAME structure contains valid cached encoding - i2d_X509_NAME(x, NULL); - if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, EVP_sha1(), NULL)) { +uint32_t X509_NAME_hash(X509_NAME *x) { + // Make sure the X509_NAME structure contains a valid cached encoding. + if (i2d_X509_NAME(x, NULL) < 0) { return 0; } - ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)) & - 0xffffffffL; - return ret; + uint8_t md[SHA_DIGEST_LENGTH]; + SHA1(x->canon_enc, x->canon_enclen, md); + return CRYPTO_load_u32_le(md); } // I now DER encode the name and hash it. Since I cache the DER encoding, // this is reasonably efficient. -unsigned long X509_NAME_hash_old(X509_NAME *x) { - EVP_MD_CTX md_ctx; - unsigned long ret = 0; - unsigned char md[16]; - - // Make sure X509_NAME structure contains valid cached encoding - i2d_X509_NAME(x, NULL); - EVP_MD_CTX_init(&md_ctx); - // EVP_MD_CTX_set_flags(&md_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - if (EVP_DigestInit_ex(&md_ctx, EVP_md5(), NULL) && - EVP_DigestUpdate(&md_ctx, x->bytes->data, x->bytes->length) && - EVP_DigestFinal_ex(&md_ctx, md, NULL)) { - ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)) & - 0xffffffffL; +uint32_t X509_NAME_hash_old(X509_NAME *x) { + // Make sure the X509_NAME structure contains a valid cached encoding. + if (i2d_X509_NAME(x, NULL) < 0) { + return 0; } - EVP_MD_CTX_cleanup(&md_ctx); - return ret; + uint8_t md[SHA_DIGEST_LENGTH]; + MD5((const uint8_t *)x->bytes->data, x->bytes->length, md); + return CRYPTO_load_u32_le(md); } X509 *X509_find_by_issuer_and_serial(const STACK_OF(X509) *sk, X509_NAME *name, diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 3458b92a32..7ca4e78768 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -3092,8 +3092,8 @@ TEST(X509Test, X509NameSet) { TEST(X509Test, NameHash) { struct { std::vector name_der; - unsigned long hash; - unsigned long hash_old; + uint32_t hash; + uint32_t hash_old; } kTests[] = { // SEQUENCE { // SET { diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 852c1dc78d..95f70c7f63 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -2947,6 +2947,22 @@ OPENSSL_EXPORT int X509_subject_name_cmp(const X509 *a, const X509 *b); // CRL, only the issuer fields using |X509_NAME_cmp|. OPENSSL_EXPORT int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b); +// X509_issuer_name_hash returns the hash of |x509|'s issuer name with +// |X509_NAME_hash|. +OPENSSL_EXPORT uint32_t X509_issuer_name_hash(X509 *x509); + +// X509_subject_name_hash returns the hash of |x509|'s subject name with +// |X509_NAME_hash|. +OPENSSL_EXPORT uint32_t X509_subject_name_hash(X509 *x509); + +// X509_issuer_name_hash_old returns the hash of |x509|'s issuer name with +// |X509_NAME_hash_old|. +OPENSSL_EXPORT uint32_t X509_issuer_name_hash_old(X509 *x509); + +// X509_subject_name_hash_old returns the hash of |x509|'s usjbect name with +// |X509_NAME_hash_old|. +OPENSSL_EXPORT uint32_t X509_subject_name_hash_old(X509 *x509); + // ex_data functions. // @@ -3569,16 +3585,24 @@ OPENSSL_EXPORT const char *X509_get_default_private_dir(void); OPENSSL_EXPORT int X509_TRUST_set(int *t, int trust); -OPENSSL_EXPORT unsigned long X509_issuer_name_hash(X509 *a); - -OPENSSL_EXPORT unsigned long X509_subject_name_hash(X509 *x); +OPENSSL_EXPORT int X509_cmp(const X509 *a, const X509 *b); -OPENSSL_EXPORT unsigned long X509_issuer_name_hash_old(X509 *a); -OPENSSL_EXPORT unsigned long X509_subject_name_hash_old(X509 *x); +// X509_NAME_hash returns a hash of |name|, or zero on error. This is the new +// hash used by |X509_LOOKUP_hash_dir|. +// +// TODO(https://crbug.com/boringssl/407): This should be const and thread-safe +// but currently is neither, notably if |name| was modified from its parsed +// value. +OPENSSL_EXPORT uint32_t X509_NAME_hash(X509_NAME *name); -OPENSSL_EXPORT int X509_cmp(const X509 *a, const X509 *b); -OPENSSL_EXPORT unsigned long X509_NAME_hash(X509_NAME *x); -OPENSSL_EXPORT unsigned long X509_NAME_hash_old(X509_NAME *x); +// X509_NAME_hash_old returns a hash of |name|, or zero on error. This is the +// legacy hash used by |X509_LOOKUP_hash_dir|, which is still supported for +// compatibility. +// +// TODO(https://crbug.com/boringssl/407): This should be const and thread-safe +// but currently is neither, notably if |name| was modified from its parsed +// value. +OPENSSL_EXPORT uint32_t X509_NAME_hash_old(X509_NAME *name); OPENSSL_EXPORT int X509_CRL_match(const X509_CRL *a, const X509_CRL *b); From 04aaa057e01d46dc7dd0c22abac8cfaba4ab56cf Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 12 Dec 2023 11:48:10 -0500 Subject: [PATCH 30/42] Assume the Arm assembler can handle ADR It's 2023. We shouldn't need to be counting offsets from PC anymore. Instead, let the assembler figure this out with an ADR instruction. Additionally, since it's easy, in chacha-armv4.pl, avoid depending on the exact offset between code and data. We still depend on the code and data being close enough to fit within ADR's (very tight) bounds however. (E.g. an ADR of K256 inside sha256_block_data_order_armv8 would not work because K256 is too far away.) I have not removed the offset dependency in the SHA-2 files yet as they're a bit thorny and .Lsha256_block_data_order-K256 does not seem to work on Apple's 32-bit Arm assembler. (We probably should drop 32-bit Arm assembly on Apple platforms. It doesn't really exist anymore.) Once the armcap references are gone, that will be more straightforward. Update-Note: If 32-bit Arm assembly no longer builds, let us know and tell us what your toolchain is. Change-Id: Ie191781fed98d53c3b986b2f535132b970d79f98 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64747 Auto-Submit: David Benjamin Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 547221f5dc50e62543733cd65bd71bd97785afb6) --- crypto/chacha/asm/chacha-armv4.pl | 11 +++-------- crypto/fipsmodule/sha/asm/sha256-armv4.pl | 6 ++---- crypto/fipsmodule/sha/asm/sha512-armv4.pl | 6 ++---- generated-src/ios-arm/crypto/chacha/chacha-armv4.S | 11 +++-------- .../ios-arm/crypto/fipsmodule/sha256-armv4.S | 6 ++---- .../ios-arm/crypto/fipsmodule/sha512-armv4.S | 6 ++---- generated-src/linux-arm/crypto/chacha/chacha-armv4.S | 11 +++-------- .../linux-arm/crypto/fipsmodule/sha256-armv4.S | 6 ++---- .../linux-arm/crypto/fipsmodule/sha512-armv4.S | 6 ++---- 9 files changed, 21 insertions(+), 48 deletions(-) diff --git a/crypto/chacha/asm/chacha-armv4.pl b/crypto/chacha/asm/chacha-armv4.pl index fc08e90bb0..9899862bfc 100755 --- a/crypto/chacha/asm/chacha-armv4.pl +++ b/crypto/chacha/asm/chacha-armv4.pl @@ -201,7 +201,7 @@ sub ROUND { .long 1,0,0,0 #if __ARM_MAX_ARCH__>=7 .LOPENSSL_armcap: -.word OPENSSL_armcap_P-.LChaCha20_ctr32 +.word OPENSSL_armcap_P-.Lsigma #else .word -1 #endif @@ -213,11 +213,7 @@ sub ROUND { .LChaCha20_ctr32: ldr r12,[sp,#0] @ pull pointer to counter and nonce stmdb sp!,{r0-r2,r4-r11,lr} -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r14,pc,#16 @ ChaCha20_ctr32 -#else - adr r14,.LChaCha20_ctr32 -#endif + adr r14,.Lsigma cmp r2,#0 @ len==0? #ifdef __thumb2__ itt eq @@ -227,7 +223,7 @@ sub ROUND { #if __ARM_MAX_ARCH__>=7 cmp r2,#192 @ test len bls .Lshort - ldr r4,[r14,#-32] + ldr r4,[r14,#32] ldr r4,[r14,r4] # ifdef __APPLE__ ldr r4,[r4] @@ -238,7 +234,6 @@ sub ROUND { #endif ldmia r12,{r4-r7} @ load counter and nonce sub sp,sp,#4*(16) @ off-load area - sub r14,r14,#64 @ .Lsigma stmdb sp!,{r4-r7} @ copy counter and nonce ldmia r3,{r4-r11} @ load key ldmia r14,{r0-r3} @ load sigma diff --git a/crypto/fipsmodule/sha/asm/sha256-armv4.pl b/crypto/fipsmodule/sha/asm/sha256-armv4.pl index 4d12f4c397..ff509a6eb0 100644 --- a/crypto/fipsmodule/sha/asm/sha256-armv4.pl +++ b/crypto/fipsmodule/sha/asm/sha256-armv4.pl @@ -229,11 +229,7 @@ sub BODY_16_XX { .type sha256_block_data_order,%function sha256_block_data_order: .Lsha256_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ sha256_block_data_order -#else adr r3,.Lsha256_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,.LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -248,6 +244,8 @@ sub BODY_16_XX { add $len,$inp,$len,lsl#6 @ len to point at the end of inp stmdb sp!,{$ctx,$inp,$len,r4-r11,lr} ldmia $ctx,{$A,$B,$C,$D,$E,$F,$G,$H} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub $Ktbl,r3,#256+32 @ K256 sub sp,sp,#16*4 @ alloca(X[16]) .Loop: diff --git a/crypto/fipsmodule/sha/asm/sha512-armv4.pl b/crypto/fipsmodule/sha/asm/sha512-armv4.pl index 61d14aea26..8da171dbc5 100644 --- a/crypto/fipsmodule/sha/asm/sha512-armv4.pl +++ b/crypto/fipsmodule/sha/asm/sha512-armv4.pl @@ -290,11 +290,7 @@ () .type sha512_block_data_order,%function sha512_block_data_order: .Lsha512_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ sha512_block_data_order -#else adr r3,.Lsha512_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,.LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -306,6 +302,8 @@ () #endif add $len,$inp,$len,lsl#7 @ len to point at the end of inp stmdb sp!,{r4-r12,lr} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub $Ktbl,r3,#672 @ K512 sub sp,sp,#9*8 diff --git a/generated-src/ios-arm/crypto/chacha/chacha-armv4.S b/generated-src/ios-arm/crypto/chacha/chacha-armv4.S index bd836b60a4..9aa23e1c48 100644 --- a/generated-src/ios-arm/crypto/chacha/chacha-armv4.S +++ b/generated-src/ios-arm/crypto/chacha/chacha-armv4.S @@ -31,7 +31,7 @@ Lone: .long 1,0,0,0 #if __ARM_MAX_ARCH__>=7 LOPENSSL_armcap: -.word OPENSSL_armcap_P-LChaCha20_ctr32 +.word OPENSSL_armcap_P-Lsigma #else .word -1 #endif @@ -46,11 +46,7 @@ _ChaCha20_ctr32: LChaCha20_ctr32: ldr r12,[sp,#0] @ pull pointer to counter and nonce stmdb sp!,{r0,r1,r2,r4-r11,lr} -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r14,pc,#16 @ _ChaCha20_ctr32 -#else - adr r14,LChaCha20_ctr32 -#endif + adr r14,Lsigma cmp r2,#0 @ len==0? #ifdef __thumb2__ itt eq @@ -60,7 +56,7 @@ LChaCha20_ctr32: #if __ARM_MAX_ARCH__>=7 cmp r2,#192 @ test len bls Lshort - ldr r4,[r14,#-32] + ldr r4,[r14,#32] ldr r4,[r14,r4] # ifdef __APPLE__ ldr r4,[r4] @@ -71,7 +67,6 @@ Lshort: #endif ldmia r12,{r4,r5,r6,r7} @ load counter and nonce sub sp,sp,#4*(16) @ off-load area - sub r14,r14,#64 @ Lsigma stmdb sp!,{r4,r5,r6,r7} @ copy counter and nonce ldmia r3,{r4,r5,r6,r7,r8,r9,r10,r11} @ load key ldmia r14,{r0,r1,r2,r3} @ load sigma diff --git a/generated-src/ios-arm/crypto/fipsmodule/sha256-armv4.S b/generated-src/ios-arm/crypto/fipsmodule/sha256-armv4.S index cfe8de2d9b..31747007d3 100644 --- a/generated-src/ios-arm/crypto/fipsmodule/sha256-armv4.S +++ b/generated-src/ios-arm/crypto/fipsmodule/sha256-armv4.S @@ -103,11 +103,7 @@ LOPENSSL_armcap: #endif _sha256_block_data_order: Lsha256_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ _sha256_block_data_order -#else adr r3,Lsha256_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -122,6 +118,8 @@ Lsha256_block_data_order: add r2,r1,r2,lsl#6 @ len to point at the end of inp stmdb sp!,{r0,r1,r2,r4-r11,lr} ldmia r0,{r4,r5,r6,r7,r8,r9,r10,r11} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub r14,r3,#256+32 @ K256 sub sp,sp,#16*4 @ alloca(X[16]) Loop: diff --git a/generated-src/ios-arm/crypto/fipsmodule/sha512-armv4.S b/generated-src/ios-arm/crypto/fipsmodule/sha512-armv4.S index 2b1cd5004a..fb737619a4 100644 --- a/generated-src/ios-arm/crypto/fipsmodule/sha512-armv4.S +++ b/generated-src/ios-arm/crypto/fipsmodule/sha512-armv4.S @@ -150,11 +150,7 @@ LOPENSSL_armcap: #endif _sha512_block_data_order: Lsha512_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ _sha512_block_data_order -#else adr r3,Lsha512_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -166,6 +162,8 @@ Lsha512_block_data_order: #endif add r2,r1,r2,lsl#7 @ len to point at the end of inp stmdb sp!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub r14,r3,#672 @ K512 sub sp,sp,#9*8 diff --git a/generated-src/linux-arm/crypto/chacha/chacha-armv4.S b/generated-src/linux-arm/crypto/chacha/chacha-armv4.S index 4494c50b8e..6d3e84bc5e 100644 --- a/generated-src/linux-arm/crypto/chacha/chacha-armv4.S +++ b/generated-src/linux-arm/crypto/chacha/chacha-armv4.S @@ -31,7 +31,7 @@ .long 1,0,0,0 #if __ARM_MAX_ARCH__>=7 .LOPENSSL_armcap: -.word OPENSSL_armcap_P-.LChaCha20_ctr32 +.word OPENSSL_armcap_P-.Lsigma #else .word -1 #endif @@ -44,11 +44,7 @@ ChaCha20_ctr32: .LChaCha20_ctr32: ldr r12,[sp,#0] @ pull pointer to counter and nonce stmdb sp!,{r0,r1,r2,r4-r11,lr} -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r14,pc,#16 @ ChaCha20_ctr32 -#else - adr r14,.LChaCha20_ctr32 -#endif + adr r14,.Lsigma cmp r2,#0 @ len==0? #ifdef __thumb2__ itt eq @@ -58,7 +54,7 @@ ChaCha20_ctr32: #if __ARM_MAX_ARCH__>=7 cmp r2,#192 @ test len bls .Lshort - ldr r4,[r14,#-32] + ldr r4,[r14,#32] ldr r4,[r14,r4] # ifdef __APPLE__ ldr r4,[r4] @@ -69,7 +65,6 @@ ChaCha20_ctr32: #endif ldmia r12,{r4,r5,r6,r7} @ load counter and nonce sub sp,sp,#4*(16) @ off-load area - sub r14,r14,#64 @ .Lsigma stmdb sp!,{r4,r5,r6,r7} @ copy counter and nonce ldmia r3,{r4,r5,r6,r7,r8,r9,r10,r11} @ load key ldmia r14,{r0,r1,r2,r3} @ load sigma diff --git a/generated-src/linux-arm/crypto/fipsmodule/sha256-armv4.S b/generated-src/linux-arm/crypto/fipsmodule/sha256-armv4.S index 28ff5978b8..610a35afc0 100644 --- a/generated-src/linux-arm/crypto/fipsmodule/sha256-armv4.S +++ b/generated-src/linux-arm/crypto/fipsmodule/sha256-armv4.S @@ -101,11 +101,7 @@ K256: .type sha256_block_data_order,%function sha256_block_data_order: .Lsha256_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ sha256_block_data_order -#else adr r3,.Lsha256_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,.LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -120,6 +116,8 @@ sha256_block_data_order: add r2,r1,r2,lsl#6 @ len to point at the end of inp stmdb sp!,{r0,r1,r2,r4-r11,lr} ldmia r0,{r4,r5,r6,r7,r8,r9,r10,r11} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub r14,r3,#256+32 @ K256 sub sp,sp,#16*4 @ alloca(X[16]) .Loop: diff --git a/generated-src/linux-arm/crypto/fipsmodule/sha512-armv4.S b/generated-src/linux-arm/crypto/fipsmodule/sha512-armv4.S index 4003168827..135d23338e 100644 --- a/generated-src/linux-arm/crypto/fipsmodule/sha512-armv4.S +++ b/generated-src/linux-arm/crypto/fipsmodule/sha512-armv4.S @@ -148,11 +148,7 @@ K512: .type sha512_block_data_order,%function sha512_block_data_order: .Lsha512_block_data_order: -#if __ARM_ARCH<7 && !defined(__thumb2__) - sub r3,pc,#8 @ sha512_block_data_order -#else adr r3,.Lsha512_block_data_order -#endif #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) ldr r12,.LOPENSSL_armcap ldr r12,[r3,r12] @ OPENSSL_armcap_P @@ -164,6 +160,8 @@ sha512_block_data_order: #endif add r2,r1,r2,lsl#7 @ len to point at the end of inp stmdb sp!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + @ TODO(davidben): When the OPENSSL_armcap logic above is removed, + @ replace this with a simple ADR. sub r14,r3,#672 @ K512 sub sp,sp,#9*8 From ec99b829dc2093e29ac6363d80e8f8e944ff112d Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 12 Dec 2023 13:58:22 -0500 Subject: [PATCH 31/42] Disable 32-bit Arm assembly optimizations on iOS The last iOS version that supported 32-bit was iOS 10, which I don't believe any of our consumers support anymore. (Chromium does not, neither does google/oss-policies-info.) Our iOS CI coverage comes from Chromium, so this means we don't actually have any test coverage for 32-bit iOS, only compile coverage. In addition to lacking any test coverage, 32-bit Arm assembly is more platform-dependent than one might expect, between different limitiations on patterns for PC-relative loads and lots of assembler quirks around what kinds of label expressions (which show up in PC-relative loads a lot) are allowed. Finally, since iOS in that era did not do runtime detection of features and relied on compiling a binary multiple times, the 32-bit assembly would never enable AES acceleration anyway, so it's not as impactful as on other platforms. Between all that, it's no longer worth enabling this. Disable it in target.h which, with the all the recent build simplifications, should be sufficient to disable this code. Update-Note: iOS on 32-bit Arm now disables assembly. This is unlikely to impact anyone. As far as I can tell, 32-bit Arm for iOS thoroughly does not exist anymore. Change-Id: If31208b42047377ad1b4fb0af6fee17334f18330 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64748 Auto-Submit: David Benjamin Reviewed-by: Bob Beck Commit-Queue: Bob Beck (cherry picked from commit fcec1397a411cbe4e27bd1428dad63adf207dc33) --- include/openssl/target.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/openssl/target.h b/include/openssl/target.h index 8bb421cb48..feb0bdfd4c 100644 --- a/include/openssl/target.h +++ b/include/openssl/target.h @@ -212,6 +212,12 @@ #endif #endif +// Disable 32-bit Arm assembly on Apple platforms. The last iOS version that +// supported 32-bit Arm was iOS 10. +#if defined(OPENSSL_APPLE) && defined(OPENSSL_ARM) +#define OPENSSL_ASM_INCOMPATIBLE +#endif + #if defined(OPENSSL_ASM_INCOMPATIBLE) #undef OPENSSL_ASM_INCOMPATIBLE #if !defined(OPENSSL_NO_ASM) From 7682ed1d26a60b90b243a1070afcc0439ffc9c14 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 13 Dec 2023 10:33:17 -0500 Subject: [PATCH 32/42] Fix X509_ATTRIBUTE_set1_data with negative attributes One of the WARNINGs in this function is unambiguously a bug. Just fix it. In doing so, add a helper for the ASN1_STRING -> ASN1_TYPE conversion and make this function easier to follow. Also document the attrtype == 0 case. I missed that one when enumerating this overcomplicated calling convention. Move that check earlier so we don't try to process the input. It's ignored anyway. Change-Id: Ia6e2dcb7c69488b6e2e58a68c3b701040ed3d4ef Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64827 Reviewed-by: Bob Beck Auto-Submit: David Benjamin Commit-Queue: David Benjamin (cherry picked from commit 23d58423169a2d473a6028228e46f68d1e65f503) --- crypto/asn1/a_strex.c | 20 ++------- crypto/asn1/a_type.c | 20 ++++++++- crypto/asn1/internal.h | 4 ++ crypto/x509/x509_att.c | 67 +++++++++++++++-------------- crypto/x509/x509_test.cc | 91 ++++++++++++++++++++++++++-------------- include/openssl/x509.h | 20 ++++----- 6 files changed, 132 insertions(+), 90 deletions(-) diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c index 7bcbdfb29d..06af54d566 100644 --- a/crypto/asn1/a_strex.c +++ b/crypto/asn1/a_strex.c @@ -67,6 +67,8 @@ #include #include +#include "../bytestring/internal.h" +#include "../internal.h" #include "internal.h" @@ -256,22 +258,8 @@ static int do_dump(unsigned long flags, BIO *out, const ASN1_STRING *str) { // Placing the ASN1_STRING in a temporary ASN1_TYPE allows the DER encoding // to readily obtained. ASN1_TYPE t; - t.type = str->type; - // Negative INTEGER and ENUMERATED values are the only case where - // |ASN1_STRING| and |ASN1_TYPE| types do not match. - // - // TODO(davidben): There are also some type fields which, in |ASN1_TYPE|, do - // not correspond to |ASN1_STRING|. It is unclear whether those are allowed - // in |ASN1_STRING| at all, or what the space of allowed types is. - // |ASN1_item_ex_d2i| will never produce such a value so, for now, we say - // this is an invalid input. But this corner of the library in general - // should be more robust. - if (t.type == V_ASN1_NEG_INTEGER) { - t.type = V_ASN1_INTEGER; - } else if (t.type == V_ASN1_NEG_ENUMERATED) { - t.type = V_ASN1_ENUMERATED; - } - t.value.asn1_string = (ASN1_STRING *)str; + OPENSSL_memset(&t, 0, sizeof(ASN1_TYPE)); + asn1_type_set0_string(&t, (ASN1_STRING *)str); unsigned char *der_buf = NULL; int der_len = i2d_ASN1_TYPE(&t, &der_buf); if (der_len < 0) { diff --git a/crypto/asn1/a_type.c b/crypto/asn1/a_type.c index 0c2fd136d3..af603a3e8e 100644 --- a/crypto/asn1/a_type.c +++ b/crypto/asn1/a_type.c @@ -56,7 +56,8 @@ #include -#include +#include + #include #include #include @@ -89,6 +90,23 @@ const void *asn1_type_value_as_pointer(const ASN1_TYPE *a) { } } +void asn1_type_set0_string(ASN1_TYPE *a, ASN1_STRING *str) { + // |ASN1_STRING| types are almost the same as |ASN1_TYPE| types, except that + // the negative flag is not reflected into |ASN1_TYPE|. + int type = str->type; + if (type == V_ASN1_NEG_INTEGER) { + type = V_ASN1_INTEGER; + } else if (type == V_ASN1_NEG_ENUMERATED) { + type = V_ASN1_ENUMERATED; + } + + // These types are not |ASN1_STRING| types and use a different + // representation when stored in |ASN1_TYPE|. + assert(type != V_ASN1_NULL && type != V_ASN1_OBJECT && + type != V_ASN1_BOOLEAN); + ASN1_TYPE_set(a, type, str); +} + void asn1_type_cleanup(ASN1_TYPE *a) { switch (a->type) { case V_ASN1_NULL: diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h index 56b534fd29..c05ead243a 100644 --- a/crypto/asn1/internal.h +++ b/crypto/asn1/internal.h @@ -218,6 +218,10 @@ void asn1_encoding_clear(ASN1_ENCODING *enc); // a pointer. const void *asn1_type_value_as_pointer(const ASN1_TYPE *a); +// asn1_type_set0_string sets |a|'s value to the object represented by |str| and +// takes ownership of |str|. +void asn1_type_set0_string(ASN1_TYPE *a, ASN1_STRING *str); + // asn1_type_cleanup releases memory associated with |a|'s value, without // freeing |a| itself. void asn1_type_cleanup(ASN1_TYPE *a); diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c index 062168eaf7..e3d0c6a595 100644 --- a/crypto/x509/x509_att.c +++ b/crypto/x509/x509_att.c @@ -137,54 +137,57 @@ int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, const ASN1_OBJECT *obj) { int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, const void *data, int len) { - ASN1_TYPE *ttmp = NULL; - ASN1_STRING *stmp = NULL; - int atype = 0; if (!attr) { return 0; } + + if (attrtype == 0) { + // Do nothing. This is used to create an empty value set in + // |X509_ATTRIBUTE_create_by_*|. This is invalid, but supported by OpenSSL. + return 1; + } + + ASN1_TYPE *typ = ASN1_TYPE_new(); + if (typ == NULL) { + return 0; + } + + // This function is several functions in one. if (attrtype & MBSTRING_FLAG) { - stmp = ASN1_STRING_set_by_NID(NULL, data, len, attrtype, - OBJ_obj2nid(attr->object)); - if (!stmp) { + // |data| is an encoded string. We must decode and re-encode it to |attr|'s + // preferred ASN.1 type. Note |len| may be -1, in which case + // |ASN1_STRING_set_by_NID| calls |strlen| automatically. + ASN1_STRING *str = ASN1_STRING_set_by_NID(NULL, data, len, attrtype, + OBJ_obj2nid(attr->object)); + if (str == NULL) { OPENSSL_PUT_ERROR(X509, ERR_R_ASN1_LIB); - return 0; - } - atype = stmp->type; - } else if (len != -1) { - if (!(stmp = ASN1_STRING_type_new(attrtype))) { goto err; } - if (!ASN1_STRING_set(stmp, data, len)) { + asn1_type_set0_string(typ, str); + } else if (len != -1) { + // |attrtype| must be a valid |ASN1_STRING| type. |data| and |len| is a + // value in the corresponding |ASN1_STRING| representation. + ASN1_STRING *str = ASN1_STRING_type_new(attrtype); + if (str == NULL || !ASN1_STRING_set(str, data, len)) { + ASN1_STRING_free(str); goto err; } - atype = attrtype; - } - // This is a bit naughty because the attribute should really have at - // least one value but some types use and zero length SET and require - // this. - if (attrtype == 0) { - ASN1_STRING_free(stmp); - return 1; - } - if (!(ttmp = ASN1_TYPE_new())) { - goto err; - } - if ((len == -1) && !(attrtype & MBSTRING_FLAG)) { - if (!ASN1_TYPE_set1(ttmp, attrtype, data)) { + asn1_type_set0_string(typ, str); + } else { + // |attrtype| must be a valid |ASN1_TYPE| type. |data| is a pointer to an + // object of the corresponding type. + if (!ASN1_TYPE_set1(typ, attrtype, data)) { goto err; } - } else { - ASN1_TYPE_set(ttmp, atype, stmp); - stmp = NULL; } - if (!sk_ASN1_TYPE_push(attr->set, ttmp)) { + + if (!sk_ASN1_TYPE_push(attr->set, typ)) { goto err; } return 1; + err: - ASN1_TYPE_free(ttmp); - ASN1_STRING_free(stmp); + ASN1_TYPE_free(typ); return 0; } diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 7ca4e78768..3c2b63e78c 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -4342,46 +4342,62 @@ TEST(X509Test, X509AlgorExtract) { // Test the various |X509_ATTRIBUTE| creation functions. TEST(X509Test, Attribute) { - // The friendlyName attribute has a BMPString value. See RFC 2985, - // section 5.5.1. + // The expected attribute values are: + // 1. BMPString U+2603 + // 2. BMPString "test" + // 3. INTEGER -1 (not valid for friendlyName) static const uint8_t kTest1[] = {0x26, 0x03}; // U+2603 SNOWMAN static const uint8_t kTest1UTF8[] = {0xe2, 0x98, 0x83}; static const uint8_t kTest2[] = {0, 't', 0, 'e', 0, 's', 0, 't'}; - auto check_attribute = [&](X509_ATTRIBUTE *attr, bool has_test2) { + constexpr uint32_t kTest1Mask = 1 << 0; + constexpr uint32_t kTest2Mask = 1 << 1; + constexpr uint32_t kTest3Mask = 1 << 2; + auto check_attribute = [&](X509_ATTRIBUTE *attr, uint32_t mask) { EXPECT_EQ(NID_friendlyName, OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr))); - EXPECT_EQ(has_test2 ? 2 : 1, X509_ATTRIBUTE_count(attr)); - - // The first attribute should contain |kTest1|. - const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, 0); - ASSERT_TRUE(value); - EXPECT_EQ(V_ASN1_BMPSTRING, value->type); - EXPECT_EQ(Bytes(kTest1), - Bytes(ASN1_STRING_get0_data(value->value.bmpstring), - ASN1_STRING_length(value->value.bmpstring))); - - // |X509_ATTRIBUTE_get0_data| requires the type match. - EXPECT_FALSE( - X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, nullptr)); - const ASN1_BMPSTRING *bmpstring = static_cast( - X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_BMPSTRING, nullptr)); - ASSERT_TRUE(bmpstring); - EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(bmpstring), - ASN1_STRING_length(bmpstring))); - - if (has_test2) { - value = X509_ATTRIBUTE_get0_type(attr, 1); + int idx = 0; + if (mask & kTest1Mask) { + // The first attribute should contain |kTest1|. + const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); + ASSERT_TRUE(value); + EXPECT_EQ(V_ASN1_BMPSTRING, value->type); + EXPECT_EQ(Bytes(kTest1), + Bytes(ASN1_STRING_get0_data(value->value.bmpstring), + ASN1_STRING_length(value->value.bmpstring))); + + // |X509_ATTRIBUTE_get0_data| requires the type match. + EXPECT_FALSE( + X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_OCTET_STRING, nullptr)); + const ASN1_BMPSTRING *bmpstring = static_cast( + X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_BMPSTRING, nullptr)); + ASSERT_TRUE(bmpstring); + EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(bmpstring), + ASN1_STRING_length(bmpstring))); + idx++; + } + + if (mask & kTest2Mask) { + const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_BMPSTRING, value->type); EXPECT_EQ(Bytes(kTest2), Bytes(ASN1_STRING_get0_data(value->value.bmpstring), ASN1_STRING_length(value->value.bmpstring))); - } else { - EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, 1)); + idx++; } - EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, 2)); + if (mask & kTest3Mask) { + const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); + ASSERT_TRUE(value); + EXPECT_EQ(V_ASN1_INTEGER, value->type); + int64_t v; + ASSERT_TRUE(ASN1_INTEGER_get_int64(&v, value->value.integer)); + EXPECT_EQ(v, -1); + idx++; + } + + EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, idx)); }; bssl::UniquePtr str(ASN1_STRING_type_new(V_ASN1_BMPSTRING)); @@ -4393,7 +4409,7 @@ TEST(X509Test, Attribute) { X509_ATTRIBUTE_create(NID_friendlyName, V_ASN1_BMPSTRING, str.get())); ASSERT_TRUE(attr); str.release(); // |X509_ATTRIBUTE_create| takes ownership on success. - check_attribute(attr.get(), /*has_test2=*/false); + check_attribute(attr.get(), kTest1Mask); // Test the |MBSTRING_*| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); @@ -4402,12 +4418,19 @@ TEST(X509Test, Attribute) { X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName))); ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), MBSTRING_UTF8, kTest1UTF8, sizeof(kTest1UTF8))); - check_attribute(attr.get(), /*has_test2=*/false); + check_attribute(attr.get(), kTest1Mask); // Test the |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data|. ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, kTest2, sizeof(kTest2))); - check_attribute(attr.get(), /*has_test2=*/true); + check_attribute(attr.get(), kTest1Mask | kTest2Mask); + + // The |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data| should correctly + // handle negative integers. + const uint8_t kOne = 1; + ASSERT_TRUE( + X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_NEG_INTEGER, &kOne, 1)); + check_attribute(attr.get(), kTest1Mask | kTest2Mask | kTest3Mask); // Test the |ASN1_TYPE| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); @@ -4419,7 +4442,13 @@ TEST(X509Test, Attribute) { ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1))); ASSERT_TRUE( X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, str.get(), -1)); - check_attribute(attr.get(), /*has_test2=*/false); + check_attribute(attr.get(), kTest1Mask); + + // An |attrtype| of zero leaves the attribute empty. + attr.reset(X509_ATTRIBUTE_create_by_NID( + nullptr, NID_friendlyName, /*attrtype=*/0, /*data=*/nullptr, /*len=*/0)); + ASSERT_TRUE(attr); + check_attribute(attr.get(), 0); } // Test that, by default, |X509_V_FLAG_TRUSTED_FIRST| is set, which means we'll diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 95f70c7f63..14acf3012e 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -2182,21 +2182,21 @@ OPENSSL_EXPORT int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, // X509_ATTRIBUTE_set1_data appends a value to |attr|'s value set and returns // one on success or zero on error. The value is determined as follows: // -// If |attrtype| is a |MBSTRING_*| constant, the value is an ASN.1 string. The -// string is determined by decoding |len| bytes from |data| in the encoding -// specified by |attrtype|, and then re-encoding it in a form appropriate for -// |attr|'s type. If |len| is -1, |strlen(data)| is used instead. See -// |ASN1_STRING_set_by_NID| for details. +// If |attrtype| is zero, this function returns one and does nothing. This form +// may be used when calling |X509_ATTRIBUTE_create_by_*| to create an attribute +// with an empty value set. Such attributes are invalid, but OpenSSL supports +// creating them. +// +// Otherwise, if |attrtype| is a |MBSTRING_*| constant, the value is an ASN.1 +// string. The string is determined by decoding |len| bytes from |data| in the +// encoding specified by |attrtype|, and then re-encoding it in a form +// appropriate for |attr|'s type. If |len| is -1, |strlen(data)| is used +// instead. See |ASN1_STRING_set_by_NID| for details. // // Otherwise, if |len| is not -1, the value is an ASN.1 string. |attrtype| is an // |ASN1_STRING| type value and the |len| bytes from |data| are copied as the // type-specific representation of |ASN1_STRING|. See |ASN1_STRING| for details. // -// WARNING: If this form is used to construct a negative INTEGER or ENUMERATED, -// |attrtype| includes the |V_ASN1_NEG| flag for |ASN1_STRING|, but the function -// forgets to clear the flag for |ASN1_TYPE|. This matches OpenSSL but is -// probably a bug. For now, do not use this form with negative values. -// // Otherwise, if |len| is -1, the value is constructed by passing |attrtype| and // |data| to |ASN1_TYPE_set1|. That is, |attrtype| is an |ASN1_TYPE| type value, // and |data| is cast to the corresponding pointer type. From 6635445c8171d27a5697fdb854815265e5b46dd8 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 11 Dec 2023 01:18:00 -0500 Subject: [PATCH 33/42] Change certificate depth limit to match OpenSSL and document OpenSSL 1.1.0 included d9b8b89bec4480de3a10bdaf9425db371c19145b, which a cleanup change to X509_verify_cert. This cleanup changed the semanitcs of the depth limit. Previously, the depth limit omitted the leaf but included the trust anchor. Now it omits both. We forked a little before 1.0.2, so we still had the old behavior. Now that the new behavior is well-established, switch to new one. Bump BORINGSSL_API_VERSION so callers can detect one or the other as needed. Document the new semantics. Also fix up some older docs where I implied -1 was unlimited depth. Negative numbers were actually enforced as negative numbers (which means only explicitly-trusted self-signed certs worked). Update-Note: The new semantics increase the limit by 1 compared to the old ones. Thus this change should only accept more chains than previously and be relatively safe. It also makes us more OpenSSL-compatible. Envoy will need a tweak because they unit test the boundary condition for the depth limit. Bug: 426 Fixed: 459 Change-Id: Ifaa108b8135ea3d875f2ac1f2a3b2cd8a22aa323 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64707 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit b251d813ec615e7ef01d82073f94960eb13b1e0a) --- crypto/x509/internal.h | 2 +- crypto/x509/x509_test.cc | 61 +++++++++++++++++++++++++++++++--------- crypto/x509/x509_vfy.c | 30 +++++++++++--------- include/openssl/base.h | 2 +- include/openssl/ssl.h | 8 +++--- include/openssl/x509.h | 22 +++++++++++---- 6 files changed, 85 insertions(+), 40 deletions(-) diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 7e3cb21faf..9c7f2117e7 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -346,7 +346,7 @@ struct x509_store_ctx_st { X509_STORE_CTX_check_crl_fn check_crl; // Check CRL validity // The following is built up - int valid; // if 0, rebuild chain + int last_untrusted; // index of last untrusted cert STACK_OF(X509) *chain; // chain of X509s - built up and trusted diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 3c2b63e78c..ce9409f1db 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -12,6 +12,8 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include #include #include @@ -1614,33 +1616,37 @@ TEST(X509Test, TestVerify) { // though in different ways. for (bool trusted_first : {true, false}) { SCOPED_TRACE(trusted_first); - std::function configure_callback; - if (!trusted_first) { + bool override_depth = false; + int depth = -1; + auto configure_callback = [&](X509_VERIFY_PARAM *param) { // Note we need the callback to clear the flag. Setting |flags| to zero // only skips setting new flags. - configure_callback = [&](X509_VERIFY_PARAM *param) { + if (!trusted_first) { X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); - }; - } + } + if (override_depth) { + X509_VERIFY_PARAM_set_depth(param, depth); + } + }; // No trust anchors configured. - ASSERT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, configure_callback)); - ASSERT_EQ( + EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Each chain works individually. - ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, + EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); - ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get()}, + EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // When both roots are available, we pick one or the other. - ASSERT_EQ(X509_V_OK, + EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get(), root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); @@ -1649,7 +1655,7 @@ TEST(X509Test, TestVerify) { // the cross-sign in the intermediates. With |trusted_first|, we // preferentially stop path-building at |intermediate|. Without // |trusted_first|, the "altchains" logic repairs it. - ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, + EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); @@ -1660,7 +1666,7 @@ TEST(X509Test, TestVerify) { // This test exists to confirm our current behavior, but these modes are // just workarounds for not having an actual path-building verifier. If we // fix it, this test can be removed. - ASSERT_EQ(trusted_first ? X509_V_OK + EXPECT_EQ(trusted_first ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, @@ -1668,18 +1674,45 @@ TEST(X509Test, TestVerify) { // |forgery| is signed by |leaf_no_key_usage|, but is rejected because the // leaf is not a CA. - ASSERT_EQ(X509_V_ERR_INVALID_CA, + EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get()}, {leaf_no_key_usage.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Test that one cannot skip Basic Constraints checking with a contorted set // of roots and intermediates. This is a regression test for CVE-2015-1793. - ASSERT_EQ(X509_V_ERR_INVALID_CA, + EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get(), root_cross_signed.get()}, {leaf_no_key_usage.get(), intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); + + // Test depth limits. |configure_callback| looks at |override_depth| and + // |depth|. Negative numbers have historically worked, so test those too. + for (int d : {-4, -3, -2, -1, 0, 1, 2, 3, 4, INT_MAX - 3, INT_MAX - 2, + INT_MAX - 1, INT_MAX}) { + SCOPED_TRACE(d); + override_depth = true; + depth = d; + // A chain with a leaf, two intermediates, and a root is depth two. + EXPECT_EQ( + depth >= 2 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + Verify(leaf.get(), {cross_signing_root.get()}, + {intermediate.get(), root_cross_signed.get()}, + /*crls=*/{}, /*flags=*/0, configure_callback)); + + // A chain with a leaf, a root, and no intermediates is depth zero. + EXPECT_EQ( + depth >= 0 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + Verify(root_cross_signed.get(), {cross_signing_root.get()}, {}, + /*crls=*/{}, /*flags=*/0, configure_callback)); + + // An explicitly trusted self-signed certificate is unaffected by depth + // checks. + EXPECT_EQ(X509_V_OK, + Verify(cross_signing_root.get(), {cross_signing_root.get()}, {}, + /*crls=*/{}, /*flags=*/0, configure_callback)); + } } } diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 19698f06cf..0cf343fda7 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -55,6 +55,7 @@ * [including the GNU Public Licence.] */ #include +#include #include #include @@ -160,11 +161,11 @@ static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) { } int X509_verify_cert(X509_STORE_CTX *ctx) { - X509 *x, *xtmp, *xtmp2, *chain_ss = NULL; + X509 *xtmp, *xtmp2, *chain_ss = NULL; int bad_chain = 0; X509_VERIFY_PARAM *param = ctx->param; - int depth, i, ok = 0; - int num, j, retry, trust; + int i, ok = 0; + int j, retry, trust; STACK_OF(X509) *sktmp = NULL; if (ctx->cert == NULL) { @@ -207,17 +208,17 @@ int X509_verify_cert(X509_STORE_CTX *ctx) { goto end; } - num = (int)sk_X509_num(ctx->chain); - x = sk_X509_value(ctx->chain, num - 1); - depth = param->depth; + int num = (int)sk_X509_num(ctx->chain); + X509 *x = sk_X509_value(ctx->chain, num - 1); + // |param->depth| does not include the leaf certificate or the trust anchor, + // so the maximum size is 2 more. + int max_chain = param->depth >= INT_MAX - 2 ? INT_MAX : param->depth + 2; for (;;) { - // If we have enough, we break - if (depth < num) { - break; // FIXME: If this happens, we should take - // note of it and, if appropriate, use the - // X509_V_ERR_CERT_CHAIN_TOO_LONG error code - // later. + if (num >= max_chain) { + // FIXME: If this happens, we should take note of it and, if appropriate, + // use the X509_V_ERR_CERT_CHAIN_TOO_LONG error code later. + break; } int is_self_signed; @@ -321,8 +322,9 @@ int X509_verify_cert(X509_STORE_CTX *ctx) { } // We now lookup certs from the certificate store for (;;) { - // If we have enough, we break - if (depth < num) { + if (num >= max_chain) { + // FIXME: If this happens, we should take note of it and, if + // appropriate, use the X509_V_ERR_CERT_CHAIN_TOO_LONG error code later. break; } if (!cert_self_signed(x, &is_self_signed)) { diff --git a/include/openssl/base.h b/include/openssl/base.h index a759dfa9b7..5866918944 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -114,7 +114,7 @@ extern "C" { // A consumer may use this symbol in the preprocessor to temporarily build // against multiple revisions of BoringSSL at the same time. It is not // recommended to do so for longer than is necessary. -#define AWSLC_API_VERSION 28 +#define AWSLC_API_VERSION 29 // This string tracks the most current production release version on Github // https://github.com/aws/aws-lc/releases. diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index b4dbbd3889..364fbbd96e 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2933,13 +2933,13 @@ OPENSSL_EXPORT int SSL_set1_host(SSL *ssl, const char *hostname); OPENSSL_EXPORT void SSL_set_hostflags(SSL *ssl, unsigned flags); // SSL_CTX_set_verify_depth sets the maximum depth of a certificate chain -// accepted in verification. This number does not include the leaf, so a depth -// of 1 allows the leaf and one CA certificate. +// accepted in verification. This count excludes both the target certificate and +// the trust anchor (root certificate). OPENSSL_EXPORT void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); // SSL_set_verify_depth sets the maximum depth of a certificate chain accepted -// in verification. This number does not include the leaf, so a depth of 1 -// allows the leaf and one CA certificate. +// in verification. This count excludes both the target certificate and the +// trust anchor (root certificate). OPENSSL_EXPORT void SSL_set_verify_depth(SSL *ssl, int depth); // SSL_CTX_get_verify_depth returns the maximum depth of a certificate accepted diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 14acf3012e..bbd6f1311b 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -3671,8 +3671,14 @@ typedef int (*X509_STORE_CTX_get_crl_fn)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); typedef int (*X509_STORE_CTX_check_crl_fn)(X509_STORE_CTX *ctx, X509_CRL *crl); +// X509_STORE_set_depth configures |store| to, by default, limit certificate +// chains to |depth| intermediate certificates. This count excludes both the +// target certificate and the trust anchor (root certificate). OPENSSL_EXPORT int X509_STORE_set_depth(X509_STORE *store, int depth); +// X509_STORE_CTX_set_depth configures |ctx| to, by default, limit certificate +// chains to |depth| intermediate certificates. This count excludes both the +// target certificate and the trust anchor (root certificate). OPENSSL_EXPORT void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); #define X509_STORE_CTX_set_app_data(ctx, data) \ @@ -3922,14 +3928,12 @@ OPENSSL_EXPORT int X509_STORE_set1_param(X509_STORE *store, // // As of writing these late defaults are a depth limit (see // |X509_VERIFY_PARAM_set_depth|) and the |X509_V_FLAG_TRUSTED_FIRST| flag. This -// warning does not apply if the parameters were set in |store|. That is, -// callers may safely set a concrete depth limit in |store|, but unlimited depth -// must be configured at |X509_STORE_CTX|. +// warning does not apply if the parameters were set in |store|. // // TODO(crbug.com/boringssl/441): This behavior is very surprising. Can we -// remove this notion of late defaults? A depth limit of 100 can probably be -// applied unconditionally. |X509_V_FLAG_TRUSTED_FIRST| is mostly a workaround -// for poor path-building. +// remove this notion of late defaults? The unsettable value at |X509_STORE| is +// -1, which rejects everything but explicitly-trusted self-signed certificates. +// |X509_V_FLAG_TRUSTED_FIRST| is mostly a workaround for poor path-building. OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store); // X509_STORE_set_verify_cb acts like |X509_STORE_CTX_set_verify_cb| but sets @@ -4170,6 +4174,10 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose); OPENSSL_EXPORT int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust); + +// X509_VERIFY_PARAM_set_depth configures |param| to limit certificate chains to +// |depth| intermediate certificates. This count excludes both the target +// certificate and the trust anchor (root certificate). OPENSSL_EXPORT void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth); @@ -4270,6 +4278,8 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc); +// X509_VERIFY_PARAM_get_depth returns the maximum depth configured in |param|. +// See |X509_VERIFY_PARAM_set_depth|. OPENSSL_EXPORT int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); typedef void *(*X509V3_EXT_NEW)(void); From 96b9299b9aa853e7fd380567978bfc37c357dcb0 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 15 Dec 2023 18:28:54 -0500 Subject: [PATCH 34/42] Skip emitting empty
 blocks in documentation

We sometimes have decl-less comments interspersed in headers. Avoid the
empty patch of blue when they get rendered.

Change-Id: I6668946d07349febd6d28bbe7d4fd8d2426adca9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64927
Auto-Submit: David Benjamin 
Commit-Queue: David Benjamin 
Reviewed-by: Bob Beck 
Commit-Queue: Bob Beck 
(cherry picked from commit 56112cc01f8be29578d52c1c2af6f481c3d77bd2)
---
 util/doc.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/util/doc.go b/util/doc.go
index a76a4d0912..0449a210ab 100644
--- a/util/doc.go
+++ b/util/doc.go
@@ -649,7 +649,7 @@ func generate(outPath string, config *Config) (map[string]string, error) {
           {{range .Comment}}
             

{{. | markupPipeWords | newlinesToBR | markupFirstWord | markupRFC}}

{{end}} -
{{.Decl}}
+ {{if .Decl}}
{{.Decl}}
{{end}} {{end}} From c19cdbfef92f1f4db6e21f041635c3f980b11828 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 15 Dec 2023 18:23:43 -0500 Subject: [PATCH 35/42] Add conf.h to the documentation output We don't want anyone using it, but we probably should render all our documented headers. I've grouped it with the rest of the legacy X.509 stack. Change-Id: If01dfd19c71598f47a40cd334b0fd7ef3e00685d Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64928 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 9c821af02f2c38ac9ba58708164ccb6c4d7654bc) --- include/openssl/conf.h | 5 ++++- util/doc.config | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/openssl/conf.h b/include/openssl/conf.h index e02f76939d..2a829ae9e2 100644 --- a/include/openssl/conf.h +++ b/include/openssl/conf.h @@ -67,7 +67,9 @@ extern "C" { #endif -// Config files look like: +// Config files. +// +// This library handles OpenSSL's config files, which look like: // // # Comment // @@ -82,6 +84,7 @@ extern "C" { // untrusted input as a config file risks string injection and denial of service // vulnerabilities. + struct conf_value_st { char *section; char *name; diff --git a/util/doc.config b/util/doc.config index 68c739e20c..340130e8c1 100644 --- a/util/doc.config +++ b/util/doc.config @@ -58,6 +58,7 @@ "Name": "Legacy ASN.1 and X.509 implementation (documentation in progress)", "Headers": [ "include/openssl/asn1.h", + "include/openssl/conf.h", "include/openssl/x509.h" ] },{ From e20e087ef6cd30c658b94cdddab715a7e61385b3 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 16 Dec 2023 21:07:46 -0500 Subject: [PATCH 36/42] Restore the X509 ASN1_ITEM https://boringssl-review.googlesource.com/c/boringssl/+/63946 removed it, but sadly wpa_supplicant depends on it. Change-Id: Ib3aca5269d740457ba83ca529b797bfb4a089763 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64907 Reviewed-by: Bob Beck Auto-Submit: David Benjamin Commit-Queue: Bob Beck (cherry picked from commit faac623b09d6b14fbb06fe5150016de78b359eea) --- crypto/asn1/asn1_test.cc | 162 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index c8cea4613c..25e19d32eb 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -2851,4 +2851,166 @@ TEST(ASN1Test, OptionalChoice) { TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kTrue); } +struct EMBED_X509_ALGOR { + X509_ALGOR *simple; + X509_ALGOR *opt; + STACK_OF(X509_ALGOR) *seq; +}; + +struct EMBED_X509_NAME { + X509_NAME *simple; + X509_NAME *opt; + STACK_OF(X509_NAME) *seq; +}; + +struct EMBED_X509 { + X509 *simple; + X509 *opt; + STACK_OF(X509) *seq; +}; + +DECLARE_ASN1_FUNCTIONS(EMBED_X509_ALGOR) +ASN1_SEQUENCE(EMBED_X509_ALGOR) = { + ASN1_SIMPLE(EMBED_X509_ALGOR, simple, X509_ALGOR), + ASN1_EXP_OPT(EMBED_X509_ALGOR, opt, X509_ALGOR, 0), + ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509_ALGOR, seq, X509_ALGOR, 1), +} ASN1_SEQUENCE_END(EMBED_X509_ALGOR) +IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509_ALGOR) + +DECLARE_ASN1_FUNCTIONS(EMBED_X509_NAME) +ASN1_SEQUENCE(EMBED_X509_NAME) = { + ASN1_SIMPLE(EMBED_X509_NAME, simple, X509_NAME), + ASN1_EXP_OPT(EMBED_X509_NAME, opt, X509_NAME, 0), + ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509_NAME, seq, X509_NAME, 1), +} ASN1_SEQUENCE_END(EMBED_X509_NAME) +IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509_NAME) + +DECLARE_ASN1_FUNCTIONS(EMBED_X509) +ASN1_SEQUENCE(EMBED_X509) = { + ASN1_SIMPLE(EMBED_X509, simple, X509), + ASN1_EXP_OPT(EMBED_X509, opt, X509, 0), + ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509, seq, X509, 1), +} ASN1_SEQUENCE_END(EMBED_X509) +IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509) + +template +void TestEmbedType(bssl::Span inp, + int (*i2d)(MaybeConstT *, uint8_t **), + EmbedT *(*embed_new)(), void (*embed_free)(EmbedT *), + EmbedT *(*d2i_embed)(EmbedT **, const uint8_t **, long), + int (*i2d_embed)(EmbedT *, uint8_t **), + size_t (*sk_num)(const StackT *), + T *(*sk_value)(const StackT *, size_t)) { + std::unique_ptr obj(nullptr, embed_free); + + // Test only the first field present. + bssl::ScopedCBB cbb; + ASSERT_TRUE(CBB_init(cbb.get(), 64)); + CBB seq; + ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE)); + ASSERT_TRUE(CBB_add_bytes(&seq, inp.data(), inp.size())); + ASSERT_TRUE(CBB_flush(cbb.get())); + const uint8_t *ptr = CBB_data(cbb.get()); + obj.reset(d2i_embed(nullptr, &ptr, CBB_len(cbb.get()))); + ASSERT_TRUE(obj); + ASSERT_TRUE(obj->simple); + // Test the field was parsed correctly by reserializing it. + TestSerialize(obj->simple, i2d, inp); + EXPECT_FALSE(obj->opt); + EXPECT_FALSE(obj->seq); + TestSerialize(obj.get(), i2d_embed, + {CBB_data(cbb.get()), CBB_len(cbb.get())}); + + // Test all fields present. + cbb.Reset(); + ASSERT_TRUE(CBB_init(cbb.get(), 64)); + ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE)); + ASSERT_TRUE(CBB_add_bytes(&seq, inp.data(), inp.size())); + CBB child; + ASSERT_TRUE(CBB_add_asn1( + &seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)); + ASSERT_TRUE(CBB_add_bytes(&child, inp.data(), inp.size())); + ASSERT_TRUE(CBB_add_asn1( + &seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)); + ASSERT_TRUE(CBB_add_bytes(&child, inp.data(), inp.size())); + ASSERT_TRUE(CBB_add_bytes(&child, inp.data(), inp.size())); + ASSERT_TRUE(CBB_flush(cbb.get())); + ptr = CBB_data(cbb.get()); + obj.reset(d2i_embed(nullptr, &ptr, CBB_len(cbb.get()))); + ASSERT_TRUE(obj); + ASSERT_TRUE(obj->simple); + TestSerialize(obj->simple, i2d, inp); + ASSERT_TRUE(obj->opt); + TestSerialize(obj->opt, i2d, inp); + ASSERT_EQ(sk_num(obj->seq), 2u); + TestSerialize(sk_value(obj->seq, 0), i2d, inp); + TestSerialize(sk_value(obj->seq, 1), i2d, inp); + TestSerialize(obj.get(), i2d_embed, + {CBB_data(cbb.get()), CBB_len(cbb.get())}); +} + +// Test that X.509 types defined in this library can be embedded into other +// types, as we rewrite them away from the templating system. +TEST(ASN1Test, EmbedTypes) { + static const uint8_t kTestAlg[] = {0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x01}; + TestEmbedType(kTestAlg, i2d_X509_ALGOR, EMBED_X509_ALGOR_new, + EMBED_X509_ALGOR_free, d2i_EMBED_X509_ALGOR, + i2d_EMBED_X509_ALGOR, sk_X509_ALGOR_num, sk_X509_ALGOR_value); + + static const uint8_t kTestName[] = { + 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64}; + TestEmbedType(kTestName, i2d_X509_NAME, EMBED_X509_NAME_new, + EMBED_X509_NAME_free, d2i_EMBED_X509_NAME, i2d_EMBED_X509_NAME, + sk_X509_NAME_num, sk_X509_NAME_value); + + static const uint8_t kTestCert[] = { + 0x30, 0x82, 0x01, 0xcf, 0x30, 0x82, 0x01, 0x76, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xd9, 0x4c, 0x04, 0xda, 0x49, 0x7d, 0xbf, 0xeb, + 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x34, 0x30, 0x34, 0x32, 0x33, 0x32, 0x33, 0x32, 0x31, + 0x35, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x32, 0x33, 0x32, + 0x33, 0x32, 0x31, 0x35, 0x37, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2, + 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5, + 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, + 0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, + 0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, + 0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1, + 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, + 0x16, 0x78, 0x07, 0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78, 0x07, + 0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x0c, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x09, + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x48, 0x00, + 0x30, 0x45, 0x02, 0x21, 0x00, 0xf2, 0xa0, 0x35, 0x5e, 0x51, 0x3a, 0x36, + 0xc3, 0x82, 0x79, 0x9b, 0xee, 0x27, 0x50, 0x85, 0x8e, 0x70, 0x06, 0x74, + 0x95, 0x57, 0xd2, 0x29, 0x74, 0x00, 0xf4, 0xbe, 0x15, 0x87, 0x5d, 0xc4, + 0x07, 0x02, 0x20, 0x7c, 0x1e, 0x79, 0x14, 0x6a, 0x21, 0x83, 0xf0, 0x7a, + 0x74, 0x68, 0x79, 0x5f, 0x14, 0x99, 0x9a, 0x68, 0xb4, 0xf1, 0xcb, 0x9e, + 0x15, 0x5e, 0xe6, 0x1f, 0x32, 0x52, 0x61, 0x5e, 0x75, 0xc9, 0x14}; + TestEmbedType(kTestCert, i2d_X509, EMBED_X509_new, EMBED_X509_free, + d2i_EMBED_X509, i2d_EMBED_X509, sk_X509_num, sk_X509_value); +} + #endif // !WINDOWS || !SHARED_LIBRARY From 64c88349deb228218e0f9dc25c788c9b2f0adfef Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 16 Dec 2023 02:43:26 -0500 Subject: [PATCH 37/42] Give time.h a title and move to low-level infra group Change-Id: I4cb50bda726fcc0f22fcb53dca92cf297aeea169 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64929 Commit-Queue: David Benjamin Reviewed-by: Bob Beck (cherry picked from commit 3d5a848d2fc081872123ba3d6e2b0f653281aa13) --- include/openssl/time.h | 45 ++++++++++++++++++++++++++++++++++++++++++ util/doc.config | 3 ++- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 include/openssl/time.h diff --git a/include/openssl/time.h b/include/openssl/time.h new file mode 100644 index 0000000000..50db22d324 --- /dev/null +++ b/include/openssl/time.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2022, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef OPENSSL_HEADER_TIME_H +#define OPENSSL_HEADER_TIME_H + +#include + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + + +// Time functions. + + +// OPENSSL_posix_to_tm converts a int64_t POSIX time value in |time|, which must +// be in the range of year 0000 to 9999, to a broken out time value in |tm|. It +// returns one on success and zero on error. +OPENSSL_EXPORT int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm); + +// OPENSSL_tm_to_posix converts a time value between the years 0 and 9999 in +// |tm| to a POSIX time value in |out|. One is returned on success, zero is +// returned on failure. It is a failure if |tm| contains out of range values. +OPENSSL_EXPORT int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out); + + +#if defined(__cplusplus) +} // extern C +#endif + +#endif // OPENSSL_HEADER_TIME_H diff --git a/util/doc.config b/util/doc.config index 340130e8c1..9663eb5b07 100644 --- a/util/doc.config +++ b/util/doc.config @@ -16,7 +16,8 @@ "include/openssl/pool.h", "include/openssl/rand.h", "include/openssl/service_indicator.h", - "include/openssl/stack.h" + "include/openssl/stack.h", + "include/openssl/time.h" ] },{ "Name": "Low-level crypto primitives", From cb404c57d3c0cadbb66bd69c5355a23d4b928324 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 14 Dec 2023 14:27:27 -0500 Subject: [PATCH 38/42] Support lists and code blocks in doc.go Our documentation comments already include examples of code blocks and lists, they just don't get rendered right. We also have things that were trying to be lists but aren't. Go ahead and add support for it, and fix the handful of list-like things that didn't get rendered as lists. I took inspiration from CommonMark (https://spec.commonmark.org/0.30/) to resolve questions such as whether blank lines are needed between lists, etc., but this does not support any kind of nesting and is still far from a CommonMark parser. Aligning with CommonMark leaves the door open to pulling in a real Markdown parser if we start to need too many features. I've also borrowed the "block" terminology from CommonMark. One ambiguity of note: whether lists may interrupt paragraphs (i.e. without a blank line in between) is a little thorny. If we say no, this doesn't work: Callers should heed the following warnings: 1) Don't use the function 2) Seriously, don't use this function 3) This function is a bad idea But if we say yes, this renders wrong: This function parses an X.509 certificate (see RFC 5280) into an X509 object. We have examples of both in existing comments, though we could easily add a blank line in the former or rewrap the latter. CommonMark has a discussion on this in https://spec.commonmark.org/0.30/#lists CommonMark says yes, but with a hack that only lists starting with 1 can interrupt paragraphs. Since we're unlikely to cite RFC 1, I've matched for now, but we may want to revisit this if it gets to be a pain. I could imagine this becoming a problem: This function, on success, does some stuff and returns 1. Otherwise, it returns 0. But that looks a little weird and we usually spell out "one" and "zero". I printed all the lists we detected in existing comments, and this has not happened so far. I've also required fewer spaces than CommonMark to trigger a code block. CommonMark uses four, but four spaces plus a leading "//" and a " " is quite a lot. For now I'm not stripping the spaces after the comment marker at comment extraction time and then requiring three spaces, so two spaces relative to normal text. This is mostly to match what we've currently been doing, but we can always change it and our comments later. Change-Id: Ic61a8e93491ed96aba755aec2a5f32914bdc42ae Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64930 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit a942d572073e98944200e154597442796fdb13de) --- include/openssl/bn.h | 6 +- include/openssl/curve25519.h | 4 +- include/openssl/ec.h | 10 +- include/openssl/ssl.h | 28 ++-- util/doc.css | 11 +- util/doc.go | 283 +++++++++++++++++++++++++++-------- 6 files changed, 249 insertions(+), 93 deletions(-) diff --git a/include/openssl/bn.h b/include/openssl/bn.h index 77c2505e09..9ec83c8fa6 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -675,11 +675,11 @@ OPENSSL_EXPORT int BN_pseudo_rand_range(BIGNUM *rnd, const BIGNUM *range); // The callback receives the address of that |BN_GENCB| structure as its last // argument and the user is free to put an arbitrary pointer in |arg|. The other // arguments are set as follows: -// event=BN_GENCB_GENERATED, n=i: after generating the i'th possible prime +// - event=BN_GENCB_GENERATED, n=i: after generating the i'th possible prime // number. -// event=BN_GENCB_PRIME_TEST, n=-1: when finished trial division primality +// - event=BN_GENCB_PRIME_TEST, n=-1: when finished trial division primality // checks. -// event=BN_GENCB_PRIME_TEST, n=i: when the i'th primality test has finished. +// - event=BN_GENCB_PRIME_TEST, n=i: when the i'th primality test has finished. // // The callback can return zero to abort the generation progress or one to // allow it to continue. diff --git a/include/openssl/curve25519.h b/include/openssl/curve25519.h index e7c88fa9c9..6c79228c11 100644 --- a/include/openssl/curve25519.h +++ b/include/openssl/curve25519.h @@ -165,10 +165,10 @@ OPENSSL_EXPORT int SPAKE2_generate_msg(SPAKE2_CTX *ctx, uint8_t *out, // |*out_key_len| to the number of bytes written. // // The resulting keying material is suitable for: -// a) Using directly in a key-confirmation step: i.e. each side could +// - Using directly in a key-confirmation step: i.e. each side could // transmit a hash of their role, a channel-binding value and the key // material to prove to the other side that they know the shared key. -// b) Using as input keying material to HKDF to generate a variety of subkeys +// - Using as input keying material to HKDF to generate a variety of subkeys // for encryption etc. // // If |max_out_key_key| is smaller than the amount of key material generated diff --git a/include/openssl/ec.h b/include/openssl/ec.h index e52d3de9fc..f1b9a6b2d0 100644 --- a/include/openssl/ec.h +++ b/include/openssl/ec.h @@ -124,11 +124,11 @@ OPENSSL_EXPORT const EC_GROUP *EC_group_secp256k1(void); // calling |EC_GROUP_free| is optional. // // The supported NIDs are (see crypto/fipsmodule/ec/ec.c): -// NID_secp224r1 (NIST P-224), -// NID_X9_62_prime256v1 (NIST P-256), -// NID_secp384r1 (NIST P-384), -// NID_secp521r1 (NIST P-521), -// NID_secp256k1 (SEC/ANSI P-256 K1) +// - |NID_secp224r1| (NIST P-224) +// - |NID_X9_62_prime256v1| (NIST P-256) +// - |NID_secp384r1| (NIST P-384) +// - |NID_secp521r1| (NIST P-521) +// - |NID_secp256k1| (SEC/ANSI P-256 K1) // // Calling this function causes all four curves to be linked into the binary. // Prefer calling |EC_group_*| to allow the static linker to drop unused curves. diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 364fbbd96e..556b21c0e4 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1603,19 +1603,19 @@ OPENSSL_EXPORT size_t SSL_get_all_standard_cipher_names(const char **out, // // Available opcodes are: // -// The empty opcode enables and appends all matching disabled ciphers to the +// - The empty opcode enables and appends all matching disabled ciphers to the // end of the enabled list. The newly appended ciphers are ordered relative to // each other matching their order in the disabled list. // -// |-| disables all matching enabled ciphers and prepends them to the disabled +// - |-| disables all matching enabled ciphers and prepends them to the disabled // list, with relative order from the enabled list preserved. This means the // most recently disabled ciphers get highest preference relative to other // disabled ciphers if re-enabled. // -// |+| moves all matching enabled ciphers to the end of the enabled list, with +// - |+| moves all matching enabled ciphers to the end of the enabled list, with // relative order preserved. // -// |!| deletes all matching ciphers, enabled or not, from either list. Deleted +// - |!| deletes all matching ciphers, enabled or not, from either list. Deleted // ciphers will not matched by future operations. // // A selector may be a specific cipher (using either the standard or OpenSSL @@ -1625,38 +1625,38 @@ OPENSSL_EXPORT size_t SSL_get_all_standard_cipher_names(const char **out, // // Available cipher rules are: // -// |ALL| matches all ciphers. +// - |ALL| matches all ciphers. // -// |kRSA|, |kDHE|, |kECDHE|, and |kPSK| match ciphers using plain RSA, DHE, +// - |kRSA|, |kDHE|, |kECDHE|, and |kPSK| match ciphers using plain RSA, DHE, // ECDHE, and plain PSK key exchanges, respectively. Note that ECDHE_PSK is // matched by |kECDHE| and not |kPSK|. // -// |aRSA|, |aECDSA|, and |aPSK| match ciphers authenticated by RSA, ECDSA, and +// - |aRSA|, |aECDSA|, and |aPSK| match ciphers authenticated by RSA, ECDSA, and // a pre-shared key, respectively. // -// |RSA|, |DHE|, |ECDHE|, |PSK|, |ECDSA|, and |PSK| are aliases for the +// - |RSA|, |DHE|, |ECDHE|, |PSK|, |ECDSA|, and |PSK| are aliases for the // corresponding |k*| or |a*| cipher rule. |RSA| is an alias for |kRSA|, not // |aRSA|. // -// |3DES|, |AES128|, |AES256|, |AES|, |AESGCM|, |CHACHA20| match ciphers +// - |3DES|, |AES128|, |AES256|, |AES|, |AESGCM|, |CHACHA20| match ciphers // whose bulk cipher use the corresponding encryption scheme. Note that // |AES|, |AES128|, and |AES256| match both CBC and GCM ciphers. // -// |SHA1|, and its alias |SHA|, match legacy cipher suites using HMAC-SHA1. +// - |SHA1|, and its alias |SHA|, match legacy cipher suites using HMAC-SHA1. // // Although implemented, authentication-only ciphers match no rules and must be // explicitly selected by name. // // Deprecated cipher rules: // -// |kEDH|, |EDH|, |kEECDH|, and |EECDH| are legacy aliases for |kDHE|, |DHE|, +// - |kEDH|, |EDH|, |kEECDH|, and |EECDH| are legacy aliases for |kDHE|, |DHE|, // |kECDHE|, and |ECDHE|, respectively. // -// |HIGH| is an alias for |ALL|. +// - |HIGH| is an alias for |ALL|. // -// |FIPS| is an alias for |HIGH|. +// - |FIPS| is an alias for |HIGH|. // -// |SSLv3| and |TLSv1| match ciphers available in TLS 1.1 or earlier. +// - |SSLv3| and |TLSv1| match ciphers available in TLS 1.1 or earlier. // |TLSv1_2| matches ciphers new in TLS 1.2. This is confusing and should not // be used. // diff --git a/util/doc.css b/util/doc.css index a868e44445..f176f259b7 100644 --- a/util/doc.css +++ b/util/doc.css @@ -16,12 +16,13 @@ div.title { margin-bottom: 2em; } -ol { +ol.toc { list-style: none; + padding-left: 0; margin-bottom: 4em; } -li a { +ol.toc li a { color: black; } @@ -49,12 +50,16 @@ div.decl p:first-child .first-word { font-size: 1.5em; } -.section pre { +pre.code { background-color: #b2c9db; padding: 5px; border-radius: 5px; } +.comment pre { + margin-left: 2em; +} + td { padding: 2px; } diff --git a/util/doc.go b/util/doc.go index 0449a210ab..0d2e2628b4 100644 --- a/util/doc.go +++ b/util/doc.go @@ -18,7 +18,9 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" + "unicode" ) // Config describes the structure of the config JSON file. @@ -41,7 +43,7 @@ type HeaderFile struct { Name string // Preamble contains a comment for the file as a whole. Each string // is a separate paragraph. - Preamble []string + Preamble []CommentBlock Sections []HeaderSection // AllDecls maps all decls to their URL fragments. AllDecls map[string]string @@ -49,7 +51,7 @@ type HeaderFile struct { type HeaderSection struct { // Preamble contains a comment for a group of functions. - Preamble []string + Preamble []CommentBlock Decls []HeaderDecl // Anchor, if non-empty, is the URL fragment to use in anchor tags. Anchor string @@ -62,7 +64,7 @@ type HeaderDecl struct { // Comment contains a comment for a specific function. Each string is a // paragraph. Some paragraph may contain \n runes to indicate that they // are preformatted. - Comment []string + Comment []CommentBlock // Name contains the name of the function, if it could be extracted. Name string // Decl contains the preformatted C declaration itself. @@ -71,6 +73,20 @@ type HeaderDecl struct { Anchor string } +type CommentBlockType int + +const ( + CommentParagraph CommentBlockType = iota + CommentOrderedListItem + CommentBulletListItem + CommentCode +) + +type CommentBlock struct { + Type CommentBlockType + Paragraph string +} + const ( cppGuard = "#if defined(__cplusplus)" commentStart = "/* " @@ -95,7 +111,7 @@ func commentSubject(line string) string { return line[:idx] } -func extractComment(lines []string, lineNo int) (comment []string, rest []string, restLineNo int, err error) { +func extractCommentLines(lines []string, lineNo int) (comment []string, rest []string, restLineNo int, err error) { if len(lines) == 0 { return nil, lines, lineNo, nil } @@ -109,22 +125,19 @@ func extractComment(lines []string, lineNo int) (comment []string, rest []string } else if !strings.HasPrefix(rest[0], lineComment) { panic("extractComment called on non-comment") } - commentParagraph := rest[0][len(commentStart):] + comment = []string{rest[0][len(commentStart):]} rest = rest[1:] restLineNo++ for len(rest) > 0 { if isBlock { - i := strings.Index(commentParagraph, commentEnd) - if i >= 0 { - if i != len(commentParagraph)-len(commentEnd) { + last := &comment[len(comment)-1] + if i := strings.Index(*last, commentEnd); i >= 0 { + if i != len(*last)-len(commentEnd) { err = fmt.Errorf("garbage after comment end on line %d", restLineNo) return } - commentParagraph = commentParagraph[:i] - if len(commentParagraph) > 0 { - comment = append(comment, commentParagraph) - } + *last = (*last)[:i] return } } @@ -136,36 +149,136 @@ func extractComment(lines []string, lineNo int) (comment []string, rest []string return } } else if !strings.HasPrefix(line, "//") { - if len(commentParagraph) > 0 { - comment = append(comment, commentParagraph) - } return } - if len(line) == 2 || !isBlock || line[2] != '/' { - line = line[2:] + comment = append(comment, line[2:]) + rest = rest[1:] + restLineNo++ + } + + err = errors.New("hit EOF in comment") + return +} + +func removeBulletListMarker(line string) (string, bool) { + orig := line + line = strings.TrimSpace(line) + if !strings.HasPrefix(line, "+ ") && !strings.HasPrefix(line, "- ") && !strings.HasPrefix(line, "* ") { + return orig, false + } + return line[2:], true +} + +func removeOrderedListMarker(line string) (rest string, num int, ok bool) { + orig := line + line = strings.TrimSpace(line) + if len(line) == 0 || !unicode.IsDigit(rune(line[0])) { + return orig, -1, false + } + + l := 0 + for l < len(line) && unicode.IsDigit(rune(line[l])) { + l++ + } + num, err := strconv.Atoi(line[:l]) + if err != nil { + return orig, -1, false + } + + line = line[l:] + if line, ok := strings.CutPrefix(line, ". "); ok { + return line, num, true + } + if line, ok := strings.CutPrefix(line, ") "); ok { + return line, num, true + } + + return orig, -1, false +} + +func removeCodeIndent(line string) (string, bool) { + return strings.CutPrefix(line, " ") +} + +func extractComment(lines []string, lineNo int) (comment []CommentBlock, rest []string, restLineNo int, err error) { + commentLines, rest, restLineNo, err := extractCommentLines(lines, lineNo) + if err != nil { + return + } + + // This syntax and parsing algorithm is loosely inspired by CommonMark, + // but reduced to a small subset with no nesting. Blocks being open vs. + // closed can be tracked implicitly. We're also much slopplier about how + // indentation. Additionally, rather than grouping list items into + // lists, our parser just emits a list items, which are grouped later at + // rendering time. + // + // If we later need more features, such as nested lists, this can evolve + // into a more complex implementation. + var numBlankLines int + for _, line := range commentLines { + // Defer blank lines until we know the next element. + if len(strings.TrimSpace(line)) == 0 { + numBlankLines++ + continue } - if strings.HasPrefix(line, " ") { - /* Identing the lines of a paragraph marks them as - * preformatted. */ - if len(commentParagraph) > 0 { - commentParagraph += "\n" + + blankLinesSkipped := numBlankLines + numBlankLines = 0 + + // Attempt to continue the previous block. + if len(comment) > 0 { + last := &comment[len(comment)-1] + if last.Type == CommentCode { + l, ok := removeCodeIndent(line) + if ok { + for i := 0; i < blankLinesSkipped; i++ { + last.Paragraph += "\n" + } + last.Paragraph += l + "\n" + continue + } + } else if blankLinesSkipped == 0 { + _, isBulletList := removeBulletListMarker(line) + _, num, isOrderedList := removeOrderedListMarker(line) + if isOrderedList && last.Type == CommentParagraph && num != 1 { + // A list item can only interrupt a paragraph if the number is one. + // See the discussion in https://spec.commonmark.org/0.30/#lists. + // This avoids wrapping like "(See RFC\n5280)" turning into a list. + isOrderedList = false + } + if !isBulletList && !isOrderedList { + // This is a continuation line of the previous paragraph. + last.Paragraph += " " + strings.TrimSpace(line) + continue + } } - line = line[3:] } - if len(line) > 0 { - commentParagraph = commentParagraph + line - if len(commentParagraph) > 0 && commentParagraph[0] == ' ' { - commentParagraph = commentParagraph[1:] - } + + // Make a new block. + if line, ok := removeBulletListMarker(line); ok { + comment = append(comment, CommentBlock{ + Type: CommentBulletListItem, + Paragraph: strings.TrimSpace(line), + }) + } else if line, _, ok := removeOrderedListMarker(line); ok { + comment = append(comment, CommentBlock{ + Type: CommentOrderedListItem, + Paragraph: strings.TrimSpace(line), + }) + } else if line, ok := removeCodeIndent(line); ok { + comment = append(comment, CommentBlock{ + Type: CommentCode, + Paragraph: line + "\n", + }) } else { - comment = append(comment, commentParagraph) - commentParagraph = "" + comment = append(comment, CommentBlock{ + Type: CommentParagraph, + Paragraph: strings.TrimSpace(line), + }) } - rest = rest[1:] - restLineNo++ } - err = errors.New("hit EOF in comment") return } @@ -390,7 +503,8 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) { return nil, err } if len(rest) > 0 && len(rest[0]) == 0 { - anchor := sanitizeAnchor(firstSentence(comment)) + heading := firstSentence(comment) + anchor := sanitizeAnchor(heading) if len(anchor) > 0 { if _, ok := allAnchors[anchor]; ok { return nil, fmt.Errorf("duplicate anchor: %s", anchor) @@ -399,7 +513,7 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) { } section.Preamble = comment - section.IsPrivate = len(comment) > 0 && isPrivateSection(comment[0]) + section.IsPrivate = isPrivateSection(heading) section.Anchor = anchor lines = rest[1:] lineNo = restLineNo + 1 @@ -417,7 +531,7 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) { return nil, fmt.Errorf("hit ending C++ guard while in section on line %d (possibly missing two empty lines ahead of guard?)", lineNo) } - var comment []string + var comment []CommentBlock var decl string if isComment(line) { comment, lines, lineNo, err = extractComment(lines, lineNo) @@ -444,10 +558,11 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) { // with the name of the thing that they are // commenting on. We make an exception here for // collective comments. + sentence := firstSentence(comment) if len(comment) > 0 && len(name) > 0 && - !isCollectiveComment(comment[0]) { - subject := commentSubject(comment[0]) + !isCollectiveComment(sentence) { + subject := commentSubject(sentence) ok := subject == name if l := len(subject); l > 0 && subject[l-1] == '*' { // Groups of names, notably #defines, are often @@ -486,11 +601,11 @@ func (config *Config) parseHeader(path string) (*HeaderFile, error) { return header, nil } -func firstSentence(paragraphs []string) string { - if len(paragraphs) == 0 { +func firstSentence(comment []CommentBlock) string { + if len(comment) == 0 { return "" } - s := paragraphs[0] + s := comment[0].Paragraph i := strings.Index(s, ". ") if i >= 0 { return s[:i] @@ -501,6 +616,61 @@ func firstSentence(paragraphs []string) string { return s } +func markupComment(allDecls map[string]string, comment []CommentBlock) template.HTML { + var b strings.Builder + lastType := CommentParagraph + closeList := func() { + if lastType == CommentOrderedListItem { + b.WriteString("") + } else if lastType == CommentBulletListItem { + b.WriteString("") + } + } + + for _, block := range comment { + // Group consecutive list items of the same type into a list. + if block.Type != lastType { + closeList() + if block.Type == CommentOrderedListItem { + b.WriteString("
    ") + } else if block.Type == CommentBulletListItem { + b.WriteString("
      ") + } + } + lastType = block.Type + + switch block.Type { + case CommentParagraph: + b.WriteString("

      ") + b.WriteString(string(markupParagraph(allDecls, block.Paragraph))) + b.WriteString("

      ") + case CommentOrderedListItem, CommentBulletListItem: + b.WriteString("
    • ") + b.WriteString(string(markupParagraph(allDecls, block.Paragraph))) + b.WriteString("
    • ") + case CommentCode: + b.WriteString("
      ")
      +			b.WriteString(block.Paragraph)
      +			b.WriteString("
      ") + default: + panic(block.Type) + } + } + + closeList() + return template.HTML(b.String()) +} + +func markupParagraph(allDecls map[string]string, s string) template.HTML { + // TODO(davidben): Ideally the inline transforms would be unified into + // one pass, so that the HTML output of one pass does not interfere with + // the next. + ret := markupPipeWords(allDecls, s, true /* linkDecls */) + ret = markupFirstWord(ret) + ret = markupRFC(ret) + return ret +} + // markupPipeWords converts |s| into an HTML string, safe to be included outside // a tag, while also marking up words surrounded by |. func markupPipeWords(allDecls map[string]string, s string, linkDecls bool) template.HTML { @@ -585,27 +755,14 @@ func markupRFC(html template.HTML) template.HTML { return template.HTML(b.String()) } -func newlinesToBR(html template.HTML) template.HTML { - s := string(html) - if !strings.Contains(s, "\n") { - return html - } - s = strings.Replace(s, "\n", "
      ", -1) - s = strings.Replace(s, " ", " ", -1) - return template.HTML(s) -} - func generate(outPath string, config *Config) (map[string]string, error) { allDecls := make(map[string]string) headerTmpl := template.New("headerTmpl") headerTmpl.Funcs(template.FuncMap{ "firstSentence": firstSentence, - "markupPipeWords": func(s string) template.HTML { return markupPipeWords(allDecls, s, true /* linkDecls */) }, "markupPipeWordsNoLink": func(s string) template.HTML { return markupPipeWords(allDecls, s, false /* linkDecls */) }, - "markupFirstWord": markupFirstWord, - "markupRFC": markupRFC, - "newlinesToBR": newlinesToBR, + "markupComment": func(c []CommentBlock) template.HTML { return markupComment(allDecls, c) }, }) headerTmpl, err := headerTmpl.Parse(` @@ -622,9 +779,9 @@ func generate(outPath string, config *Config) (map[string]string, error) { All headers - {{range .Preamble}}

      {{. | markupPipeWords | markupRFC}}

      {{end}} + {{if .Preamble}}
      {{.Preamble | markupComment}}
      {{end}} -
        +
          {{range .Sections}} {{if not .IsPrivate}} {{if .Anchor}}
        1. {{.Preamble | firstSentence | markupPipeWordsNoLink}}
        2. {{end}} @@ -638,18 +795,12 @@ func generate(outPath string, config *Config) (map[string]string, error) { {{range .Sections}} {{if not .IsPrivate}}
          - {{if .Preamble}} -
          - {{range .Preamble}}

          {{. | markupPipeWords | markupRFC}}

          {{end}} -
          - {{end}} + {{if .Preamble}}
          {{.Preamble | markupComment}}
          {{end}} {{range .Decls}}
          - {{range .Comment}} -

          {{. | markupPipeWords | newlinesToBR | markupFirstWord | markupRFC}}

          - {{end}} - {{if .Decl}}
          {{.Decl}}
          {{end}} + {{if .Comment}}
          {{.Comment | markupComment}}
          {{end}} + {{if .Decl}}
          {{.Decl}}
          {{end}}
          {{end}}
          From eb56a63c0da761ff6fff83ff04ddec0c190c5734 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 16 Dec 2023 10:47:35 -0500 Subject: [PATCH 39/42] Give WARNING paragraphs a splash of color I'm not sure if this is necessary. I was playing around and this didn't look terrible. Though it will probably turn x509.h into a sea of yellow when it's ready to be rendered. Change-Id: I34b26aad8a779a3fde761558d15b64c79159892a Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64931 Reviewed-by: Bob Beck Commit-Queue: David Benjamin (cherry picked from commit 89dd8d9eb4eabb4fbe20eac977f4827065bc493b) --- util/doc.css | 10 ++++++++++ util/doc.go | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/util/doc.css b/util/doc.css index f176f259b7..05d90bec6b 100644 --- a/util/doc.css +++ b/util/doc.css @@ -56,6 +56,16 @@ pre.code { border-radius: 5px; } +p.warning { + background-color: #fef5d3; + padding: 5px; + border-radius: 5px; +} + +p.warning .first-word { + font-weight: bold; +} + .comment pre { margin-left: 2em; } diff --git a/util/doc.go b/util/doc.go index 0d2e2628b4..da4b290756 100644 --- a/util/doc.go +++ b/util/doc.go @@ -641,7 +641,11 @@ func markupComment(allDecls map[string]string, comment []CommentBlock) template. switch block.Type { case CommentParagraph: - b.WriteString("

          ") + if strings.HasPrefix(block.Paragraph, "WARNING:") { + b.WriteString("

          ") + } else { + b.WriteString("

          ") + } b.WriteString(string(markupParagraph(allDecls, block.Paragraph))) b.WriteString("

          ") case CommentOrderedListItem, CommentBulletListItem: From 0aab2946e6429987a9db1dde1005183c3bcd2e78 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 18 Dec 2023 13:33:36 -0500 Subject: [PATCH 40/42] Restore the X509_EXTENSION ASN1_ITEM too wpa_supplicant also depends on that one. Change-Id: I6abb505d076eb258e6b8d68be42e624f4aedc725 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/64947 Auto-Submit: David Benjamin Commit-Queue: Bob Beck Reviewed-by: Bob Beck (cherry picked from commit 538b2a6cf0497cf8bb61ae726a484a3d7a34e54e) --- crypto/asn1/asn1_test.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index 25e19d32eb..d738e59071 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -2857,6 +2857,12 @@ struct EMBED_X509_ALGOR { STACK_OF(X509_ALGOR) *seq; }; +struct EMBED_X509_EXTENSION { + X509_EXTENSION *simple; + X509_EXTENSION *opt; + STACK_OF(X509_EXTENSION) *seq; +}; + struct EMBED_X509_NAME { X509_NAME *simple; X509_NAME *opt; @@ -2885,6 +2891,14 @@ ASN1_SEQUENCE(EMBED_X509_NAME) = { } ASN1_SEQUENCE_END(EMBED_X509_NAME) IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509_NAME) +DECLARE_ASN1_FUNCTIONS(EMBED_X509_EXTENSION) +ASN1_SEQUENCE(EMBED_X509_EXTENSION) = { + ASN1_SIMPLE(EMBED_X509_EXTENSION, simple, X509_EXTENSION), + ASN1_EXP_OPT(EMBED_X509_EXTENSION, opt, X509_EXTENSION, 0), + ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509_EXTENSION, seq, X509_EXTENSION, 1), +} ASN1_SEQUENCE_END(EMBED_X509_EXTENSION) +IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509_EXTENSION) + DECLARE_ASN1_FUNCTIONS(EMBED_X509) ASN1_SEQUENCE(EMBED_X509) = { ASN1_SIMPLE(EMBED_X509, simple, X509), @@ -2969,6 +2983,14 @@ TEST(ASN1Test, EmbedTypes) { EMBED_X509_NAME_free, d2i_EMBED_X509_NAME, i2d_EMBED_X509_NAME, sk_X509_NAME_num, sk_X509_NAME_value); + static const uint8_t kTestExtension[] = {0x30, 0x0c, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x04, 0x05, 0x30, + 0x03, 0x01, 0x01, 0xf}; + TestEmbedType(kTestExtension, i2d_X509_EXTENSION, EMBED_X509_EXTENSION_new, + EMBED_X509_EXTENSION_free, d2i_EMBED_X509_EXTENSION, + i2d_EMBED_X509_EXTENSION, sk_X509_EXTENSION_num, + sk_X509_EXTENSION_value); + static const uint8_t kTestCert[] = { 0x30, 0x82, 0x01, 0xcf, 0x30, 0x82, 0x01, 0x76, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xd9, 0x4c, 0x04, 0xda, 0x49, 0x7d, 0xbf, 0xeb, From 6b85f2f251923ba18127cb15a1a268131b2cba74 Mon Sep 17 00:00:00 2001 From: Andrew Hopkins Date: Tue, 4 Jun 2024 09:14:57 -0700 Subject: [PATCH 41/42] Add libevent to GitHub integration CI (#1615) ### Issues: Resolves CryptoAlg-2465 ### Description of changes: Libevent already works with AWS-LC thanks to other compatibility work. This adds a test to make sure it keeps working. ### Testing: Tested the script locally but need to see what happens with GitHub actions. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. --------- Co-authored-by: Justin W Smith <103147162+justsmth@users.noreply.github.com> --- .github/workflows/integrations.yml | 13 +++++++ .../integration/run_libevent_integration.sh | 38 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100755 tests/ci/integration/run_libevent_integration.sh diff --git a/.github/workflows/integrations.yml b/.github/workflows/integrations.yml index 1d6fd570d5..ab71319f95 100644 --- a/.github/workflows/integrations.yml +++ b/.github/workflows/integrations.yml @@ -148,3 +148,16 @@ jobs: - name: Run strongswan build run: | ./tests/ci/integration/run_strongswan_integration.sh + libevent: + if: github.repository_owner == 'aws' + runs-on: ubuntu-latest + steps: + - name: Install OS Dependencies + run: | + sudo apt-get update + sudo apt-get -y --no-install-recommends install \ + cmake gcc ninja-build golang + - uses: actions/checkout@v4 + - name: Run libevent build + run: | + ./tests/ci/integration/run_libevent_integration.sh diff --git a/tests/ci/integration/run_libevent_integration.sh b/tests/ci/integration/run_libevent_integration.sh new file mode 100755 index 0000000000..ccba997632 --- /dev/null +++ b/tests/ci/integration/run_libevent_integration.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +set -exu + +source tests/ci/common_posix_setup.sh + +# Assumes script is executed from the root of aws-lc directory +SCRATCH_FOLDER=${SRC_ROOT}/"scratch" +AWS_LC_BUILD_FOLDER="${SCRATCH_FOLDER}/aws-lc-build" +AWS_LC_INSTALL_FOLDER="${SCRATCH_FOLDER}/aws-lc-install" +LIBEVENT_SRC="${SCRATCH_FOLDER}/libevent" +export LD_LIBRARY_PATH="${AWS_LC_INSTALL_FOLDER}/lib" + +function build_and_test_libevent() { + pushd "${LIBEVENT_SRC}" + mkdir build && pushd build + cmake -GNinja -DOPENSSL_ROOT_DIR="${AWS_LC_INSTALL_FOLDER}" ../ + ninja verify + popd && popd +} + +# Make script execution idempotent. +mkdir -p "${SCRATCH_FOLDER}" +rm -rf "${SCRATCH_FOLDER:?}"/* +pushd "${SCRATCH_FOLDER}" + +mkdir -p "${AWS_LC_BUILD_FOLDER}" "${AWS_LC_INSTALL_FOLDER}" +git clone --depth 1 https://github.com/libevent/libevent.git + +# Test with shared AWS-LC libraries +aws_lc_build "$SRC_ROOT" "$AWS_LC_BUILD_FOLDER" "$AWS_LC_INSTALL_FOLDER" -DBUILD_TESTING=OFF -DBUILD_TOOL=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=1 +export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:${AWS_LC_INSTALL_FOLDER}/lib/" +build_and_test_libevent + +ldd "${LIBEVENT_SRC}/build/lib/libevent_openssl.so" | grep "${AWS_LC_INSTALL_FOLDER}/lib/libcrypto.so" || exit 1 +popd From c7759e4947ff2b5233a3908564ced9a2a6f1443b Mon Sep 17 00:00:00 2001 From: ecdeye Date: Tue, 4 Jun 2024 09:15:40 -0700 Subject: [PATCH 42/42] Add support for ocsp get id (#1609) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license. Description: Added "OCSP_onereq_get0_id," which is an OCSP function available in OpenSSL, but missing in AWS-LC --------- Co-authored-by: Justin W Smith <103147162+justsmth@users.noreply.github.com> --- crypto/ocsp/ocsp_server.c | 7 +++++++ crypto/ocsp/ocsp_test.cc | 19 +++++++++++++++++++ include/openssl/ocsp.h | 4 ++++ 3 files changed, 30 insertions(+) diff --git a/crypto/ocsp/ocsp_server.c b/crypto/ocsp/ocsp_server.c index 925ebe9f5a..31afb710da 100644 --- a/crypto/ocsp/ocsp_server.c +++ b/crypto/ocsp/ocsp_server.c @@ -34,6 +34,13 @@ int OCSP_id_get0_info(ASN1_OCTET_STRING **nameHash, ASN1_OBJECT **algor, return 1; } +OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *one) { + if(one == NULL) { + return NULL; + } + return one->reqCert; +} + int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert) { if (resp->certs == NULL && (resp->certs = sk_X509_new_null()) == NULL) { return 0; diff --git a/crypto/ocsp/ocsp_test.cc b/crypto/ocsp/ocsp_test.cc index cade86be56..cf591d4b65 100644 --- a/crypto/ocsp/ocsp_test.cc +++ b/crypto/ocsp/ocsp_test.cc @@ -1581,3 +1581,22 @@ TEST(OCSPTest, OCSPRequestPrint) { EXPECT_EQ(line, expected); } } + +TEST(OCSPTest, OCSPGetID) { + // Create new OCSP_CERTID + OCSP_CERTID *cert_id = OCSP_CERTID_new(); + ASSERT_TRUE(cert_id); + + bssl::UniquePtr request(OCSP_REQUEST_new()); + ASSERT_TRUE(request); + + OCSP_ONEREQ *one = OCSP_request_add0_id(request.get(), cert_id); + ASSERT_TRUE(one); + + // Call function to get OCSP_CERTID + OCSP_CERTID *returned_id = OCSP_onereq_get0_id(one); + + // Verify the returned OCSP_CERTID is same as the one set + ASSERT_EQ(returned_id, cert_id); +} + diff --git a/include/openssl/ocsp.h b/include/openssl/ocsp.h index ed82af5c18..60a05f578c 100644 --- a/include/openssl/ocsp.h +++ b/include/openssl/ocsp.h @@ -159,6 +159,10 @@ OPENSSL_EXPORT int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, OPENSSL_EXPORT OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid); +// OCSP_onereq_get0_id returns the certificate identifier +// associated with an OCSP request +OPENSSL_EXPORT OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *one); + // OCSP_request_add1_nonce adds a nonce of value |val| and length |len| to // |req|. If |val| is NULL, a random nonce is generated and used. If |len| is // zero or negative, a default length of 16 bytes will be used.