diff options
author | Péter Dimitrov <[email protected]> | 2019-01-30 10:50:20 +0100 |
---|---|---|
committer | Péter Dimitrov <[email protected]> | 2019-01-30 10:50:20 +0100 |
commit | 80bf6298d21340d07aedb15e59fdc3b6eaef6687 (patch) | |
tree | ff8f34da3c8ea07e122b0ec01b45a274147485cd /lib/ssl/src | |
parent | 4c25910dcaafe59e074afec9a2835d16f187f467 (diff) | |
parent | d2e05d86dd22f63d47f50202ab95fcbc29764794 (diff) | |
download | otp-80bf6298d21340d07aedb15e59fdc3b6eaef6687.tar.gz otp-80bf6298d21340d07aedb15e59fdc3b6eaef6687.tar.bz2 otp-80bf6298d21340d07aedb15e59fdc3b6eaef6687.zip |
Merge branch 'peterdmv/ssl/server-send-finished'
* peterdmv/ssl/server-send-finished:
ssl: Fix dialyzer warnings
ssl: Add 'Finished'
ssl: Use HKDF hash function in Transcript-Hash
ssl: Improve test of 1-RTT handshake
ssl: Update certificate_verify
ssl: Update function build_content
ssl: Fix encoding of the Certificate message
ssl: Add EncryptedExtensions
ssl: Fix encoding of empty extensions
ssl: Fix key schedule and traffic keys
ssl: Encode/decode CertificateVerify
Change-Id: Ie525de276ca4ebd9f9fb0fbdc9dc3822f91834e0
Diffstat (limited to 'lib/ssl/src')
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 2 | ||||
-rw-r--r-- | lib/ssl/src/ssl_logger.erl | 17 | ||||
-rw-r--r-- | lib/ssl/src/ssl_record.erl | 11 | ||||
-rw-r--r-- | lib/ssl/src/tls_connection_1_3.erl | 33 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 239 | ||||
-rw-r--r-- | lib/ssl/src/tls_v1.erl | 42 |
6 files changed, 221 insertions, 123 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 5e3c767c2c..a28f4add1b 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -592,7 +592,7 @@ encode_extensions(Exts) -> encode_extensions(Exts, <<>>). encode_extensions([], <<>>) -> - <<>>; + <<?UINT16(0)>>; encode_extensions([], Acc) -> Size = byte_size(Acc), <<?UINT16(Size), Acc/binary>>; diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index ce8225bf72..930077ba3c 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -35,6 +35,7 @@ -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). -include_lib("kernel/include/logger.hrl"). %%------------------------------------------------------------------------- @@ -159,8 +160,24 @@ parse_handshake(Direction, #hello_request{} = HelloRequest) -> Header = io_lib:format("~s Handshake, HelloRequest", [header_prefix(Direction)]), Message = io_lib:format("~p", [?rec_info(hello_request, HelloRequest)]), + {Header, Message}; +parse_handshake(Direction, #certificate_1_3{} = Certificate) -> + Header = io_lib:format("~s Handshake, Certificate", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_1_3, Certificate)]), + {Header, Message}; +parse_handshake(Direction, #certificate_verify_1_3{} = CertificateVerify) -> + Header = io_lib:format("~s Handshake, CertificateVerify", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(certificate_verify_1_3, CertificateVerify)]), + {Header, Message}; +parse_handshake(Direction, #encrypted_extensions{} = EncryptedExtensions) -> + Header = io_lib:format("~s Handshake, EncryptedExtensions", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(encrypted_extensions, EncryptedExtensions)]), {Header, Message}. + parse_cipher_suites([_|_] = Ciphers) -> [format_cipher(C) || C <- Ciphers]. diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 499ba108f2..d0a72ce51f 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -25,6 +25,7 @@ -module(ssl_record). -include("ssl_record.hrl"). +-include("ssl_connection.hrl"). -include("ssl_internal.hrl"). -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). @@ -124,12 +125,14 @@ activate_pending_connection_state(#{current_write := Current, %% Description: Activates the next encyrption state (e.g. handshake %% encryption). %%-------------------------------------------------------------------- -step_encryption_state(#{pending_read := PendingRead, - pending_write := PendingWrite} = States) -> +step_encryption_state(#state{connection_states = + #{pending_read := PendingRead, + pending_write := PendingWrite} = ConnStates} = State) -> NewRead = PendingRead#{sequence_number => 0}, NewWrite = PendingWrite#{sequence_number => 0}, - States#{current_read => NewRead, - current_write => NewWrite}. + State#state{connection_states = + ConnStates#{current_read => NewRead, + current_write => NewWrite}}. %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index a20499972b..48b3ff0d97 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -135,32 +135,10 @@ start(internal, end. -negotiated(internal, - Map, - #state{connection_states = ConnectionStates0, - session = #session{session_id = SessionId, - own_certificate = OwnCert}, - ssl_options = #ssl_options{} = SslOpts, - key_share = KeyShare, - handshake_env = #handshake_env{tls_handshake_history = HHistory0}, - private_key = CertPrivateKey, - static_env = #static_env{ - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, - socket = Socket, - transport_cb = Transport}} = State0, _Module) -> - Env = #{connection_states => ConnectionStates0, - session_id => SessionId, - own_certificate => OwnCert, - cert_db => CertDbHandle, - cert_db_ref => CertDbRef, - ssl_options => SslOpts, - key_share => KeyShare, - tls_handshake_history => HHistory0, - transport_cb => Transport, - socket => Socket, - private_key => CertPrivateKey}, - case tls_handshake_1_3:do_negotiated(Map, Env) of +%% TODO: remove suppression when function implemented! +-dialyzer([{nowarn_function, [negotiated/4]}, no_match]). +negotiated(internal, Map, State0, _Module) -> + case tls_handshake_1_3:do_negotiated(Map, State0) of #alert{} = Alert -> ssl_connection:handle_own_alert(Alert, {3,4}, negotiated, State0); M -> @@ -187,4 +165,5 @@ update_state(#state{connection_states = ConnectionStates0, pending_write => PendingWrite#{security_parameters => SecParamsW}}, State#state{connection_states = ConnectionStates, key_share = KeyShare, - session = Session#session{session_id = SessionId}}. + session = Session#session{session_id = SessionId}, + negotiated_version = {3,4}}. diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 670c4d424d..f92c54dc53 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -28,6 +28,7 @@ -include("tls_handshake_1_3.hrl"). -include("ssl_alert.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_connection.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -40,7 +41,8 @@ %% Create handshake messages -export([certificate/5, - certificate_verify/5, + certificate_verify/4, + encrypted_extensions/0, server_hello/4]). -export([do_negotiated/2]). @@ -66,8 +68,35 @@ server_hello_extensions(KeyShare) -> Extensions = #{server_hello_selected_version => SupportedVersions}, ssl_handshake:add_server_share(Extensions, KeyShare). +%% TODO: implement support for encrypted_extensions +encrypted_extensions() -> + #encrypted_extensions{ + extensions = #{} + }. %% TODO: use maybe monad for error handling! +%% enum { +%% X509(0), +%% RawPublicKey(2), +%% (255) +%% } CertificateType; +%% +%% struct { +%% select (certificate_type) { +%% case RawPublicKey: +%% /* From RFC 7250 ASN.1_subjectPublicKeyInfo */ +%% opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; +%% +%% case X509: +%% opaque cert_data<1..2^24-1>; +%% }; +%% Extension extensions<0..2^16-1>; +%% } CertificateEntry; +%% +%% struct { +%% opaque certificate_request_context<0..2^8-1>; +%% CertificateEntry certificate_list<0..2^24-1>; +%% } Certificate; certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, _, Chain} -> @@ -82,23 +111,56 @@ certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) end. -%% TODO: use maybe monad for error handling! -certificate_verify(OwnCert, PrivateKey, SignatureScheme, Messages, server) -> + +certificate_verify(PrivateKey, SignatureScheme, + #state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages, _}}}, server) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, write), + #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, + {HashAlgo, _, _} = ssl_cipher:scheme_to_components(SignatureScheme), - %% Transcript-Hash(Handshake Context, Certificate) - Context = [Messages, OwnCert], - THash = tls_v1:transcript_hash(Context, HashAlgo), + Context = lists:reverse(Messages), - Signature = digitally_sign(THash, <<"TLS 1.3, server CertificateVerify">>, - HashAlgo, PrivateKey), + %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. + THash = tls_v1:transcript_hash(Context, HKDFAlgo), - #certificate_verify_1_3{ - algorithm = SignatureScheme, - signature = Signature + %% Digital signatures use the hash function defined by the selected signature + %% scheme. + case digitally_sign(THash, <<"TLS 1.3, server CertificateVerify">>, + HashAlgo, PrivateKey) of + {ok, Signature} -> + {ok, #certificate_verify_1_3{ + algorithm = SignatureScheme, + signature = Signature + }}; + {error, badarg} -> + {error, badarg} + + end. + + +finished(#state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = {Messages, _}}}) -> + #{security_parameters := SecParamsR} = + ssl_record:current_connection_state(ConnectionStates, write), + #security_parameters{prf_algorithm = HKDFAlgo, + master_secret = SHTS} = SecParamsR, + + FinishedKey = tls_v1:finished_key(SHTS, HKDFAlgo), + VerifyData = tls_v1:finished_verify_data(FinishedKey, HKDFAlgo, Messages), + + #finished{ + verify_data = VerifyData }. + %%==================================================================== %% Encode handshake %%==================================================================== @@ -115,6 +177,12 @@ encode_handshake(#certificate_1_3{ EncContext = encode_cert_req_context(Context), EncEntries = encode_cert_entries(Entries), {?CERTIFICATE, <<EncContext/binary, EncEntries/binary>>}; +encode_handshake(#certificate_verify_1_3{ + algorithm = Algorithm, + signature = Signature}) -> + EncAlgo = encode_algorithm(Algorithm), + EncSign = encode_signature(Signature), + {?CERTIFICATE_VERIFY, <<EncAlgo/binary, EncSign/binary>>}; encode_handshake(#encrypted_extensions{extensions = Exts})-> {?ENCRYPTED_EXTENSIONS, encode_extensions(Exts)}; encode_handshake(#new_session_ticket{ @@ -164,6 +232,11 @@ decode_handshake(?CERTIFICATE, <<?BYTE(CSize), Context:CSize/binary, certificate_request_context = Context, certificate_list = CertList }; +decode_handshake(?CERTIFICATE_VERIFY, <<?UINT16(EncAlgo), ?UINT16(Size), Signature:Size/binary>>) -> + Algorithm = ssl_cipher:signature_scheme(EncAlgo), + #certificate_verify_1_3{ + algorithm = Algorithm, + signature = Signature}; decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>) -> #encrypted_extensions{ extensions = decode_extensions(EncExts, encrypted_extensions) @@ -204,9 +277,16 @@ encode_cert_entries([#certificate_entry{data = Data, extensions = Exts} | Rest], Acc) -> DSize = byte_size(Data), BinExts = encode_extensions(Exts), - ExtSize = byte_size(BinExts), encode_cert_entries(Rest, - [<<?UINT24(DSize), Data/binary, ?UINT16(ExtSize), BinExts/binary>> | Acc]). + [<<?UINT24(DSize), Data/binary, BinExts/binary>> | Acc]). + +encode_algorithm(Algo) -> + Scheme = ssl_cipher:signature_scheme(Algo), + <<?UINT16(Scheme)>>. + +encode_signature(Signature) -> + Size = byte_size(Signature), + <<?UINT16(Size), Signature/binary>>. decode_cert_entries(Entries) -> decode_cert_entries(Entries, []). @@ -260,22 +340,26 @@ certificate_entry(DER) -> %% 79 %% 00 %% 0101010101010101010101010101010101010101010101010101010101010101 -digitally_sign(THash, Context, HashAlgo, PrivateKey = #'RSAPrivateKey'{}) -> +digitally_sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), %% The length of the Salt MUST be equal to the length of the output - %% of the digest algorithm. - PadLen = ssl_cipher:hash_size(HashAlgo), - - public_key:sign(Content, HashAlgo, PrivateKey, + %% of the digest algorithm: rsa_pss_saltlen = -1 + try public_key:sign(Content, HashAlgo, PrivateKey, [{rsa_padding, rsa_pkcs1_pss_padding}, - {rsa_pss_saltlen, PadLen}]). + {rsa_pss_saltlen, -1}, + {rsa_mgf1_md, HashAlgo}]) of + Signature -> + {ok, Signature} + catch + error:badarg -> + {error, badarg} + end. build_content(Context, THash) -> - <<" ", - " ", - Context/binary,?BYTE(0),THash/binary>>. + Prefix = binary:copy(<<32>>, 64), + <<Prefix/binary,Context/binary,?BYTE(0),THash/binary>>. %%==================================================================== %% Handle handshake messages @@ -362,17 +446,19 @@ do_negotiated(#{client_share := ClientKey, group := SelectedGroup, sign_alg := SignatureScheme } = Map, - #{connection_states := ConnectionStates0, - session_id := SessionId, - own_certificate := OwnCert, - cert_db := CertDbHandle, - cert_db_ref := CertDbRef, - ssl_options := SslOpts, - key_share := KeyShare, - tls_handshake_history := HHistory0, - transport_cb := Transport, - socket := Socket, - private_key := CertPrivateKey}) -> + #state{connection_states = ConnectionStates0, + session = #session{session_id = SessionId, + own_certificate = OwnCert}, + ssl_options = #ssl_options{} = _SslOpts, + key_share = KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + private_key = CertPrivateKey, + static_env = #static_env{ + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> {Ref,Maybe} = maybe(), try @@ -380,46 +466,40 @@ do_negotiated(#{client_share := ClientKey, %% Extensions: supported_versions, key_share, (pre_shared_key) ServerHello = server_hello(SessionId, KeyShare, ConnectionStates0, Map), - %% Update handshake_history (done in encode!) - %% Encode handshake - {BinMsg, ConnectionStates1, HHistory1} = - tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0), - %% Send server_hello - tls_connection:send(Transport, Socket, BinMsg), - log_handshake(SslOpts, ServerHello), - log_tls_record(SslOpts, BinMsg), - - %% ConnectionStates2 = calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, - %% HHistory1, ConnectionStates1), + {State1, _} = tls_connection:send_handshake(ServerHello, State0), + {HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV} = - calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, - HHistory1, ConnectionStates1), - ConnectionStates2 = - update_pending_connection_states(ConnectionStates1, HandshakeSecret, + calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, State1), + + State2 = + update_pending_connection_states(State1, HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV), - ConnectionStates3 = - ssl_record:step_encryption_state(ConnectionStates2), + + State3 = ssl_record:step_encryption_state(State2), + + %% Create EncryptedExtensions + EncryptedExtensions = encrypted_extensions(), + + %% Encode EncryptedExtensions + State4 = tls_connection:queue_handshake(EncryptedExtensions, State3), %% Create Certificate Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server), %% Encode Certificate - {_, _ConnectionStates4, HHistory2} = - tls_connection:encode_handshake(Certificate, {3,4}, ConnectionStates3, HHistory1), - %% log_handshake(SslOpts, Certificate), + State5 = tls_connection:queue_handshake(Certificate, State4), %% Create CertificateVerify - {Messages, _} = HHistory2, - - %% Use selected signature_alg from here, HKDF only used for key_schedule - CertificateVerify = - tls_handshake_1_3:certificate_verify(OwnCert, CertPrivateKey, SignatureScheme, - Messages, server), - io:format("### CertificateVerify: ~p~n", [CertificateVerify]), - + CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, + State5, server)), %% Encode CertificateVerify + State6 = tls_connection:queue_handshake(CertificateVerify, State5), + + %% Create Finished + Finished = finished(State6), - %% Send Certificate, CertifricateVerify + %% Encode Certificate, CertifricateVerify + {_State7, _} = tls_connection:send_handshake(Finished, State6), %% Send finished @@ -440,28 +520,19 @@ not_implemented(State) -> {error, {state_not_implemented, State}}. -log_handshake(SslOpts, Message) -> - Msg = #{direction => outbound, - protocol => 'handshake', - message => Message}, - ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}). - - -log_tls_record(SslOpts, BinMsg) -> - Report = #{direction => outbound, - protocol => 'tls_record', - message => BinMsg}, - ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}). - - -calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, HHistory, ConnectionStates) -> +calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, + #state{connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}}) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, read), #security_parameters{prf_algorithm = HKDFAlgo, cipher_suite = CipherSuite} = SecParamsR, %% Calculate handshake_secret - EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, <<>>}), + PSK = binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo)), + EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}), PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), @@ -479,7 +550,8 @@ calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, HHistory, Conn {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret), {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret), - {HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV}. + %% TODO: store all relevant secrets in state! + {ServerHSTrafficSecret, ReadKey, ReadIV, WriteKey, WriteIV}. %% %% Update pending connection state %% PendingRead0 = ssl_record:pending_connection_state(ConnectionStates, read), @@ -527,13 +599,14 @@ calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) public_key:compute_key(Point, MyKey). -update_pending_connection_states(CS = #{pending_read := PendingRead0, - pending_write := PendingWrite0}, +update_pending_connection_states(#state{connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV) -> PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV), PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV), - CS#{pending_read => PendingRead, - pending_write => PendingWrite}. + State#state{connection_states = CS#{pending_read => PendingRead, + pending_write => PendingWrite}}. update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, HandshakeSecret, Key, IV) -> diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index df2a421bce..5c023bd2d8 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -37,14 +37,14 @@ 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, - key_schedule/3, key_schedule/4, + key_schedule/3, key_schedule/4, create_info/3, 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]). + transcript_hash/2, finished_key/2, finished_verify_data/3]). -type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 | @@ -74,18 +74,24 @@ derive_secret(Secret, Label, Messages, Algo) -> Context::binary(), Length::integer(), Algo::ssl_cipher_format:hash()) -> KeyingMaterial::binary(). hkdf_expand_label(Secret, Label0, Context, Length, Algo) -> + HkdfLabel = create_info(Label0, Context, Length), + hkdf_expand(Secret, HkdfLabel, Length, Algo). + +%% Create info parameter for HKDF-Expand: +%% HKDF-Expand(PRK, info, L) -> OKM +create_info(Label0, Context0, Length) -> %% struct { %% uint16 length = Length; %% opaque label<7..255> = "tls13 " + Label; %% opaque context<0..255> = Context; %% } HkdfLabel; Label1 = << <<"tls13 ">>/binary, Label0/binary>>, - LLen = size(Label1), - Label = <<?BYTE(LLen), Label1/binary>>, + LabelLen = size(Label1), + Label = <<?BYTE(LabelLen), Label1/binary>>, + ContextLen = size(Context0), + Context = <<?BYTE(ContextLen),Context0/binary>>, Content = <<Label/binary, Context/binary>>, - Len = size(Content), - HkdfLabel = <<?UINT16(Len), Content/binary>>, - hkdf_expand(Secret, HkdfLabel, Length, Algo). + <<?UINT16(Length), Content/binary>>. -spec hkdf_extract(MacAlg::ssl_cipher_format:hash(), Salt::binary(), KeyingMaterial::binary()) -> PseudoRandKey::binary(). @@ -368,6 +374,25 @@ exporter_master_secret(Algo, {master_secret, Secret}, M) -> resumption_master_secret(Algo, {master_secret, Secret}, M) -> derive_secret(Secret, <<"res master">>, M, Algo). +-spec finished_key(binary(), atom()) -> binary(). +finished_key(BaseKey, Algo) -> + %% finished_key = + %% HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) + ssl_cipher:hash_size(Algo), + hkdf_expand_label(BaseKey, <<"finished">>, <<>>, ssl_cipher:hash_size(Algo), Algo). + +-spec finished_verify_data(binary(), atom(), iodata()) -> binary(). +finished_verify_data(FinishedKey, HKDFAlgo, Messages) -> + %% The verify_data value is computed as follows: + %% + %% verify_data = + %% HMAC(finished_key, + %% Transcript-Hash(Handshake Context, + %% Certificate*, CertificateVerify*)) + Context = lists:reverse(Messages), + THash = tls_v1:transcript_hash(Context, HKDFAlgo), + tls_v1:hmac_hash(HKDFAlgo, FinishedKey, THash). + %% The next-generation application_traffic_secret is computed as: %% %% application_traffic_secret_N+1 = @@ -394,7 +419,8 @@ update_traffic_secret(Algo, Secret) -> -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), + %% TODO: remove hard coded IV size + IV = hkdf_expand_label(Secret, <<"iv">>, <<>>, 12, HKDFAlgo), {Key, IV}. %% TLS v1.3 --------------------------------------------------- |