X-Git-Url: https://www.bearssl.org/gitweb//home/git/?p=BearSSL;a=blobdiff_plain;f=src%2Fssl%2Fssl_hs_server.t0;h=bb3bc3dc3b5723306905ac1a9bdd27f03b0b3956;hp=a31ab6aaeba94934dfa525ec48aac7441b5729ca;hb=d8641065c992e2d06494d51f151355635f05dfa0;hpb=44c79c1add4cd4a217b1dd77c8421c1d3a08dcef diff --git a/src/ssl/ssl_hs_server.t0 b/src/ssl/ssl_hs_server.t0 index a31ab6a..bb3bc3d 100644 --- a/src/ssl/ssl_hs_server.t0 +++ b/src/ssl/ssl_hs_server.t0 @@ -49,7 +49,7 @@ do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id, /* * Decrypt the PMS. */ - x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, len); + x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, &len); /* * Set the first two bytes to the maximum supported client @@ -85,21 +85,12 @@ do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id, */ static void ecdh_common(br_ssl_server_context *ctx, int prf_id, - unsigned char *cpoint, size_t cpoint_len, uint32_t ctl) + unsigned char *xcoor, size_t xcoor_len, uint32_t ctl) { unsigned char rpms[80]; - size_t pms_len; - /* - * The point length is supposed to be 1+2*Xlen, where Xlen is - * the length (in bytes) of the X coordinate, i.e. the pre-master - * secret. If the provided point is too large, then it is - * obviously incorrect (i.e. everybody can see that it is - * incorrect), so leaking that fact is not a problem. - */ - pms_len = cpoint_len >> 1; - if (pms_len > sizeof rpms) { - pms_len = sizeof rpms; + if (xcoor_len > sizeof rpms) { + xcoor_len = sizeof rpms; ctl = 0; } @@ -108,19 +99,19 @@ ecdh_common(br_ssl_server_context *ctx, int prf_id, * decryption failed. Note that we use a constant-time conditional * copy. */ - br_hmac_drbg_generate(&ctx->eng.rng, rpms, pms_len); - br_ccopy(ctl ^ 1, cpoint + 1, rpms, pms_len); + br_hmac_drbg_generate(&ctx->eng.rng, rpms, xcoor_len); + br_ccopy(ctl ^ 1, xcoor, rpms, xcoor_len); /* * Compute master secret. */ - br_ssl_engine_compute_master(&ctx->eng, prf_id, cpoint + 1, pms_len); + br_ssl_engine_compute_master(&ctx->eng, prf_id, xcoor, xcoor_len); /* * Clear the pre-master secret from RAM: it is normally a buffer * in the context, hence potentially long-lived. */ - memset(cpoint, 0, cpoint_len); + memset(xcoor, 0, xcoor_len); } /* @@ -136,7 +127,7 @@ do_ecdh(br_ssl_server_context *ctx, int prf_id, * Finalise the key exchange. */ x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, - cpoint, cpoint_len); + cpoint, &cpoint_len); ecdh_common(ctx, prf_id, cpoint, cpoint_len, x); } @@ -169,6 +160,44 @@ do_static_ecdh(br_ssl_server_context *ctx, int prf_id) do_ecdh(ctx, prf_id, cpoint, cpoint_len); } +static size_t +hash_data(br_ssl_server_context *ctx, + void *dst, int hash_id, const void *src, size_t len) +{ + const br_hash_class *hf; + br_hash_compat_context hc; + + if (hash_id == 0) { + unsigned char tmp[36]; + + hf = br_multihash_getimpl(&ctx->eng.mhash, br_md5_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp); + hf = br_multihash_getimpl(&ctx->eng.mhash, br_sha1_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp + 16); + memcpy(dst, tmp, 36); + return 36; + } else { + hf = br_multihash_getimpl(&ctx->eng.mhash, hash_id); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, dst); + return (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + } +} + /* * Do the ECDHE key exchange (part 1: generation of transient key, and * computing of the point to send to the client). Returned value is the @@ -179,12 +208,10 @@ do_static_ecdh(br_ssl_server_context *ctx, int prf_id) static int do_ecdhe_part1(br_ssl_server_context *ctx, int curve) { - int hash; + unsigned algo_id; unsigned mask; const unsigned char *order; size_t olen, glen; - br_multihash_context mhc; - unsigned char head[4]; size_t hv_len, sig_len; if (!((ctx->eng.iec->supported_curves >> curve) & 1)) { @@ -213,55 +240,33 @@ do_ecdhe_part1(br_ssl_server_context *ctx, int curve) /* * Compute our ECDH point. */ -#if 0 -/* obsolete */ - generator = ctx->eng.iec->generator(curve, &glen); - memcpy(ctx->eng.ecdhe_point, generator, glen); - ctx->eng.ecdhe_point_len = glen; - if (!ctx->eng.iec->mul(ctx->eng.ecdhe_point, glen, - ctx->ecdhe_key, olen, curve)) - { - return -BR_ERR_INVALID_ALGORITHM; - } -#endif glen = ctx->eng.iec->mulgen(ctx->eng.ecdhe_point, ctx->ecdhe_key, olen, curve); ctx->eng.ecdhe_point_len = glen; /* - * Compute the signature. + * Assemble the message to be signed, and possibly hash it. */ - br_multihash_zero(&mhc); - br_multihash_copyimpl(&mhc, &ctx->eng.mhash); - br_multihash_init(&mhc); - br_multihash_update(&mhc, - ctx->eng.client_random, sizeof ctx->eng.client_random); - br_multihash_update(&mhc, - ctx->eng.server_random, sizeof ctx->eng.server_random); - head[0] = 3; - head[1] = 0; - head[2] = curve; - head[3] = ctx->eng.ecdhe_point_len; - br_multihash_update(&mhc, head, sizeof head); - br_multihash_update(&mhc, + memcpy(ctx->eng.pad, ctx->eng.client_random, 32); + memcpy(ctx->eng.pad + 32, ctx->eng.server_random, 32); + ctx->eng.pad[64 + 0] = 0x03; + ctx->eng.pad[64 + 1] = 0x00; + ctx->eng.pad[64 + 2] = curve; + ctx->eng.pad[64 + 3] = ctx->eng.ecdhe_point_len; + memcpy(ctx->eng.pad + 64 + 4, ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len); - hash = ctx->sign_hash_id; - if (hash) { - hv_len = br_multihash_out(&mhc, hash, ctx->eng.pad); + hv_len = 64 + 4 + ctx->eng.ecdhe_point_len; + algo_id = ctx->sign_hash_id; + if (algo_id >= (unsigned)0xFF00) { + hv_len = hash_data(ctx, ctx->eng.pad, algo_id & 0xFF, + ctx->eng.pad, hv_len); if (hv_len == 0) { return -BR_ERR_INVALID_ALGORITHM; } - } else { - if (!br_multihash_out(&mhc, br_md5_ID, ctx->eng.pad) - || !br_multihash_out(&mhc, - br_sha1_ID, ctx->eng.pad + 16)) - { - return -BR_ERR_INVALID_ALGORITHM; - } - hv_len = 36; } + sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable, - hash, hv_len, ctx->eng.pad, sizeof ctx->eng.pad); + algo_id, ctx->eng.pad, hv_len, sizeof ctx->eng.pad); return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM; } @@ -274,16 +279,18 @@ do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id, unsigned char *cpoint, size_t cpoint_len) { int curve; - uint32_t x; + uint32_t ctl; + size_t xoff, xlen; curve = ctx->eng.ecdhe_curve; /* * Finalise the key exchange. */ - x = ctx->eng.iec->mul(cpoint, cpoint_len, + ctl = ctx->eng.iec->mul(cpoint, cpoint_len, ctx->ecdhe_key, ctx->ecdhe_key_len, curve); - ecdh_common(ctx, prf_id, cpoint, cpoint_len, x); + xoff = ctx->eng.iec->xoff(curve, &xlen); + ecdh_common(ctx, prf_id, cpoint + xoff, xlen, ctl); /* * Clear the ECDHE private key. Forward Secrecy is achieved insofar @@ -509,7 +516,7 @@ cc: set-max-frag-len ( len -- ) { \ Open extension value. read16 open-elt - read-list-sign-algos addr-hashes set16 + read-list-sign-algos addr-hashes set32 \ Close extension value. close-elt ; @@ -578,7 +585,7 @@ cc: call-policy-handler ( -- bool ) { x = (*CTX->policy_vtable)->choose( CTX->policy_vtable, CTX, &choices); ENG->session.cipher_suite = choices.cipher_suite; - CTX->sign_hash_id = choices.hash_id; + CTX->sign_hash_id = choices.algo_id; ENG->chain = choices.chain; ENG->chain_len = choices.chain_len; T0_PUSHi(-(x != 0)); @@ -714,7 +721,7 @@ cc: save-session ( -- ) { \ -- client is reputed to know RSA and ECDSA, both with SHA-1 \ -- the default elliptic curve is P-256 (secp256r1, id = 23) 0 addr-server_name set8 - 0x404 addr-hashes set16 + 0x0404 addr-hashes set32 0x800000 addr-curves set32 \ Process extensions, if any. @@ -807,14 +814,19 @@ cc: save-session ( -- ) { \ we should mark the client as "supporting secure renegotiation". reneg-scsv if 2 addr-reneg set8 then + \ If, at that point, the 'reneg' value is still 0, then the client + \ did not send the extension or the SCSV, so we have to assume + \ that secure renegotiation is not supported by that client. + addr-reneg get8 ifnot 1 addr-reneg set8 then + \ Check compression. ok-compression ifnot 40 fail-alert then \ Filter hash function support by what the server also supports. \ If no common hash function remains with RSA and/or ECDSA, then \ the corresponding ECDHE suites are not possible. - supported-hash-functions drop 257 * - addr-hashes get16 and dup addr-hashes set16 + supported-hash-functions drop 257 * 0xFFFF0000 or + addr-hashes get32 and dup addr-hashes set32 \ In 'can-ecdhe', bit 12 is set if ECDHE_RSA is possible, bit 13 is \ set if ECDHE_ECDSA is possible. dup 0xFF and 0<> neg @@ -974,21 +986,33 @@ cc: do-ecdhe-part1 ( curve -- len ) { T0_PUSHi(do_ecdhe_part1(CTX, curve)); } +\ Get index of first bit set to 1 (in low to high order). +: lowest-1 ( bits -- n ) + dup ifnot drop -1 ret then + 0 begin dup2 >> 1 and 0= while 1+ repeat + swap drop ; + \ Write the Server Key Exchange message (if applicable). : write-ServerKeyExchange ( -- ) addr-cipher_suite get16 use-ecdhe? ifnot ret then \ We must select an appropriate curve among the curves that - \ are supported both by us and the peer. Right now we use - \ the one with the smallest ID, which in practice means P-256. + \ are supported both by us and the peer. Right now, we apply + \ a fixed preference order: Curve25519, P-256, P-384, P-521, + \ then the common curve with the lowest ID. \ (TODO: add some option to make that behaviour configurable.) \ \ This loop always terminates because previous processing made \ sure that ECDHE suites are not selectable if there is no common \ curve. - addr-curves get32 0 - begin dup2 >> 1 and 0= while 1+ repeat - { curve-id } drop + addr-curves get32 + dup 0x20000000 and if + drop 29 + else + dup 0x38000000 and dup if swap then + drop lowest-1 + then + { curve-id } \ Compute the signed curve point to send. curve-id do-ecdhe-part1 dup 0< if neg fail then { sig-len } @@ -1008,11 +1032,18 @@ cc: do-ecdhe-part1 ( curve -- len ) { \ If TLS-1.2+, write hash and signature identifiers. tls1.2+ if - \ Hash identifier is in the sign_hash_id field. - addr-sign_hash_id get8 write8 - \ 'use-rsa-ecdhe?' returns -1 for RSA, 0 for ECDSA. - \ The byte on the wire shall be 1 for RSA, 3 for ECDSA. - addr-cipher_suite get16 use-rsa-ecdhe? 1 << 3 + write8 + \ sign_hash_id contains either a hash identifier, + \ or the complete 16-bit value to write. + addr-sign_hash_id get16 + dup 0xFF00 < if + write16 + else + 0xFF and write8 + \ 'use-rsa-ecdhe?' returns -1 for RSA, 0 for + \ ECDSA. The byte on the wire shall be 1 for RSA, + \ 3 for ECDSA. + addr-cipher_suite get16 use-rsa-ecdhe? 1 << 3 + write8 + then then \ Signature.