diff options
Diffstat (limited to 'lib/ssl/src/tls_v1.erl')
-rw-r--r-- | lib/ssl/src/tls_v1.erl | 264 |
1 files changed, 234 insertions, 30 deletions
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 83dd7585dd..df2a421bce 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -36,7 +36,15 @@ default_signature_schemes/1, signature_schemes/2, groups/1, groups/2, group_to_enum/1, enum_to_group/1, default_groups/1]). --export([derive_secret/4, hkdf_expand_label/5, hkdf_extract/3, hkdf_expand/4]). +-export([derive_secret/4, hkdf_expand_label/5, hkdf_extract/3, hkdf_expand/4, + key_schedule/3, key_schedule/4, + external_binder_key/2, resumption_binder_key/2, + client_early_traffic_secret/3, early_exporter_master_secret/3, + client_handshake_traffic_secret/3, server_handshake_traffic_secret/3, + client_application_traffic_secret_0/3, server_application_traffic_secret_0/3, + exporter_master_secret/3, resumption_master_secret/3, + update_traffic_secret/2, calculate_traffic_keys/3, + transcript_hash/2]). -type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 | @@ -56,7 +64,7 @@ %% TLS 1.3 --------------------------------------------------- -spec derive_secret(Secret::binary(), Label::binary(), - Messages::binary(), Algo::ssl_cipher_format:hash()) -> Key::binary(). + Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Key::binary(). derive_secret(Secret, Label, Messages, Algo) -> Hash = crypto:hash(mac_algo(Algo), Messages), hkdf_expand_label(Secret, Label, @@ -71,11 +79,14 @@ hkdf_expand_label(Secret, Label0, Context, Length, Algo) -> %% opaque label<7..255> = "tls13 " + Label; %% opaque context<0..255> = Context; %% } HkdfLabel; - Content = << <<"tls13">>/binary, Label0/binary, Context/binary>>, + Label1 = << <<"tls13 ">>/binary, Label0/binary>>, + LLen = size(Label1), + Label = <<?BYTE(LLen), Label1/binary>>, + Content = <<Label/binary, Context/binary>>, Len = size(Content), HkdfLabel = <<?UINT16(Len), Content/binary>>, hkdf_expand(Secret, HkdfLabel, Length, Algo). - + -spec hkdf_extract(MacAlg::ssl_cipher_format:hash(), Salt::binary(), KeyingMaterial::binary()) -> PseudoRandKey::binary(). @@ -89,6 +100,12 @@ hkdf_extract(MacAlg, Salt, KeyingMaterial) -> hkdf_expand(PseudoRandKey, ContextInfo, Length, Algo) -> Iterations = erlang:ceil(Length / ssl_cipher:hash_size(Algo)), hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, 1, Iterations, <<>>, <<>>). + + +-spec transcript_hash(Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Hash::binary(). + +transcript_hash(Messages, Algo) -> + crypto:hash(mac_algo(Algo), Messages). %% TLS 1.3 --------------------------------------------------- %% TLS 1.0 -1.2 --------------------------------------------------- @@ -235,6 +252,153 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, ServerWriteKey, ClientIV, ServerIV}. %% TLS v1.2 --------------------------------------------------- +%% TLS v1.3 --------------------------------------------------- +%% RFC 8446 - 7.1. Key Schedule +%% +%% 0 +%% | +%% v +%% PSK -> HKDF-Extract = Early Secret +%% | +%% +-----> Derive-Secret(., "ext binder" | "res binder", "") +%% | = binder_key +%% | +%% +-----> Derive-Secret(., "c e traffic", ClientHello) +%% | = client_early_traffic_secret +%% | +%% +-----> Derive-Secret(., "e exp master", ClientHello) +%% | = early_exporter_master_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% (EC)DHE -> HKDF-Extract = Handshake Secret +%% | +%% +-----> Derive-Secret(., "c hs traffic", +%% | ClientHello...ServerHello) +%% | = client_handshake_traffic_secret +%% | +%% +-----> Derive-Secret(., "s hs traffic", +%% | ClientHello...ServerHello) +%% | = server_handshake_traffic_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% 0 -> HKDF-Extract = Master Secret +%% | +%% +-----> Derive-Secret(., "c ap traffic", +%% | ClientHello...server Finished) +%% | = client_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "s ap traffic", +%% | ClientHello...server Finished) +%% | = server_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "exp master", +%% | ClientHello...server Finished) +%% | = exporter_master_secret +%% | +%% +-----> Derive-Secret(., "res master", +%% ClientHello...client Finished) +%% = resumption_master_secret +-spec key_schedule(early_secret | handshake_secret | master_secret, + atom(), {psk | early_secret | handshake_secret, binary()}) -> + {early_secret | handshake_secret | master_secret, binary()}. + +key_schedule(early_secret, Algo, {psk, PSK}) -> + Len = ssl_cipher:hash_size(Algo), + Salt = binary:copy(<<?BYTE(0)>>, Len), + {early_secret, hkdf_extract(Algo, Salt, PSK)}; +key_schedule(master_secret, Algo, {handshake_secret, Secret}) -> + Len = ssl_cipher:hash_size(Algo), + IKM = binary:copy(<<?BYTE(0)>>, Len), + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {master_secret, hkdf_extract(Algo, Salt, IKM)}. +%% +key_schedule(handshake_secret, Algo, IKM, {early_secret, Secret}) -> + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {handshake_secret, hkdf_extract(Algo, Salt, IKM)}. + +-spec external_binder_key(atom(), {early_secret, binary()}) -> binary(). +external_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"ext binder">>, <<>>, Algo). + +-spec resumption_binder_key(atom(), {early_secret, binary()}) -> binary(). +resumption_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"res binder">>, <<>>, Algo). + +-spec client_early_traffic_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +client_early_traffic_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"c e traffic">>, M, Algo). + +-spec early_exporter_master_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +early_exporter_master_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"e exp master">>, M, Algo). + +-spec client_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +client_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"c hs traffic">>, M, Algo). + +-spec server_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +server_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"s hs traffic">>, M, Algo). + +-spec client_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +client_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"c ap traffic">>, M, Algo). + +-spec server_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +server_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"s ap traffic">>, M, Algo). + +-spec exporter_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +exporter_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"exp master">>, M, Algo). + +-spec resumption_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...client Finished +resumption_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"res master">>, M, Algo). + +%% The next-generation application_traffic_secret is computed as: +%% +%% application_traffic_secret_N+1 = +%% HKDF-Expand-Label(application_traffic_secret_N, +%% "traffic upd", "", Hash.length) +-spec update_traffic_secret(atom(), binary()) -> binary(). +update_traffic_secret(Algo, Secret) -> + hkdf_expand_label(Secret, <<"traffic upd">>, <<>>, ssl_cipher:hash_size(Algo), Algo). + +%% The traffic keying material is generated from the following input +%% values: +%% +%% - A secret value +%% +%% - A purpose value indicating the specific value being generated +%% +%% - The length of the key being generated +%% +%% The traffic keying material is generated from an input traffic secret +%% value using: +%% +%% [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) +%% [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) +-spec calculate_traffic_keys(atom(), atom(), binary()) -> {binary(), binary()}. +calculate_traffic_keys(HKDFAlgo, Cipher, Secret) -> + Key = hkdf_expand_label(Secret, <<"key">>, <<>>, ssl_cipher:key_material(Cipher), HKDFAlgo), + IV = hkdf_expand_label(Secret, <<"iv">>, <<>>, ssl_cipher:key_material(Cipher), HKDFAlgo), + {Key, IV}. + +%% TLS v1.3 --------------------------------------------------- + %% TLS 1.0 -1.2 --------------------------------------------------- -spec mac_hash(integer() | atom(), binary(), integer(), integer(), tls_record:tls_version(), integer(), binary()) -> binary(). @@ -254,7 +418,7 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, %% TODO 1.3 same as above? --spec suites(1|2|3|4) -> [ssl_cipher_format:cipher_suite()]. +-spec suites(1|2|3|4|'TLS_v1.3') -> [ssl_cipher_format:cipher_suite()]. suites(Minor) when Minor == 1; Minor == 2 -> [ @@ -315,7 +479,17 @@ suites(4) -> %% Not supported %% ?TLS_AES_128_CCM_SHA256, %% ?TLS_AES_128_CCM_8_SHA256 - ] ++ suites(3). + ] ++ suites(3); + +suites('TLS_v1.3') -> + [?TLS_AES_256_GCM_SHA384, + ?TLS_AES_128_GCM_SHA256, + ?TLS_CHACHA20_POLY1305_SHA256 + %% Not supported + %% ?TLS_AES_128_CCM_SHA256, + %% ?TLS_AES_128_CCM_8_SHA256 + ]. + signature_algs({3, 4}, HashSigns) -> signature_algs({3, 3}, HashSigns); @@ -347,7 +521,9 @@ signature_algs({3, 3}, HashSigns) -> lists:reverse(Supported). default_signature_algs({3, 4} = Version) -> - default_signature_schemes(Version); + %% TLS 1.3 servers shall be prepared to process TLS 1.2 ClientHellos + %% containing legacy hash-sign tuples. + default_signature_schemes(Version) ++ default_signature_algs({3,3}); default_signature_algs({3, 3} = Version) -> Default = [%% SHA2 {sha512, ecdsa}, @@ -373,15 +549,23 @@ signature_schemes(Version, SignatureSchemes) when is_tuple(Version) Hashes = proplists:get_value(hashs, CryptoSupports), PubKeys = proplists:get_value(public_keys, CryptoSupports), Curves = proplists:get_value(curves, CryptoSupports), - Fun = fun (Scheme, Acc) -> + RSAPSSSupported = lists:member(rsa_pkcs1_pss_padding, + proplists:get_value(rsa_opts, CryptoSupports)), + Fun = fun (Scheme, Acc) when is_atom(Scheme) -> {Hash0, Sign0, Curve} = ssl_cipher:scheme_to_components(Scheme), Sign = case Sign0 of - rsa_pkcs1 -> rsa; + rsa_pkcs1 -> + rsa; + rsa_pss_rsae when RSAPSSSupported -> + rsa; + rsa_pss_pss when RSAPSSSupported -> + rsa; S -> S end, Hash = case Hash0 of - sha1 -> sha; + sha1 -> + sha; H -> H end, case proplists:get_bool(Sign, PubKeys) @@ -394,7 +578,10 @@ signature_schemes(Version, SignatureSchemes) when is_tuple(Version) [Scheme | Acc]; false -> Acc - end + end; + %% Special clause for filtering out the legacy hash-sign tuples. + (_ , Acc) -> + Acc end, Supported = lists:foldl(Fun, [], SignatureSchemes), lists:reverse(Supported); @@ -403,22 +590,29 @@ signature_schemes(_, _) -> default_signature_schemes(Version) -> Default = [ - rsa_pkcs1_sha256, - rsa_pkcs1_sha384, - rsa_pkcs1_sha512, - ecdsa_secp256r1_sha256, - ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, - rsa_pss_rsae_sha256, - rsa_pss_rsae_sha384, + ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256, rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256, %% ed25519, %% ed448, - rsa_pss_pss_sha256, - rsa_pss_pss_sha384, - rsa_pss_pss_sha512, - rsa_pkcs1_sha1, - ecdsa_sha1 + + %% These values refer solely to signatures + %% which appear in certificates (see Section 4.4.2.2) and are not + %% defined for use in signed TLS handshake messages, although they + %% MAY appear in "signature_algorithms" and + %% "signature_algorithms_cert" for backward compatibility with + %% TLS 1.2. + rsa_pkcs1_sha512, + rsa_pkcs1_sha384, + rsa_pkcs1_sha256, + ecdsa_sha1, + rsa_pkcs1_sha1 ], signature_schemes(Version, Default). @@ -553,7 +747,9 @@ ecc_curves(_Minor, TLSCurves) -> -spec groups(4 | all | default) -> [group()]. groups(all) -> - [secp256r1, + [x25519, + x448, + secp256r1, secp384r1, secp521r1, ffdhe2048, @@ -562,27 +758,33 @@ groups(all) -> ffdhe6144, ffdhe8192]; groups(default) -> - [secp256r1, - secp384r1, - secp521r1, - ffdhe2048]; + [x25519, + x448, + secp256r1, + secp384r1]; groups(Minor) -> TLSGroups = groups(all), groups(Minor, TLSGroups). %% -spec groups(4, [group()]) -> [group()]. groups(_Minor, TLSGroups) -> - %% TODO: Adding FFDHE groups to crypto? - CryptoGroups = crypto:ec_curves() ++ [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192], + CryptoGroups = supported_groups(), lists:filter(fun(Group) -> proplists:get_bool(Group, CryptoGroups) end, TLSGroups). default_groups(Minor) -> TLSGroups = groups(default), groups(Minor, TLSGroups). +supported_groups() -> + %% TODO: Add new function to crypto? + proplists:get_value(curves, crypto:supports()) ++ + [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192]. + group_to_enum(secp256r1) -> 23; group_to_enum(secp384r1) -> 24; group_to_enum(secp521r1) -> 25; +group_to_enum(x25519) -> 29; +group_to_enum(x448) -> 30; group_to_enum(ffdhe2048) -> 256; group_to_enum(ffdhe3072) -> 257; group_to_enum(ffdhe4096) -> 258; @@ -592,6 +794,8 @@ group_to_enum(ffdhe8192) -> 260. enum_to_group(23) -> secp256r1; enum_to_group(24) -> secp384r1; enum_to_group(25) -> secp521r1; +enum_to_group(29) -> x25519; +enum_to_group(30) -> x448; enum_to_group(256) -> ffdhe2048; enum_to_group(257) -> ffdhe3072; enum_to_group(258) -> ffdhe4096; |