Crypt-Bear

 view release on metacpan or  search on metacpan

src/ssl/ssl_hs_common.t0  view on Meta::CPAN


\ Scan the list of supported cipher suites for a given value. If found,
\ then the list index at which it was found is returned; otherwise, -1
\ is returned.
: scan-suite ( suite -- index )
	{ suite }
	addr-suites_num get8 { num }
	0
	begin dup num < while
		dup 1 << addr-suites_buf + get16 suite = if ret then
		1+
	repeat
	drop -1 ;

\ =======================================================================

\ Generate random bytes into buffer (address is offset in context).
cc: mkrand ( addr len -- ) {
	size_t len = (size_t)T0_POP();
	void *addr = (unsigned char *)ENG + (size_t)T0_POP();
	br_hmac_drbg_generate(&ENG->rng, addr, len);
}

\ Read a handshake message header: type and length. These are returned
\ in reverse order (type is TOS, length is below it).
: read-handshake-header-core ( -- lim type )
	read8-nc 3 read24 swap drop swap ;

\ Read a handshake message header: type and length. If the header is for
\ a HelloRequest message, then it is discarded and a new header is read
\ (repeatedly if necessary).
: read-handshake-header ( -- lim type )
	begin
		read-handshake-header-core dup 0= while
		drop if ERR_BAD_HANDSHAKE fail then
	repeat ;

\ =======================================================================

\ Cipher suite processing.
\
\ Unfortunately, cipher suite identifiers are attributed mostly arbitrary,
\ so we have to map the cipher suite numbers we support into aggregate
\ words that encode the information we need. Table below is organized
\ as a sequence of pairs of 16-bit words, the first being the cipher suite
\ identifier, the second encoding the algorithm elements. The suites are
\ ordered by increasing cipher suite ID, so that fast lookups may be
\ performed with a binary search (not implemented for the moment, since it
\ does not appear to matter much in practice).
\
\ Algorithm elements are encoded over 4 bits each, in the following order
\ (most significant to least significant):
\ 
\ -- Server key type:
\       0  RSA           (RSA key exchange)
\       1  ECDHE-RSA     (ECDHE key exchange, RSA signature)
\       2  ECDHE-ECDSA   (ECDHE key exchange, ECDSA signature)
\       3  ECDH-RSA      (ECDH key exchange, certificate is RSA-signed)
\       4  ECDH-ECDSA    (ECDH key exchange, certificate is ECDSA-signed)
\ -- Encryption algorithm:
\       0  3DES/CBC
\       1  AES-128/CBC
\       2  AES-256/CBC
\       3  AES-128/GCM
\       4  AES-256/GCM
\       5  ChaCha20/Poly1305
\       6  AES-128/CCM
\       7  AES-256/CCM
\       8  AES-128/CCM8
\       9  AES-256/CCM8
\ -- MAC algorithm:
\       0  none         (for suites with AEAD encryption)
\       2  HMAC/SHA-1
\       4  HMAC/SHA-256
\       5  HMAC/SHA-384
\ -- PRF for TLS-1.2:
\       4  with SHA-256
\       5  with SHA-384
\
\ WARNING: if adding a new cipher suite that does not use SHA-256 for the
\ PRF (with TLS 1.2), be sure to check the suites_sha384[] array defined
\ in ssl/ssl_keyexport.c

data: cipher-suite-def

hexb| 000A 0024 | \ TLS_RSA_WITH_3DES_EDE_CBC_SHA
hexb| 002F 0124 | \ TLS_RSA_WITH_AES_128_CBC_SHA
hexb| 0035 0224 | \ TLS_RSA_WITH_AES_256_CBC_SHA
hexb| 003C 0144 | \ TLS_RSA_WITH_AES_128_CBC_SHA256
hexb| 003D 0244 | \ TLS_RSA_WITH_AES_256_CBC_SHA256

hexb| 009C 0304 | \ TLS_RSA_WITH_AES_128_GCM_SHA256
hexb| 009D 0405 | \ TLS_RSA_WITH_AES_256_GCM_SHA384

hexb| C003 4024 | \ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
hexb| C004 4124 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
hexb| C005 4224 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
hexb| C008 2024 | \ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
hexb| C009 2124 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
hexb| C00A 2224 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
hexb| C00D 3024 | \ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
hexb| C00E 3124 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
hexb| C00F 3224 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
hexb| C012 1024 | \ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
hexb| C013 1124 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
hexb| C014 1224 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

hexb| C023 2144 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
hexb| C024 2255 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
hexb| C025 4144 | \ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
hexb| C026 4255 | \ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
hexb| C027 1144 | \ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
hexb| C028 1255 | \ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
hexb| C029 3144 | \ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
hexb| C02A 3255 | \ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
hexb| C02B 2304 | \ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
hexb| C02C 2405 | \ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
hexb| C02D 4304 | \ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
hexb| C02E 4405 | \ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
hexb| C02F 1304 | \ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
hexb| C030 1405 | \ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
hexb| C031 3304 | \ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
hexb| C032 3405 | \ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384

