From 68e803093b8bf43b39f3090a1b717b850a5e8f4b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 24 Oct 2011 15:01:28 +0200 Subject: Clean up of public_key code adding specs and documentation --- lib/public_key/src/pubkey_pbe.erl | 57 +++++++++++++++----------- lib/public_key/src/pubkey_pem.erl | 63 +++++------------------------ lib/public_key/src/public_key.erl | 85 ++++++++++++++++++--------------------- 3 files changed, 81 insertions(+), 124 deletions(-) (limited to 'lib/public_key/src') diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index 32be347039..77d6943d96 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -26,7 +26,6 @@ -export([pbdkdf1/4, pbdkdf2/6]). -define(DEFAULT_SHA_MAC_KEYLEN, 20). - -define(ASN1_OCTET_STR_TAG, 4). -define(IV_LEN, 8). @@ -34,11 +33,11 @@ %% Internal application API %%==================================================================== -pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfOutputLen)-> - NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen), - NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen , - blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, Count, Prf, PrfOutputLen, <<>>). - +%%-------------------------------------------------------------------- +-spec encode(binary(), string(), string(), term()) -> binary(). +%% +%% Description: Performs password based encoding +%%-------------------------------------------------------------------- encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_encrypt(Key, IV, Data); @@ -51,7 +50,11 @@ encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:rc2_cbc_encrypt(Key, IV, Data). - +%%-------------------------------------------------------------------- +-spec decode(binary(), string(), string(), term()) -> binary(). +%% +%% Description: Performs password based decoding +%%-------------------------------------------------------------------- decode(Data, Password,"DES-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), crypto:des_cbc_decrypt(Key, IV, Data); @@ -102,7 +105,13 @@ decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{ %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -password_to_key_and_iv(Password, Cipher, {salt, Salt}) -> +password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) -> + {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen, IV} = + key_derivation_params(Params), + <> = + pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen), + {Key, IV}; +password_to_key_and_iv(Password, Cipher, Salt) -> KeyLen = derived_key_length(Cipher, undefined), <> = pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5), @@ -110,12 +119,6 @@ password_to_key_and_iv(Password, Cipher, {salt, Salt}) -> %% pbdkdf1 and uses then salt as IV {Key, Salt}. -password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) -> - {Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen, IV} = - key_derivation_params(Params), - <> = - pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoOtputLen), - {Key, IV}. pem_encrypt(_, _, _, 0, Acc, _) -> Acc; pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) -> @@ -129,8 +132,8 @@ do_pbdkdf1(Prev, Count, Acc, Hash) -> do_pbdkdf1(Result, Count-1 , <>, Hash). iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, - parameters = ASNIV}) when (Algo == ?'desCBC') or - (Algo == ?'des-EDE3-CBC') -> + parameters = ASNIV}) when (Algo == ?'desCBC') or + (Algo == ?'des-EDE3-CBC') -> %% This is an so called open ASN1-type that in this %% case will be an octet-string of length 8 <> = ASNIV, @@ -145,7 +148,8 @@ blocks(1, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> <>; blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfLen, Acc) -> XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfLen), - blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfLen, <>). + blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, + PrfLen, <>). xor_sum(Password, Salt, Count, Index, Prf, PrfLen) -> Result = Prf(Password, [Salt,<>], PrfLen), @@ -172,11 +176,13 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, #'PBES2-params_encryptionScheme'{algorithm = Algo} = EncScheme, {PseudoRandomFunction, PseudoOtputLen} = pseudo_random_function(Prf), KeyLen = derived_key_length(Algo, Length), - {iolist_to_binary(OctetSalt), Count, KeyLen, PseudoRandomFunction, PseudoOtputLen, iv(EncScheme)}. + {OctetSalt, Count, KeyLen, + PseudoRandomFunction, PseudoOtputLen, iv(EncScheme)}. -%% This function currently matches a tuple that ougth to be the value ?'id-hmacWithSHA1, -%% but we need some kind of ASN1-fix for this. -pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> +%% This function currently matches a tuple that ougth to be the value +%% ?'id-hmacWithSHA1, but we need some kind of ASN1-fix for this. +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = + {_,_, _,'id-hmacWithSHA1'}}) -> {fun crypto:sha_mac/3, pseudo_output_length(?'id-hmacWithSHA1')}. pseudo_output_length(?'id-hmacWithSHA1') -> @@ -184,11 +190,14 @@ pseudo_output_length(?'id-hmacWithSHA1') -> derived_key_length(_, Len) when is_integer(Len) -> Len; -derived_key_length(Cipher,_) when (Cipher == ?'desCBC') or (Cipher == "DES-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'desCBC') or + (Cipher == "DES-CBC") -> 8; -derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or (Cipher == "RC2-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or + (Cipher == "RC2-CBC") -> 16; -derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> +derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or + (Cipher == "DES-EDE3-CBC") -> 24. cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index f19aab0533..910473d629 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -43,8 +43,6 @@ -include("public_key.hrl"). -export([encode/1, decode/1, decipher/2, cipher/3]). -%% Backwards compatibility -%%-export([decode_key/2]). -define(ENCODED_LINE_LENGTH, 64). @@ -69,26 +67,22 @@ encode(PemEntries) -> encode_pem_entries(PemEntries). %%-------------------------------------------------------------------- --spec decipher({pki_asn1_type(), DerEncrypted::binary(), term()}, - %%{Cipher :: string(), - %%Salt :: binary()}}, - string()) -> Der::binary(). +-spec decipher({pki_asn1_type(), DerEncrypted::binary(), + {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}}}, + string()) -> Der::binary(). %% %% Description: Deciphers a decrypted pem entry. %%-------------------------------------------------------------------- decipher({_, DecryptDer, {Cipher, KeyDevParams}}, Password) -> - %%decode_key(DecryptDer, Password, Cipher, Salt). pubkey_pbe:decode(DecryptDer, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- --spec cipher(Der::binary(), term(), -%%{Cipher :: string(), Hash::atom(), Salt :: binary()} , +-spec cipher(Der::binary(), {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}} , string()) -> binary(). %% %% Description: Ciphers a PEM entry %%-------------------------------------------------------------------- cipher(Der, {Cipher, KeyDevParams}, Password)-> - %%encode_key(Der, Password, Cipher, Salt). pubkey_pbe:encode(Der, Password, Cipher, KeyDevParams). %%-------------------------------------------------------------------- @@ -100,7 +94,7 @@ encode_pem_entries(Entries) -> encode_pem_entry({Type, Der, not_encrypted}) -> StartStr = pem_start(Type), [StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]; -encode_pem_entry({Type, Der, {Cipher, {_, Salt}}}) -> +encode_pem_entry({Type, Der, {Cipher, Salt}}) -> StartStr = pem_start(Type), [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"]. @@ -126,7 +120,7 @@ decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) Decoded = base64:mime_decode(Cs), [_, DekInfo0] = string:tokens(binary_to_list(Line), ": "), [Cipher, Salt] = string:tokens(DekInfo0, ","), - {Type, Decoded, {Cipher, {salt, unhex(Salt)}}}; + {Type, Decoded, {Cipher, unhex(Salt)}}; decode_pem_entry(Start, Lines) -> Type = asn1_type(Start), Cs = erlang:iolist_to_binary(Lines), @@ -140,9 +134,9 @@ decode_pem_entry(Start, Lines) -> decode_encrypted_private_keyinfo(Der) -> #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo, - encryptedData = Data} = public_key:der_decode('EncryptedPrivateKeyInfo', Der), - DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo), - + encryptedData = Data} = + public_key:der_decode('EncryptedPrivateKeyInfo', Der), + DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo), {'PrivateKeyInfo', iolist_to_binary(Data), DecryptParams}. split_bin(Bin) -> @@ -176,37 +170,6 @@ join_entry([<<"-----END ", _/binary>>| Lines], Entry) -> join_entry([Line | Lines], Entry) -> join_entry(Lines, [Line | Entry]). -%% decode_key(Data, Password, "DES-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 8), -%% IV = Salt, -%% crypto:des_cbc_decrypt(Key, IV, Data); -%% decode_key(Data, Password, "DES-EDE3-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 24), -%% IV = Salt, -%% <> = Key, -%% crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data). - -%% encode_key(Data, Password, "DES-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 8), -%% IV = Salt, -%% crypto:des_cbc_encrypt(Key, IV, Data); -%% encode_key(Data, Password, "DES-EDE3-CBC", Salt) -> -%% Key = password_to_key(Password, Salt, 24), -%% IV = Salt, -%% <> = Key, -%% crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data). - -%% password_to_key(Data, Salt, KeyLen) -> -%% <> = -%% password_to_key(<<>>, Data, Salt, KeyLen, <<>>), -%% Key. - -%% password_to_key(_, _, _, Len, Acc) when Len =< 0 -> -%% Acc; -%% password_to_key(Prev, Data, Salt, Len, Acc) -> -%% M = crypto:md5([Prev, Data, Salt]), -%% password_to_key(M, Data, Salt, Len - size(M), <>). - unhex(S) -> unhex(S, []). @@ -273,11 +236,3 @@ pem_decrypt() -> pem_decrypt_info(Cipher, Salt) -> io_lib:format("DEK-Info: ~s,~s", [Cipher, lists:flatten(hexify(Salt))]). - -%%-------------------------------------------------------------------- -%%% Deprecated -%%-------------------------------------------------------------------- -%% decode_key({_Type, Bin, not_encrypted}, _) -> -%% Bin; -%% decode_key({_Type, Bin, {Chipher,Salt}}, Password) -> -%% decode_key(Bin, Password, Chipher, Salt). diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 19465e7828..753322b46d 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -45,13 +45,6 @@ ssh_decode/2, ssh_encode/2 ]). -%% Deprecated -%% -export([decode_private_key/1, decode_private_key/2, pem_to_der/1]). - -%% -deprecated({pem_to_der, 1, next_major_release}). -%% -deprecated({decode_private_key, 1, next_major_release}). -%% -deprecated({decode_private_key, 2, next_major_release}). - -type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'. -type public_crypt_options() :: [{rsa_pad, rsa_padding()}]. @@ -104,20 +97,23 @@ pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), pem_entry_decode({Asn1Type, Der, not_encrypted}, _) when is_atom(Asn1Type), is_binary(Der) -> der_decode(Asn1Type, Der); -pem_entry_decode({Asn1Type, CryptDer, {Cipher, _Params}} = PemEntry, - Password) when is_atom(Asn1Type), - is_binary(CryptDer), +pem_entry_decode({Asn1Type, CryptDer, {Cipher, #'PBES2-params'{}}} = PemEntry, + Password) when is_atom(Asn1Type) andalso + is_binary(CryptDer) andalso is_list(Cipher) -> - Der = pubkey_pem:decipher(PemEntry, Password), - der_decode(Asn1Type, Der). + do_pem_entry_decode(PemEntry, Password); +pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry, + Password) when is_atom(Asn1Type) andalso + is_binary(CryptDer) andalso + is_list(Cipher) andalso + is_binary(Salt) andalso + erlang:byte_size(Salt) == 8 -> + do_pem_entry_decode(PemEntry, Password). %%-------------------------------------------------------------------- -spec pem_entry_encode(pki_asn1_type(), term()) -> pem_entry(). --spec pem_entry_encode(pki_asn1_type(), term(), - %%{{Cipher :: string(), Salt :: binary()}, string()} - term()) -> - pem_entry(). - % +-spec pem_entry_encode(pki_asn1_type(), term(), term()) -> pem_entry(). +%% %% Description: Creates a pem entry that can be feed to pem_encode/1. %%-------------------------------------------------------------------- pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) -> @@ -135,22 +131,27 @@ pem_entry_encode('SubjectPublicKeyInfo', pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> Der = der_encode(Asn1Type, Entity), {Asn1Type, Der, not_encrypted}. -pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) when is_atom(Asn1Type), - %%is_list(Cipher), - %%is_binary(Salt), - %%is_atom(Hash), - %% erlang:byte_size(Salt) == 8, - is_list(Password)-> - Der = der_encode(Asn1Type, Entity), - DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password), - {Asn1Type, DecryptDer, CipherInfo}. - +pem_entry_encode(Asn1Type, Entity, {{Cipher, #'PBES2-params'{}} = CipherInfo, + Password}) when is_atom(Asn1Type) andalso + is_list(Password) andalso + is_list(Cipher) -> + do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password); + +pem_entry_encode(Asn1Type, Entity, {{Cipher, Salt} = CipherInfo, + Password}) when is_atom(Asn1Type) andalso + is_list(Password) andalso + is_list(Cipher) andalso + is_binary(Salt) andalso + erlang:byte_size(Salt) == 8 -> + do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password). + %%-------------------------------------------------------------------- -spec der_decode(asn1_type(), Der::binary()) -> term(). %% %% Description: Decodes a public key asn1 der encoded entity. %%-------------------------------------------------------------------- -der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or (Asn1Type == 'EncryptedPrivateKeyInfo') +der_decode(Asn1Type, Der) when (Asn1Type == 'PrivateKeyInfo') or + (Asn1Type == 'EncryptedPrivateKeyInfo') andalso is_binary(Der) -> try {ok, Decoded} = 'PKCS-FRAME':decode(Asn1Type, Der), @@ -174,7 +175,8 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> %% %% Description: Encodes a public key entity with asn1 DER encoding. %%-------------------------------------------------------------------- -der_encode(Asn1Type, Entity) when Asn1Type == 'PrivateKeyInfo'; Asn1Type == 'EncryptedPrivateKeyInfo' -> +der_encode(Asn1Type, Entity) when (Asn1Type == 'PrivateKeyInfo') or + (Asn1Type == 'EncryptedPrivateKeyInfo') -> try {ok, Encoded} = 'PKCS-FRAME':encode(Asn1Type, Entity), iolist_to_binary(Encoded) @@ -552,6 +554,14 @@ ssh_encode(Entries, Type) when is_list(Entries), %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password) -> + Der = der_encode(Asn1Type, Entity), + DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password), + {Asn1Type, DecryptDer, CipherInfo}. + +do_pem_entry_decode({Asn1Type,_, _} = PemEntry, Password) -> + Der = pubkey_pem:decipher(PemEntry, Password), + der_decode(Asn1Type, Der). encrypt_public(PlainText, N, E, Options)-> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), @@ -649,20 +659,3 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer, sized_binary(Binary) -> Size = size(Binary), <>. - -%%-------------------------------------------------------------------- -%%% Deprecated functions -%%-------------------------------------------------------------------- -%% pem_to_der(CertSource) -> -%% {ok, Bin} = file:read_file(CertSource), -%% {ok, pubkey_pem:decode(Bin)}. - -%% decode_private_key(KeyInfo) -> -%% decode_private_key(KeyInfo, no_passwd). - -%% decode_private_key(KeyInfo = {'RSAPrivateKey', _, _}, Password) -> -%% DerEncoded = pubkey_pem:decode_key(KeyInfo, Password), -%% 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded); -%% decode_private_key(KeyInfo = {'DSAPrivateKey', _, _}, Password) -> -%% DerEncoded = pubkey_pem:decode_key(KeyInfo, Password), -%% 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded). -- cgit v1.2.3