Added ChaCha20+Poly1305 support (stand-alone, cipher suites).
[BearSSL] / src / ssl / ssl_hs_common.t0
index a842b29..bbd37ac 100644 (file)
@@ -145,17 +145,14 @@ addr-eng: version_max
 addr-eng: suites_buf
 addr-eng: suites_num
 addr-eng: server_name
-\ addr-eng: version
-\ addr-eng: cipher_suite
 addr-eng: client_random
 addr-eng: server_random
-\ addr-eng: session_id_len
-\ addr-eng: session_id
 addr-eng: ecdhe_curve
 addr-eng: ecdhe_point
 addr-eng: ecdhe_point_len
 addr-eng: reneg
 addr-eng: saved_finished
+addr-eng: flags
 addr-eng: pad
 addr-eng: action
 addr-eng: alert
@@ -174,6 +171,10 @@ addr-session-field: version
 addr-session-field: cipher_suite
 addr-session-field: master_secret
 
+\ Check a server flag by index.
+: flag? ( index -- bool )
+       addr-flags get32 swap >> 1 and neg ;
+
 \ Define a word that evaluates to an error constant. This assumes that
 \ all relevant error codes are in the 0..63 range.
 : err:
@@ -207,6 +208,9 @@ err: ERR_LIMIT_EXCEEDED
 err: ERR_BAD_FINISHED
 err: ERR_RESUME_MISMATCH
 err: ERR_INVALID_ALGORITHM