hexb| C09C 0604 | \ TLS_RSA_WITH_AES_128_CCM
hexb| C09D 0704 | \ TLS_RSA_WITH_AES_256_CCM
hexb| C0A0 0804 | \ TLS_RSA_WITH_AES_128_CCM_8
hexb| C0A1 0904 | \ TLS_RSA_WITH_AES_256_CCM_8
hexb| C0AC 2604 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM
hexb| C0AD 2704 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM
hexb| C0AE 2804 | \ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
hexb| C0AF 2904 | \ TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8

hexb| CCA8 1504 | \ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
hexb| CCA9 2504 | \ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256

hexb| 0000 | \ List terminator.

\ Convert cipher suite identifier to element words. This returns 0 if
\ the cipher suite is not known.
: cipher-suite-to-elements ( suite -- elts )
	{ id }
	cipher-suite-def
	begin
		dup 2+ swap data-get16
		dup ifnot 2drop 0 ret then
		id = if data-get16 ret then
		2+
	again ;

\ 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
\ BR_KEYTYPE_RSA or BR_KEYTYPE_EC, combined with either BR_KEYTYPE_KEYX
\ (RSA encryption or static ECDH) or BR_KEYTYPE_SIGN (RSA or ECDSA
\ signature, for ECDHE cipher suites).
: expected-key-type ( suite -- key-type )
	cipher-suite-to-elements 12 >>
	case
		0 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX } endof
		1 of CX 0 63 { BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN } endof
		2 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_SIGN } endof
		3 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
		4 of CX 0 63 { BR_KEYTYPE_EC  | BR_KEYTYPE_KEYX } endof
		0 swap
	endcase ;

\ Test whether the cipher suite uses RSA key exchange.
: use-rsa-keyx? ( suite -- bool )
	cipher-suite-to-elements 12 >> 0= ;

\ Test whether the cipher suite uses ECDHE key exchange, signed with RSA.
: use-rsa-ecdhe? ( suite -- bool )
	cipher-suite-to-elements 12 >> 1 = ;

\ Test whether the cipher suite uses ECDHE key exchange, signed with ECDSA.
: use-ecdsa-ecdhe? ( suite -- bool )
	cipher-suite-to-elements 12 >> 2 = ;

\ Test whether the cipher suite uses ECDHE key exchange (with RSA or ECDSA).
: use-ecdhe? ( suite -- bool )
	cipher-suite-to-elements 12 >> dup 0> swap 3 < and ;

\ Test whether the cipher suite uses ECDH (static) key exchange.
: use-ecdh? ( suite -- bool )
	cipher-suite-to-elements 12 >> 2 > ;

\ Get identifier for the PRF (TLS 1.2).
: prf-id ( suite -- id )
	cipher-suite-to-elements 15 and ;

\ Test whether a cipher suite is only for TLS-1.2. Cipher suites that
\ can be used with TLS-1.0 or 1.1 use HMAC/SHA-1. RFC do not formally
\ forbid using a CBC-based TLS-1.2 cipher suite, e.g. based on HMAC/SHA-256,
\ with older protocol versions; however, servers should not do that, since
\ it may confuse clients. Since the server code does not try such games,
\ for consistency, the client should reject it as well (normal servers
\ don't do that, so any attempt is a sign of foul play).
: use-tls12? ( suite -- bool )
	cipher-suite-to-elements 0xF0 and 0x20 <> ;

\ Switch to negotiated security parameters for input or output.
: switch-encryption ( is-client for-input -- )
	{ for-input }
	addr-cipher_suite get16 cipher-suite-to-elements { elts }

	\ prf_id
	elts 15 and

	\ mac_id
	elts 4 >> 15 and

	\ cipher type and key length
	elts 8 >> 15 and case
		\ 3DES/CBC
		0 of 0 24
			for-input if
				switch-cbc-in
			else
				switch-cbc-out
			then
		endof

		\ AES-128/CBC
		1 of 1 16
			for-input if
				switch-cbc-in
			else
				switch-cbc-out
			then
		endof

		\ AES-256/CBC
		2 of 1 32
			for-input if
				switch-cbc-in
			else
				switch-cbc-out
			then
		endof

		\ AES-128/GCM
		3 of drop 16
			for-input if
				switch-aesgcm-in
			else
				switch-aesgcm-out
			then
		endof

		\ AES-256/GCM
		4 of drop 32
			for-input if
				switch-aesgcm-in
			else
				switch-aesgcm-out
			then
		endof

		\ ChaCha20+Poly1305
		5 of drop
			for-input if
				switch-chapol-in
			else
				switch-chapol-out
			then
		endof

		\ Now we only have AES/CCM suites (6 to 9). Since the
		\ input is between 0 and 15, and we checked values 0 to 5,
		\ we only need to reject values larger than 9.
		dup 9 > if
			ERR_BAD_PARAM fail
		then

		\ Stack: is_client prf_id mac_id cipher_id
		\ We want to remove the mac_id (it is zero for CCM suites)
		\ and replace the cipher_id with the key and tag lengths.
		\ The following table applies:
		\  id   key length   tag length
		\   6       16          16
		\   7       32          16
		\   8       16           8
		\   9       32           8
		swap drop
		dup 1 and 4 << 16 + swap
		8 and 16 swap -
		for-input if
			switch-aesccm-in
		else
			switch-aesccm-out
		then
		ret



( run in 0.474 second using v1.01-cache-2.11-cpan-e1769b4cff6 )