snmp_usm.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 360 行
ERL
360 行
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-module(snmp_usm).-export([passwd2localized_key/3, localize_key/3]).-export([auth_in/4, auth_out/4, set_msg_auth_params/3]).-export([des_encrypt/3, des_decrypt/3]).-export([aes_encrypt/3, aes_decrypt/5]).-define(SNMP_USE_V3, true).-include("snmp_types.hrl").-include("SNMP-USER-BASED-SM-MIB.hrl").-include("SNMP-USM-AES-MIB.hrl").-define(VMODULE,"USM").-include("snmp_verbosity.hrl").%%------------------------------------------------------------------define(twelwe_zeros, [0,0,0,0,0,0,0,0,0,0,0,0]).-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).%%-----------------------------------------------------------------%% Func: passwd2localized_key/3%% Types: Alg = md5 | sha%% Passwd = string()%% EngineID = string()%% Purpose: Generates a key that can be used as an authentication%% or privacy key using MD5 och SHA. The key is%% localized for EngineID.%% The algorithm is described in appendix A.1 2) of%% rfc2274.%%-----------------------------------------------------------------passwd2localized_key(Alg, Passwd, EngineID) when length(Passwd) > 0 -> Key = mk_digest(Alg, Passwd), localize_key(Alg, Key, EngineID). %%-----------------------------------------------------------------%% Func: localize_key/3%% Types: Alg = md5 | sha%% Passwd = string()%% EngineID = string()%% Purpose: Localizes an unlocalized key for EngineID. See rfc2274%% section 2.6 for a definition of localized keys.%%-----------------------------------------------------------------localize_key(Alg, Key, EngineID) -> Str = [Key, EngineID, Key], binary_to_list(crypto:Alg(Str)).mk_digest(md5, Passwd) -> mk_md5_digest(Passwd);mk_digest(sha, Passwd) -> mk_sha_digest(Passwd).mk_md5_digest(Passwd) -> Ctx = crypto:md5_init(), Ctx2 = md5_loop(0, [], Ctx, Passwd, length(Passwd)), crypto:md5_final(Ctx2).md5_loop(Count, Buf, Ctx, Passwd, PasswdLen) when Count < 1048576 -> {Buf64, NBuf} = mk_buf64(length(Buf), Buf, Passwd, PasswdLen), NCtx = crypto:md5_update(Ctx, Buf64), md5_loop(Count+64, NBuf, NCtx, Passwd, PasswdLen);md5_loop(_Count, _Buf, Ctx, _Passwd, _PasswdLen) -> Ctx.mk_sha_digest(Passwd) -> Ctx = crypto:sha_init(), Ctx2 = sha_loop(0, [], Ctx, Passwd, length(Passwd)), crypto:sha_final(Ctx2).sha_loop(Count, Buf, Ctx, Passwd, PasswdLen) when Count < 1048576 -> {Buf64, NBuf} = mk_buf64(length(Buf), Buf, Passwd, PasswdLen), NCtx = crypto:sha_update(Ctx, Buf64), sha_loop(Count+64, NBuf, NCtx, Passwd, PasswdLen);sha_loop(_Count, _Buf, Ctx, _Passwd, _PasswdLen) -> Ctx.%% Create a 64 bytes long string, by repeating Passwd as many times %% as necessary. Output is the 64 byte string, and the rest of the %% last repetition of the Passwd. This is used as input in the next%% invocation.mk_buf64(BufLen, Buf, Passwd, PasswdLen) -> case BufLen + PasswdLen of TotLen when TotLen > 64 -> {[Buf, lists:sublist(Passwd, 64-BufLen)], lists:sublist(Passwd, 65-BufLen, PasswdLen)}; TotLen -> mk_buf64(TotLen, [Buf, Passwd], Passwd, PasswdLen) end.%%-----------------------------------------------------------------%% Auth and priv algorithms%%-----------------------------------------------------------------auth_in(usmHMACMD5AuthProtocol, AuthKey, AuthParams, Packet) -> md5_auth_in(AuthKey, AuthParams, Packet);auth_in(?usmHMACMD5AuthProtocol, AuthKey, AuthParams, Packet) -> md5_auth_in(AuthKey, AuthParams, Packet);auth_in(usmHMACSHAAuthProtocol, AuthKey, AuthParams, Packet) -> sha_auth_in(AuthKey, AuthParams, Packet);auth_in(?usmHMACSHAAuthProtocol, AuthKey, AuthParams, Packet) -> sha_auth_in(AuthKey, AuthParams, Packet).auth_out(usmNoAuthProtocol, _AuthKey, _Message, _UsmSecParams) -> % 3.1.3 error(unSupportedSecurityLevel);auth_out(?usmNoAuthProtocol, _AuthKey, _Message, _UsmSecParams) -> % 3.1.3 error(unSupportedSecurityLevel);auth_out(usmHMACMD5AuthProtocol, AuthKey, Message, UsmSecParams) -> md5_auth_out(AuthKey, Message, UsmSecParams);auth_out(?usmHMACMD5AuthProtocol, AuthKey, Message, UsmSecParams) -> md5_auth_out(AuthKey, Message, UsmSecParams);auth_out(usmHMACSHAAuthProtocol, AuthKey, Message, UsmSecParams) -> sha_auth_out(AuthKey, Message, UsmSecParams);auth_out(?usmHMACSHAAuthProtocol, AuthKey, Message, UsmSecParams) -> sha_auth_out(AuthKey, Message, UsmSecParams).md5_auth_out(AuthKey, Message, UsmSecParams) -> %% 6.3.1.1 Message2 = set_msg_auth_params(Message, UsmSecParams, ?twelwe_zeros), Packet = snmp_pdus:enc_message_only(Message2), %% 6.3.1.2-4 is done by the crypto function %% 6.3.1.4 MAC = binary_to_list(crypto:md5_mac_96(AuthKey, Packet)), %% 6.3.1.5 set_msg_auth_params(Message, UsmSecParams, MAC).md5_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) == 12 -> %% 6.3.2.3 Packet2 = patch_packet(binary_to_list(Packet)), %% 6.3.2.5 MAC = binary_to_list(crypto:md5_mac_96(AuthKey, Packet2)), %% 6.3.2.6 MAC == AuthParams;md5_auth_in(_AuthKey, _AuthParams, _Packet) -> %% 6.3.2.1 ?vtrace("md5_auth_in -> entry with" "~n _AuthKey: ~p" "~n _AuthParams: ~p", [_AuthKey, _AuthParams]), false.sha_auth_out(AuthKey, Message, UsmSecParams) -> %% 7.3.1.1 Message2 = set_msg_auth_params(Message, UsmSecParams, ?twelwe_zeros), Packet = snmp_pdus:enc_message_only(Message2), %% 7.3.1.2-4 is done by the crypto function %% 7.3.1.4 MAC = binary_to_list(crypto:sha_mac_96(AuthKey, Packet)), %% 7.3.1.5 set_msg_auth_params(Message, UsmSecParams, MAC).sha_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) == 12 -> %% 7.3.2.3 Packet2 = patch_packet(binary_to_list(Packet)), %% 7.3.2.5 MAC = binary_to_list(crypto:sha_mac_96(AuthKey, Packet2)), %% 7.3.2.6 MAC == AuthParams;sha_auth_in(_AuthKey, _AuthParams, _Packet) -> %% 7.3.2.1 ?vtrace("sha_auth_in -> entry with" "~n _AuthKey: ~p" "~n _AuthParams: ~p", [_AuthKey, _AuthParams]), false.des_encrypt(PrivKey, Data, SaltFun) -> [A,B,C,D,E,F,G,H | PreIV] = PrivKey, DesKey = [A,B,C,D,E,F,G,H], Salt = SaltFun(), IV = snmp_misc:str_xor(PreIV, Salt), TailLen = (8 - (length(Data) rem 8)) rem 8, Tail = mk_tail(TailLen), EncData = crypto:des_cbc_encrypt(DesKey, IV, [Data,Tail]), {ok, binary_to_list(EncData), Salt}.des_decrypt(PrivKey, MsgPrivParams, EncData) when length(MsgPrivParams) == 8 -> [A,B,C,D,E,F,G,H | PreIV] = PrivKey, DesKey = [A,B,C,D,E,F,G,H], Salt = MsgPrivParams, IV = snmp_misc:str_xor(PreIV, Salt), %% Whatabout errors here??? E.g. not a mulitple of 8! Data = binary_to_list(crypto:des_cbc_decrypt(DesKey, IV, EncData)), Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data), {ok, Data2}.aes_encrypt(PrivKey, Data, SaltFun) -> AesKey = PrivKey, Salt = SaltFun(), EngineBoots = snmp_framework_mib:get_engine_boots(), EngineTime = snmp_framework_mib:get_engine_time(), IV = [?i32(EngineBoots), ?i32(EngineTime) | Salt], EncData = crypto:aes_cfb_128_encrypt(AesKey, IV, Data), {ok, binary_to_list(EncData), Salt}.aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime) when length(MsgPrivParams) == 8 -> AesKey = PrivKey, Salt = MsgPrivParams, IV = [?i32(EngineBoots), ?i32(EngineTime) | Salt], %% Whatabout errors here??? E.g. not a mulitple of 8! Data = binary_to_list(crypto:aes_cfb_128_decrypt(AesKey, IV, EncData)), Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data), {ok, Data2}.%%-----------------------------------------------------------------%% Utility functions%%-----------------------------------------------------------------mk_tail(N) when N > 0 -> [0 | mk_tail(N-1)];mk_tail(0) -> [].set_msg_auth_params(Message, UsmSecParams, AuthParams) -> NUsmSecParams = UsmSecParams#usmSecurityParameters{msgAuthenticationParameters = AuthParams}, SecBytes = snmp_pdus:enc_usm_security_parameters(NUsmSecParams), VsnHdr = Message#message.vsn_hdr, NVsnHdr = VsnHdr#v3_hdr{msgSecurityParameters = SecBytes}, Message#message{vsn_hdr = NVsnHdr}.%% Not very nice...%% This function patches the asn.1 encoded message. It changes the%% AuthenticationParameters to 12 zeros.%% NOTE: returns a deep list of bytespatch_packet([48 | T]) -> %% Length for whole packet - 2 is tag for version {Len1, [2 | T1]} = split_len(T), %% Length for version - 48 is tag for header data {Len2, [Vsn,48|T2]} = split_len(T1), %% Length for header data {Len3, T3} = split_len(T2), [48,Len1,2,Len2,Vsn,48,Len3|pp2(dec_len(Len3),T3)].%% Skip HeaderData - 4 is tag for SecurityParameterspp2(0,[4|T]) -> %% 48 is tag for UsmSecParams {Len1,[48|T1]} = split_len(T), %% 4 is tag for EngineID {Len2,[4|T2]} = split_len(T1), %% Len 3 is length for EngineID {Len3,T3} = split_len(T2), [4,Len1,48,Len2,4,Len3|pp3(dec_len(Len3),T3)];pp2(N,[H|T]) -> [H|pp2(N-1,T)].%% Skip EngineID - 2 is tag for EngineBootspp3(0,[2|T]) -> {Len1,T1} = split_len(T), [2,Len1|pp4(dec_len(Len1),T1)];pp3(N,[H|T]) -> [H|pp3(N-1,T)].%% Skip EngineBoots - 2 is tag for EngineTimepp4(0,[2|T]) -> {Len1,T1} = split_len(T), [2,Len1|pp5(dec_len(Len1),T1)];pp4(N,[H|T]) -> [H|pp4(N-1,T)].%% Skip EngineTime - 4 is tag for UserName pp5(0,[4|T]) -> {Len1,T1} = split_len(T), [4,Len1|pp6(dec_len(Len1),T1)];pp5(N,[H|T]) -> [H|pp5(N-1,T)].%% Skip UserName - 4 is tag for AuthenticationParameters%% This is what we're looking for!pp6(0,[4|T]) -> {Len1,[_,_,_,_,_,_,_,_,_,_,_,_|T1]} = split_len(T), 12 == dec_len(Len1), [4,Len1,?twelwe_zeros|T1];pp6(N,[H|T]) -> [H|pp6(N-1,T)].%% Returns {LengthOctets, Rest}split_len([Hd|Tl]) -> %% definite form case is8set(Hd) of 0 -> % Short form {Hd,Tl}; 1 -> % Long form - at least one more octet No = clear(Hd, 8), {DigList,Rest} = head(No,Tl), {[Hd | DigList], Rest} end.dec_len(D) when integer(D) -> D;dec_len([_LongOctet|T]) -> dl(T).dl([D]) -> D;dl([A,B]) -> (A bsl 8) bor B;dl([A,B,C]) -> (A bsl 16) bor (B bsl 8) bor C;dl([0 | T]) -> dl(T).head(L,List) when length(List) == L -> {List,[]};head(L,List) -> head(L,List,[]).head(0,L,Res) -> {lists:reverse(Res),L};head(Int,[H|Tail],Res) -> head(Int-1,Tail,[H|Res]).clear(Byte, 8) -> Byte band 127.%% clear(Byte,Pos) when Pos < 9 ->%% Mask = bnot bset(0,Pos),%% Mask band Byte.%% bset(Byte, 8) -> %% Byte bor 2#10000000;%% bset(Byte, Pos) when (Pos < 9) ->%% Mask = 1 bsl (Pos-1),%% Byte bor Mask.is8set(Byte) -> if Byte > 127 -> 1; true -> 0 end.error(Reason) -> throw({error, Reason}).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?