+err: ERR_BAD_SIGNATURE
+err: ERR_WRONG_KEY_USAGE
+err: ERR_NO_CLIENT_AUTH
 
 \ Get supported curves (bit mask).
 cc: supported-curves ( -- x ) {
@@ -215,6 +219,7 @@ cc: supported-curves ( -- x ) {
 }
 
 \ Get supported hash functions (bit mask and number).
+\ Note: this (on purpose) skips MD5.
 cc: supported-hash-functions ( -- x num ) {
        int i;
        unsigned x, num;
@@ -231,6 +236,16 @@ cc: supported-hash-functions ( -- x num ) {
        T0_PUSH(num);
 }
 
+\ Test support for RSA signatures.
+cc: supports-rsa-sign? ( -- bool ) {
+       T0_PUSHi(-(ENG->irsavrfy != 0));
+}
+
+\ Test support for ECDSA signatures.
+cc: supports-ecdsa? ( -- bool ) {
+       T0_PUSHi(-(ENG->iecdsa != 0));
+}
+
 \ (Re)initialise the multihasher.
 cc: multihash-init ( -- ) {
        br_multihash_init(&ENG->mhash);
@@ -441,7 +456,7 @@ cc: read-chunk-native ( addr len -- addr len ) {
        addr-alert get8 case
                0 of
                        \ 'alert' field is 0, so this byte shall be a level.
-                       \ Levels shall be 1 (alert) or 2 (fatal); we convert
+                       \ Levels shall be 1 (warning) or 2 (fatal); we convert
                        \ all other values to "fatal".
                        dup 1 <> if drop 2 then
                        addr-alert set8 0
@@ -449,10 +464,13 @@ cc: read-chunk-native ( addr len -- addr len ) {
                1 of
                        0 addr-alert set8
                        \ close_notify has value 0.
+                       \ no_renegotiation has value 100, and we treat it
+                       \ as a fatal alert.
+                       dup 100 = if 256 + fail then
                        0= ret
                endof
                \ Fatal alert implies context termination.
-               256 + fail
+               drop 256 + fail
        endcase ;
 
 \ In general we only deal with handshake data here. Alerts are processed
@@ -782,8 +800,10 @@ hexb| 0000 | \ List terminator.
                2+
        again ;
 
-\ Check that a given cipher suite is supported.
+\ Check that a given cipher suite is supported. Note that this also
+\ returns true (-1) for the TLS_FALLBACK_SCSV pseudo-ciphersuite.
 : suite-supported? ( suite -- bool )
+       dup 0x5600 = if drop -1 ret then
        cipher-suite-to-elements 0<> ;
 
 \ Get expected key type for cipher suite. The key type is one of
@@ -883,8 +903,14 @@ hexb| 0000 | \ List terminator.
                        then
                endof
 
-               \ ChaCha20/Poly1305
-               \ 5 of endof
+               \ ChaCha20+Poly1305
+               5 of drop
+                       for-input if
+                               switch-chapol-in
+                       else
+                               switch-chapol-out
+                       then
+               endof
 
                ERR_BAD_PARAM fail
        endcase
@@ -938,6 +964,22 @@ cc: switch-aesgcm-in ( is_client prf_id cipher_key_len -- ) {
                ENG->iaes_ctr, cipher_key_len);
 }
 
+cc: switch-chapol-out ( is_client prf_id -- ) {
+       int is_client, prf_id;
+
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id);
+}
+
+cc: switch-chapol-in ( is_client prf_id -- ) {
+       int is_client, prf_id;
+
+       prf_id = T0_POP();
+       is_client = T0_POP();
+       br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id);
+}
+
 \ Write Finished message.
 : write-Finished ( from_client -- )
        compute-Finished
@@ -1010,3 +1052,186 @@ cc: compute-Finished-inner ( from_client prf_id -- ) {
        22 wait-rectype-out
        write-Finished
        flush-record ;
+
+\ Read and parse a list of supported signature algorithms (with hash
+\ functions). The resulting bit field is returned.
+: read-list-sign-algos ( lim -- lim value )
+       0 { hashes }
+       read16 open-elt
+       begin dup while
+               read8 { hash } read8 { sign }
+               \ We keep the value if the signature is either 1 (RSA)
+               \ or 3 (ECDSA), and the hash is one of the SHA-* functions
+               \ (2 to 6, from SHA-1 to SHA-512); we reject MD5.
+               hash 2 >= hash 6 <= and
+               sign 1 = sign 3 = or
+               and if
+                       hashes 1 sign 1- 2 << hash + << or >hashes
+               then
+       repeat
+       close-elt
+       hashes ;
+
+\ =======================================================================
+
+\ Compute total chain length. This includes the individual certificate
+\ headers, but not the total chain header. This also sets the cert_cur,
+\ cert_len and chain_len context fields.
+cc: total-chain-length ( -- len ) {
+       size_t u;
+       uint32_t total;
+
+       total = 0;
+       for (u = 0; u < ENG->chain_len; u ++) {
+               total += 3 + (uint32_t)ENG->chain[u].data_len;
+       }
+       T0_PUSH(total);
+}
+
+\ Get length for current certificate in the chain; if the chain end was
+\ reached, then this returns -1.
+cc: begin-cert ( -- len ) {
+       if (ENG->chain_len == 0) {
+               T0_PUSHi(-1);
+       } else {
+               ENG->cert_cur = ENG->chain->data;
+               ENG->cert_len = ENG->chain->data_len;
+               ENG->chain ++;
+               ENG->chain_len --;
+               T0_PUSH(ENG->cert_len);
+       }
+}
+
+\ Copy a chunk of certificate data into the pad. Returned value is the
+\ chunk length, or 0 if the certificate end is reached.
+cc: copy-cert-chunk ( -- len ) {
+       size_t clen;
+
+       clen = ENG->cert_len;
+       if (clen > sizeof ENG->pad) {
+               clen = sizeof ENG->pad;
+       }
+       memcpy(ENG->pad, ENG->cert_cur, clen);
+       ENG->cert_cur += clen;
+       ENG->cert_len -= clen;
+       T0_PUSH(clen);
+}
+
+\ Write a Certificate message. Total chain length (excluding the 3-byte
+\ header) is returned; it is 0 if the chain is empty.
+: write-Certificate ( -- total_chain_len )
+       11 write8
+       total-chain-length dup
+       dup 3 + write24 write24
+       begin
+               begin-cert
+               dup 0< if drop ret then write24
+               begin copy-cert-chunk dup while
+                       addr-pad swap write-blob
+               repeat
+               drop
+       again ;
+
+cc: x509-start-chain ( by_client -- ) {
+       const br_x509_class *xc;
+       uint32_t bc;
+
+       bc = T0_POP();
+       xc = *(ENG->x509ctx);
+       xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL);
+}
+
+cc: x509-start-cert ( length -- ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->start_cert(ENG->x509ctx, T0_POP());
+}
+
+cc: x509-append ( length -- ) {
+       const br_x509_class *xc;
+       size_t len;
+
+       xc = *(ENG->x509ctx);
+       len = T0_POP();
+       xc->append(ENG->x509ctx, ENG->pad, len);
+}
+
+cc: x509-end-cert ( -- ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       xc->end_cert(ENG->x509ctx);
+}
+
+cc: x509-end-chain ( -- err ) {
+       const br_x509_class *xc;
+
+       xc = *(ENG->x509ctx);
+       T0_PUSH(xc->end_chain(ENG->x509ctx));
+}
+
+cc: get-key-type-usages ( -- key-type-usages ) {
+       const br_x509_class *xc;
+       const br_x509_pkey *pk;
+       unsigned usages;
+
+       xc = *(ENG->x509ctx);
+       pk = xc->get_pkey(ENG->x509ctx, &usages);
+       if (pk == NULL) {
+               T0_PUSH(0);
+       } else {
+               T0_PUSH(pk->key_type | usages);
+       }
+}
+
+\ Read a Certificate message.
+\ Parameter: non-zero if this is a read by the client of a certificate
+\ sent by the server; zero otherwise.
+\ Returned value:
+\   - Empty: 0
+\   - Valid: combination of key type and allowed key usages.
+\   - Invalid: negative (-x for error code x)
+: read-Certificate ( by_client -- key-type-usages )
+       \ Get header, and check message type.
+       read-handshake-header 11 = ifnot ERR_UNEXPECTED fail then
+
+       \ If the chain is empty, do some special processing.
+       dup 3 = if
+               read24 if ERR_BAD_PARAM fail then
+               swap drop ret
+       then
+
+       \ Start processing the chain through the X.509 engine.
+       swap x509-start-chain
+
+       \ Total chain length is a 24-bit integer.
+       read24 open-elt
+       begin
+               dup while
+               read24 open-elt
+               dup x509-start-cert
+
+               \ We read the certificate by chunks through the pad, so
+               \ as to use the existing reading function (read-blob)
+               \ that also ensures proper hashing.
+               begin
+                       dup while
+                       dup 256 > if 256 else dup then { len }
+                       addr-pad len read-blob
+                       len x509-append
+               repeat
+               close-elt
+               x509-end-cert
+       repeat
+
+       \ We must close the chain AND the handshake message.
+       close-elt
+       close-elt
+
+       \ Chain processing is finished; get the error code.
+       x509-end-chain
+       dup if neg ret then drop
+
+       \ Return key type and usages.
+       get-key-type-usages ;