%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %% %%% Description: rsa public-key sign and verify -module(ssh_rsa). -export([verify/3, sign/2]). -export([alg_name/0]). -include("ssh.hrl"). -include("PKCS-1.hrl"). -define(MGF(Seed,Len), mgf1((Seed),(Len))). -define(HASH(X), crypto:sha((X))). -define(HLen, 20). %% start() -> %% crypto:start(). %% sign_file(File) -> %% start(), %% {ok,Bin} = file:read_file(File), %% {ok,Key} = ssh_file:private_host_rsa_key(user), %% sign(Key, Bin). %% verify_file(File, Sig) -> %% start(), %% {ok,Bin} = file:read_file(File), %% {ok,Key} = ssh_file:public_host_rsa_key(user), %% verify(Key, Bin, Sig). sign(Private,Mb) -> rsassa_pkcs1_v1_5_sign(Private,Mb). verify(Public,Mb,Sb) -> rsassa_pkcs1_v1_5_verify(Public,Mb,Sb). %% Integer to octet string i2osp(X, XLen) -> ssh_bits:i2bin(X, XLen). %% Octet string to Integer os2ip(X) -> ssh_bits:bin2i(X). %% decrypt1, M = message representative %% rsaep(#ssh_key { public={N,E}}, M) -> %% ?ssh_assert(M >= 0 andalso M =< N-1, out_of_range), %% ssh_math:ipow(M, E, N). %% encrypt1, C = cipher representative %% rsadp(#ssh_key { public={N,_}, private={_,D}}, C) -> %% ?ssh_assert(C >= 0 andalso C =< N-1, out_of_range), %% ssh_math:ipow(C, D, N). %% sign1, M = message representative rsasp1(#ssh_key { public={N,_}, private={_,D}}, M) -> ?ssh_assert((M >= 0 andalso M =< N-1), out_of_range), ssh_math:ipow(M, D, N). %% verify1, S =signature representative rsavp1(#ssh_key { public={N,E}}, S) -> ?ssh_assert(S >= 0 andalso S =< N-1, out_of_range), ssh_math:ipow(S, E, N). %% M messaage %% rsaes_oaep_encrypt(Public, M) -> %% rsaes_oaep_encrypt(Public, M, <<>>). %% rsaes_oaep_encrypt(Public=#ssh_key { public={N,_E}}, M, L) -> %% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), %% K = (ssh_bits:isize(N)+7) div 8, %% MLen = size(M), %% %% LLen = size(L), %% ?ssh_assert(MLen =< K - 2*?HLen - 2, message_to_long), %% LHash = ?HASH(L), %% PS = ssh_bits:fill_bits(K - MLen - 2*?HLen - 2, 0), %% DB = <>, %% Seed = ssh_bits:random(?HLen), %% DbMask = ?MGF(Seed, K - ?HLen - 1), %% MaskedDB = ssh_bits:xor_bits(DB, DbMask), %% SeedMask = ?MGF(MaskedDB, ?HLen), %% MaskedSeed = ssh_bits:xor_bits(Seed, SeedMask), %% EM = <<16#00, MaskedSeed/binary, MaskedDB/binary>>, %% Mc = os2ip(EM), %% C = rsaep(Public, Mc), %% i2osp(C, K). %% rsaes_oaep_decrypt(Key, C) -> %% rsaes_oaep_decrypt(Key, C, <<>>). %% rsaes_oaep_decrypt(Private=#ssh_key { public={N,_},private={_,_}},Cb,L) -> %% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), %% K = (ssh_bits:isize(N)+7) div 8, %% ?ssh_assert(K == 2*?HLen + 2, decryption_error), %% C = os2ip(Cb), %% M = rsadp(Private, C), %% EM = i2osp(M, K), %% LHash = ?HASH(L), %% MLen = K - ?HLen -1, %% case EM of %% <<16#00, MaskedSeed:?HLen/binary, MaskedDB:MLen>> -> %% SeedMask = ?MGF(MaskedDB, ?HLen), %% Seed = ssh_bits:xor_bits(MaskedSeed, SeedMask), %% DbMask = ?MGF(Seed, K - ?HLen - 1), %% DB = ssh_bits:xor_bits(MaskedDB, DbMask), %% PSLen = K - MLen - 2*?HLen - 2, %% case DB of %% <> -> %% M; %% _ -> %% exit(decryption_error) %% end; %% _ -> %% exit(decryption_error) %% end. %% rsaes_pkcs1_v1_5_encrypt(Public=#ssh_key { public={N,_}}, M) -> %% K = (ssh_bits:isize(N)+7) div 8, %% MLen = size(M), %% ?ssh_assert(MLen =< K - 11, message_to_long), %% PS = ssh_bits:random(K - MLen - 3), %% EM = <<16#00,16#02,PS/binary,16#00,M/binary>>, %% Mc = os2ip(EM), %% C = rsaep(Public, Mc), %% i2osp(C, K). %% rsaes_pkcs1_v1_5_decrypt(Private=#ssh_key { public={N,_},private={_,_}}, %% Cb) -> %% K = (ssh_bits:isize(N)+7) div 8, %% CLen = size(Cb), %% ?ssh_assert(CLen == K andalso K >= 11, decryption_error), %% C = os2ip(Cb), %% M = rsadp(Private, C), %% EM = i2osp(M, K), %% PSLen = K - CLen - 3, %% case EM of %% <<16#00, 16#02, _PS:PSLen/binary, 16#00, M>> -> %% M; %% _ -> %% exit(decryption_error) %% end. %% rsassa_pss_sign(Private=#ssh_key { public={N,_},private={_,_}},Mb) -> %% ModBits = ssh_bits:isize(N), %% K = (ModBits+7) div 8, %% EM = emsa_pss_encode(Mb, ModBits - 1), %% M = os2ip(EM), %% S = rsasp1(Private, M), %% i2osp(S, K). %% rsassa_pss_verify(Public=#ssh_key { public={N,_E}},Mb,Sb) -> %% ModBits = ssh_bits:isize(N), %% K = (ModBits+7) div 8, %% ?ssh_assert(size(Sb) == K, invalid_signature), %% S = os2ip(Sb), %% M = rsavp1(Public,S), %% EMLen = (ModBits-1+7) div 8, %% EM = i2osp(M, EMLen), %% emsa_pss_verify(Mb, EM, ModBits-1). rsassa_pkcs1_v1_5_sign(Private=#ssh_key { public={N,_},private={_,_D}},Mb) -> K = (ssh_bits:isize(N)+7) div 8, EM = emsa_pkcs1_v1_5_encode(Mb, K), M = os2ip(EM), S = rsasp1(Private, M), i2osp(S, K). rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) -> K = (ssh_bits:isize(N)+7) div 8, ?ssh_assert(size(Sb) == K, invalid_signature), S = os2ip(Sb), M = rsavp1(Public, S), EM = i2osp(M, K), %?dbg(true, "verify K=~p S=~w ~n#M=~w~n#EM=~w~n", [K, S, M, EM]), case emsa_pkcs1_v1_5_encode(Mb, K) of EM -> ok; _S -> io:format("S: ~p~n", [_S]), {error, invalid_signature} % exit(invalid_signature) end. emsa_pkcs1_v1_5_encode(M, EMLen) -> H = ?HASH(M), %% Must use speical xxNull types here! Alg = #'AlgorithmNull' { algorithm = ?'id-sha1', parameters = <<>> }, {ok,TCode} = 'PKCS-1':encode('DigestInfoNull', #'DigestInfoNull'{ digestAlgorithm = Alg, digest = H }), T = list_to_binary(TCode), TLen = size(T), ?ssh_assert(EMLen >= TLen + 11, message_to_short), PS = ssh_bits:fill_bits(EMLen - TLen - 3, 16#ff), <<16#00, 16#01, PS/binary, 16#00, T/binary>>. %% emsa_pss_encode(M, EMBits) -> %% emsa_pss_encode(M, EMBits, 0). %% emsa_pss_encode(M, EMBits, SLen) -> %% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), %% EMLen = (EMBits + 7) div 8, %% MHash = ?HASH(M), %% ?ssh_assert(EMLen >= ?HLen + SLen + 2, encoding_error), %% Salt = ssh_bits:random(SLen), %% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, %% MHash, Salt], %% H = ?HASH(M1), %% PS = ssh_bits:fill_bits(EMLen-SLen-?HLen-2, 0), %% DB = <>, %% DbMask = ?MGF(H, EMLen - ?HLen -1), %% MaskedDB = ssh_bits:xor_bits(DB, DbMask), %% ZLen = 8*EMLen - EMBits, %% NZLen = (8 - (ZLen rem 8)) rem 8, %% <<_:ZLen, NZ:NZLen, MaskedDB1/binary>> = MaskedDB, %% MaskedDB2 = <<0:ZLen, NZ:NZLen, MaskedDB1/binary>>, %% <>. %% emsa_pss_verify(M, EM, EMBits) -> %% emsa_pss_verify(M, EM, EMBits, 0). %% emsa_pss_verify(M, EM, EMBits, SLen) -> %% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), %% EMLen = (EMBits + 7) div 8, %% MHash = ?HASH(M), %% ?ssh_assert(EMLen >= ?HLen + SLen + 2, inconsistent), %% MaskLen = (EMLen - ?HLen - 1)-1, %% ZLen = 8*EMLen - EMBits, %% NZLen = (8 - (ZLen rem 8)) rem 8, %% case EM of %% <<0:ZLen,Nz:NZLen,MaskedDB1:MaskLen/binary, H:?HLen/binary, 16#BC>> -> %% MaskedDB = <<0:ZLen,Nz:NZLen,MaskedDB1/binary>>, %% DbMask = ?MGF(H, EMLen - ?HLen - 1), %% DB = ssh_bits:xor_bits(MaskedDB, DbMask), %% PSLen1 = (EMLen - SLen - ?HLen - 2) - 1, %% PS = ssh_bits:fill_bits(PSLen1, 0), %% case DB of %% <<_:ZLen,0:NZLen,PS:PSLen1/binary,16#01,Salt:SLen/binary>> -> %% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, %% MHash, Salt], %% case ?HASH(M1) of %% H -> ok; %% _ -> exit(inconsistent) %% end; %% _ -> %% exit(inconsistent) %% end; %% _ -> %% exit(inconsistent) %% end. %% Mask generating function MGF1 %% mgf1(MGFSeed, MaskLen) -> %% T = mgf1_loop(0, ((MaskLen + ?HLen -1) div ?HLen) - 1, MGFSeed, ""), %% <> = T, %% R. %% mgf1_loop(Counter, N, _, T) when Counter > N -> %% list_to_binary(T); %% mgf1_loop(Counter, N, MGFSeed, T) -> %% C = i2osp(Counter, 4), %% mgf1_loop(Counter+1, N, MGFSeed, [T, ?HASH([MGFSeed, C])]). alg_name() -> "ssh-rsa".