From 826ff38deec221e306b2f4a9ee529fae1dbbedf7 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 19 Apr 2013 15:47:47 +0200 Subject: ssl & public_key: Improved handling ECDH keys --- lib/public_key/src/public_key.erl | 163 ++++++++++++++++++-------------------- lib/ssl/src/ssl_connection.erl | 35 ++++---- lib/ssl/src/ssl_handshake.erl | 9 ++- lib/ssl/test/erl_make_certs.erl | 4 +- 4 files changed, 99 insertions(+), 112 deletions(-) diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 41ebaef76d..06bffeea76 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -34,7 +34,8 @@ decrypt_private/2, decrypt_private/3, encrypt_public/2, encrypt_public/3, decrypt_public/2, decrypt_public/3, - sign/3, verify/4, generate_key/1, generate_key/2, + sign/3, verify/4, + generate_key/1, compute_key/2, compute_key/3, pkix_sign/2, pkix_verify/2, pkix_sign_types/1, @@ -326,18 +327,14 @@ encrypt_private(PlainText, %%-------------------------------------------------------------------- -spec generate_key(#'ECPrivateKey'{} | {curve, Name ::atom()} | #'DHParameter'{}) -> {'ECKey', term()} | {binary(), binary()}. --spec generate_key(#'ECPoint'{}, #'OTPECParameters'{} | {namedCurve, oid()}) -> {'ECKey', term()}. - %% Description: Generates new key(s) %%-------------------------------------------------------------------- -generate_key(#'ECPrivateKey'{} = Key) -> - ec_private_key_to_eckey(Key); - generate_key({curve, Name}) -> %% TODO: Better crypto API ECDHKey = crypto:ec_key_new(Name), crypto:ec_key_generate(ECDHKey), - crypto:ec_key_to_term(ECDHKey); + Term = crypto:ec_key_to_term(ECDHKey), + ec_key(Term); generate_key(#'DHParameter'{prime = P, base = G}) -> crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]); @@ -350,27 +347,33 @@ generate_key({srp, Version, Generator, Prime}) when is_binary(Generator), is_bin crypto:srp_generate_key(Generator, Prime, Version); generate_key({srp, Version, Verifier, Generator, Prime}) when is_binary(Verifier), is_binary(Generator), is_binary(Prime) -> - crypto:srp_generate_key(Verifier, Generator, Prime, Version). + crypto:srp_generate_key(Verifier, Generator, Prime, Version); -generate_key(#'ECPoint'{} = Key, Params) -> +generate_key(Params) -> %% TODO: Better crypto API - ECKey = ec_public_key_to_eckey({Key,Params}), - ECClntKey = crypto:term_to_ec_key(ECKey), + Name = ec_curve_spec(Params), + ECClntKey = crypto:ec_key_new(Name), + %% ECDHKey = format_ecdh_key(Params), + %% ECClntKey = crypto:term_to_ec_key(ECDHKey), crypto:ec_key_generate(ECClntKey), - crypto:ec_key_to_term(ECClntKey). + Term = crypto:ec_key_to_term(ECClntKey), + ec_key(Term, Params). %%-------------------------------------------------------------------- --spec compute_key(#'ECPoint'{}, {'ECKey', binary()}) -> binary(). +-spec compute_key(#'ECPoint'{}, #'ECPrivateKey'{} | crypto:ecdh_key()) -> binary(). -spec compute_key(OthersKey ::binary(), MyKey::binary() | {binary(), binary()}, {dh, binary(), binary()} | - {srp, atom(), binary(), binary()} | - {srp, string(), string(), binary(), atom(), binary(), binary()}) + {srp,'3'|'6'| '6a' , binary(), binary()} | + {srp, string(), string(), binary(), '3'|'6'| '6a', binary(), binary()}) -> binary(). %% Description: Compute shared secret %%-------------------------------------------------------------------- -compute_key(#'ECPoint'{point = Point}, Term) -> +compute_key(PubKey, #'ECPrivateKey'{} = PrivateKey) -> + compute_key(PubKey, format_ecdh_key(PrivateKey)); + +compute_key(#'ECPoint'{point = Point}, ECDHKeys) -> %% TODO: Better crypto API - ECKey = crypto:term_to_ec_key(Term), + ECKey = crypto:term_to_ec_key(ECDHKeys), crypto:ecdh_compute_key(ECKey, Point). compute_key(OthersKey, MyKey, {dh, Prime, Base}) when is_binary(OthersKey), @@ -428,30 +431,16 @@ pkix_sign_types(?'ecdsa-with-SHA512') -> dsa_private_key()) -> Signature :: binary(). %% Description: Create digital signature. %%-------------------------------------------------------------------- -sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) -> - crypto:sign(rsa, DigestType, Digest, format_rsa_private_key(Key)); - -sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) -> - crypto:sign(rsa, DigestType, PlainText, format_rsa_private_key(Key)); - -sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> - crypto:sign(dss, sha, Digest, [P, Q, G, X]); +sign(DigestOrPlainText, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:sign(rsa, DigestType, DigestOrPlainText, format_rsa_private_key(Key)); -sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> - crypto:sign(dss, sha, PlainText, [P, Q, G, X]); +sign(DigestOrPlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:sign(dss, sha, DigestOrPlainText, [P, Q, G, X]); -sign(Digest, DigestType, Key = {?'id-ecPublicKey', _, _}) -> - sign(Digest, DigestType, ec_public_key_to_eckey(Key)); - -sign({digest,_} = Digest, DigestType, Key = #'ECPrivateKey'{}) -> - ECDHKey = ec_private_key_to_eckey(Key), - ECKey = crypto:term_to_ec_key(ECDHKey), - crypto:sign(ecdsa, DigestType, Digest, ECKey); - -sign(PlainText, DigestType, Key = #'ECPrivateKey'{}) -> - ECDHKey = ec_private_key_to_eckey(Key), +sign(DigestOrPlainText, DigestType, Key = #'ECPrivateKey'{}) -> + ECDHKey = format_ecdh_key(Key), ECKey = crypto:term_to_ec_key(ECDHKey), - crypto:sign(ecdsa, DigestType, PlainText, ECKey); + crypto:sign(ecdsa, DigestType, DigestOrPlainText, ECKey); %% Backwards compatible sign(Digest, none, #'DSAPrivateKey'{} = Key) -> @@ -463,36 +452,28 @@ sign(Digest, none, #'DSAPrivateKey'{} = Key) -> | dsa_public_key()) -> boolean(). %% Description: Verifies a digital signature. %%-------------------------------------------------------------------- -verify({digest,_} = Digest, DigestType, Signature, - #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> - crypto:verify(rsa, DigestType, Digest, Signature, [Exp, Mod]); - -verify(PlainText, DigestType, Signature, +verify(DigestOrPlainText, DigestType, Signature, #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> - crypto:verify(rsa, DigestType, PlainText, Signature, + crypto:verify(rsa, DigestType, DigestOrPlainText, Signature, [Exp, Mod]); -verify({digest,_} = Digest, sha = DigestType, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(Signature) -> - crypto:verify(dss, DigestType, Digest, Signature, [P, Q, G, Key]); - verify(Digest, DigestType, Signature, Key = #'ECPrivateKey'{}) -> - ECDHKey = ec_private_key_to_eckey(Key), + ECDHKey = format_ecdh_key(Key), ECKey = crypto:term_to_ec_key(ECDHKey), crypto:verify(ecdsa, DigestType, Digest, Signature, ECKey); -verify(Digest, DigestType, Signature, Key = {#'ECPoint'{}, _}) -> - ECDHKey = ec_public_key_to_eckey(Key), +verify(DigestOrPlaintext, DigestType, Signature, Key = {#'ECPoint'{}, _}) -> + ECDHKey = format_ecdh_key(Key), ECKey = crypto:term_to_ec_key(ECDHKey), - crypto:verify(ecdsa, DigestType, Digest, Signature, ECKey); + crypto:verify(ecdsa, DigestType, DigestOrPlaintext, Signature, ECKey); %% Backwards compatibility verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) -> verify({digest,Digest}, sha, Signature, Key); -verify(PlainText, sha = DigestType, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> - crypto:verify(dss, DigestType, PlainText, Signature, [P, Q, G, Key]). +verify(DigestOrPlainText, sha = DigestType, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) + when is_integer(Key), is_binary(Signature) -> + crypto:verify(dss, DigestType, DigestOrPlainText, Signature, [P, Q, G, Key]). %%-------------------------------------------------------------------- -spec pkix_sign(#'OTPTBSCertificate'{}, @@ -939,40 +920,46 @@ format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, is_integer(D) -> [E, N, D]. -%% -%% Description: convert a ECPrivate key into resource Key -%%-------------------------------------------------------------------- +format_ecdh_key(#'ECPrivateKey'{privateKey = PrivKey, + parameters = Param, + publicKey = _}) -> + ECCurve = ec_curve_spec(Param), + {ECCurve, list2int(PrivKey), undefined}; + +format_ecdh_key({#'ECPoint'{point = Point}, Param}) -> + ECCurve = ec_curve_spec(Param), + {ECCurve, undefined, Point}. + +ec_curve_spec( #'OTPECParameters'{fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) -> + Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'OTPFieldID'.fieldType), + FieldId#'OTPFieldID'.parameters}, + Curve = {list2int(PCurve#'Curve'.a), list2int(PCurve#'Curve'.b), none}, + {Field, Curve, erlang:list_to_binary(Base), Order, CoFactor}; +ec_curve_spec({namedCurve, OID}) -> + pubkey_cert_records:namedCurves(OID). + +ec_key({Curve, PrivateKey, PubKey}) when is_atom(Curve) -> + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivateKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(Curve)}, + publicKey = {0, PubKey}}. + +ec_key({Curve, PrivateKey, PubKey}, _Params) when is_atom(Curve) -> + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivateKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(Curve)}, + publicKey = {0, PubKey}}; + +ec_key({_Curve, PrivateKey, PubKey}, Params) -> + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivateKey), + parameters = Params, + publicKey = {0, PubKey}}. + list2int(L) -> S = length(L) * 8, <> = erlang:iolist_to_binary(L), R. - -ec_private_key_to_eckey(#'ECPrivateKey'{privateKey = PrivKey, - parameters = Param, - publicKey = _PubKey}) -> - ECCurve = - case Param of - #'OTPECParameters'{ fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor } -> - Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'OTPFieldID'.fieldType), - FieldId#'OTPFieldID'.parameters}, - Curve = {list2int(PCurve#'Curve'.a), list2int(PCurve#'Curve'.b), none}, - {Field, Curve, erlang:list_to_binary(Base), Order, CoFactor}; - {namedCurve, OID} -> - pubkey_cert_records:namedCurves(OID) - end, - {ECCurve, list2int(PrivKey), undefined}. - %%{'ECKey', crypto:term_to_ec_key(Key)}. - -ec_public_key_to_eckey({#'ECPoint'{point = ECPoint}, Param}) -> - ECCurve = - case Param of - #'OTPECParameters'{ fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor } -> - Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'OTPFieldID'.fieldType), - FieldId#'OTPFieldID'.parameters}, - Curve = {list2int(PCurve#'Curve'.a), list2int(PCurve#'Curve'.b), none}, - {Field, Curve, erlang:list_to_binary(Base), Order, CoFactor}; - {namedCurve, OID} -> - pubkey_cert_records:namedCurves(OID) - end, - {ECCurve, undefined, ECPoint}. - %%{'ECKey', crypto:term_to_ec_key(Key)}. +int2list(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + binary_to_list(<>). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index dc9ab89331..4d64cd8523 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1378,9 +1378,9 @@ handle_peer_cert(PeerCert, PublicKeyInfo, public_key_info = PublicKeyInfo}, State2 = case PublicKeyInfo of {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams} -> - Keys = public_key:generate_key(PublicKey, PublicKeyParams), - State3 = State1#state{diffie_hellman_keys = Keys}, - ec_dh_master_secret(Keys, PublicKey, State3); + ECDHKey = public_key:generate_key(PublicKeyParams), + State3 = State1#state{diffie_hellman_keys = ECDHKey}, + ec_dh_master_secret(ECDHKey, PublicKey, State3); _ -> State1 end, @@ -1615,13 +1615,13 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == dhe_dss; Algo == dhe_rsa; Algo == dh_anon -> - Keys = public_key:generate_key(Params), + DHKeys = public_key:generate_key(Params), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, + Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1629,13 +1629,12 @@ key_exchange(#state{role = server, key_algorithm = Algo, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = Keys, + diffie_hellman_keys = DHKeys, tls_handshake_history = Handshake}; key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = State) when Algo == ecdh_ecdsa; Algo == ecdh_rsa -> - ECDH = public_key:generate_key(Key), - State#state{diffie_hellman_keys = ECDH}; + State#state{diffie_hellman_keys = Key}; key_exchange(#state{role = server, key_algorithm = Algo, hashsign_algorithm = HashSignAlgo, private_key = PrivateKey, @@ -1648,13 +1647,13 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa; Algo == ecdh_anon -> - ECDHKey = public_key:generate_key({curve, default_curve(State)}), + ECDHKeys = public_key:generate_key({curve, default_curve(State)}), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKey, + Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1662,7 +1661,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = ECDHKey, + diffie_hellman_keys = ECDHKeys, tls_handshake_history = Handshake1}; key_exchange(#state{role = server, key_algorithm = psk, @@ -1704,13 +1703,13 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, socket = Socket, transport_cb = Transport } = State) -> - Keys = public_key:generate_key(Params), + DHKeys = public_key:generate_key(Params), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, Keys, Params, + Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1718,7 +1717,7 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = Keys, + diffie_hellman_keys = DHKeys, tls_handshake_history = Handshake}; key_exchange(#state{role = server, key_algorithm = rsa_psk, @@ -2051,8 +2050,8 @@ server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDh server_master_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey}, State) -> - Key = public_key:generate_key({curve, ECCurve}), - ec_dh_master_secret(Key, #'ECPoint'{point = ECServerPubKey}, State#state{diffie_hellman_keys = Key}); + ECDHKeys = public_key:generate_key({curve, ECCurve}), + ec_dh_master_secret(ECDHKeys, #'ECPoint'{point = ECServerPubKey}, State#state{diffie_hellman_keys = ECDHKeys}); server_master_secret(#server_psk_params{ hint = IdentityHint}, @@ -2098,9 +2097,9 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) -> {dh, PMpint, GMpint}), master_from_premaster_secret(PremasterSecret, State). -ec_dh_master_secret(ECKey, ECPoint, State) -> +ec_dh_master_secret(ECDHKeys, ECPoint, State) -> PremasterSecret = - public_key:compute_key(ECPoint, ECKey), + public_key:compute_key(ECPoint, ECDHKeys), master_from_premaster_secret(PremasterSecret, State). handle_psk_identity(_PSKIdentity, LookupFun) diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index f736de3327..cde3e6fc66 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -418,7 +418,7 @@ key_exchange(client, _Version, {dh, <>}) -> dh_public = PublicKey} }; -key_exchange(client, _Version, {ecdh, {_,_,ECPublicKey}}) -> +key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) -> #client_key_exchange{ exchange_keys = #client_ec_diffie_hellman_public{ dh_public = ECPublicKey} @@ -461,7 +461,8 @@ key_exchange(server, Version, {dh, {<>, _}, enc_server_key_exchange(Version, ServerDHParams, HashSign, ClientRandom, ServerRandom, PrivateKey); -key_exchange(server, Version, {ecdh, {ECCurve, _, ECPublicKey}, HashSign, ClientRandom, ServerRandom, +key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}, + parameters = ECCurve}, HashSign, ClientRandom, ServerRandom, PrivateKey}) -> ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey}, enc_server_key_exchange(Version, ServerECParams, HashSign, @@ -1513,10 +1514,10 @@ enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) -> GLen = byte_size(G), YLen = byte_size(Y), <>; -enc_server_key(#server_ecdh_params{curve = ECCurve, public = ECPubKey}) -> +enc_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) -> %%TODO: support arbitrary keys KLen = size(ECPubKey), - <>; enc_server_key(#server_psk_params{hint = PskIdentityHint}) -> Len = byte_size(PskIdentityHint), diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl index f37928be85..f8d086513b 100644 --- a/lib/ssl/test/erl_make_certs.erl +++ b/lib/ssl/test/erl_make_certs.erl @@ -114,8 +114,8 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) -> #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}); #'ECPrivateKey'{version = _Version, privateKey = _PrivKey, - parameters = _Params, publicKey = _PubKey} -> - public_key:pkix_verify(DerEncodedCert, Key) + parameters = Params, publicKey = {0, PubKey}} -> + public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params}) end. %%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3