Merge pull request #2353 from triska/hmac_verification
ENHANCED: Safe HMAC verification, using constant time string comparison.
This commit is contained in:
@@ -91,9 +91,36 @@ bytes_hex([B|Bs]) --> [C0,C1],
|
|||||||
},
|
},
|
||||||
bytes_hex(Bs).
|
bytes_hex(Bs).
|
||||||
|
|
||||||
|
char_hexval(C, H) :-
|
||||||
|
integer(H),
|
||||||
|
!,
|
||||||
|
hexval_char(H, C).
|
||||||
char_hexval(C, H) :- nth0(H, "0123456789abcdef", C), !.
|
char_hexval(C, H) :- nth0(H, "0123456789abcdef", C), !.
|
||||||
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
|
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
We specialize char_hexval/2 for use if the value is given,
|
||||||
|
so that it works in constant time in this case.
|
||||||
|
|
||||||
|
The security of HMAC verification depends on this property.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
hexval_char(0, '0').
|
||||||
|
hexval_char(1, '1').
|
||||||
|
hexval_char(2, '2').
|
||||||
|
hexval_char(3, '3').
|
||||||
|
hexval_char(4, '4').
|
||||||
|
hexval_char(5, '5').
|
||||||
|
hexval_char(6, '6').
|
||||||
|
hexval_char(7, '7').
|
||||||
|
hexval_char(8, '8').
|
||||||
|
hexval_char(9, '9').
|
||||||
|
hexval_char(0xa, a).
|
||||||
|
hexval_char(0xb, b).
|
||||||
|
hexval_char(0xc, c).
|
||||||
|
hexval_char(0xd, d).
|
||||||
|
hexval_char(0xe, e).
|
||||||
|
hexval_char(0xf, f).
|
||||||
|
|
||||||
must_be_bytes(Bytes, Context) :-
|
must_be_bytes(Bytes, Context) :-
|
||||||
must_be(list, Bytes),
|
must_be(list, Bytes),
|
||||||
@@ -195,7 +222,13 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
|||||||
% - `hmac(+Key)`
|
% - `hmac(+Key)`
|
||||||
% Compute a hash-based message authentication code (HMAC) using
|
% Compute a hash-based message authentication code (HMAC) using
|
||||||
% Key, a list of bytes. This option is currently supported for
|
% Key, a list of bytes. This option is currently supported for
|
||||||
% algorithms `sha256`, `sha384` and `sha512`.
|
% algorithms `sha256`, `sha384` and `sha512`. If `Hash` is
|
||||||
|
% instantiated, then it is compared with the computed HMAC
|
||||||
|
% in such a way that no information about the expected HMAC
|
||||||
|
% is revealed, using a comparison of strings that always takes
|
||||||
|
% the same time independent of whether and where the strings
|
||||||
|
% differ. This option can therefore also be used to safely
|
||||||
|
% _verify_ a given HMAC.
|
||||||
%
|
%
|
||||||
% Example:
|
% Example:
|
||||||
%
|
%
|
||||||
@@ -222,10 +255,26 @@ crypto_data_hash(Data0, Hash, Options0) :-
|
|||||||
( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
|
( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
|
||||||
must_be_bytes(Ks, crypto_data_hash/3),
|
must_be_bytes(Ks, crypto_data_hash/3),
|
||||||
hmac_algorithm(A),
|
hmac_algorithm(A),
|
||||||
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A)
|
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A),
|
||||||
; '$crypto_data_hash'(Data, Encoding, HashBytes, A)
|
( var(Hash) ->
|
||||||
),
|
hex_bytes(Hash, HashBytes)
|
||||||
hex_bytes(Hash, HashBytes).
|
; must_be(chars, Hash),
|
||||||
|
hex_bytes(HashMAC, HashBytes),
|
||||||
|
chars_equal_constant_time(Hash, HashMAC)
|
||||||
|
)
|
||||||
|
; '$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||||
|
hex_bytes(Hash, HashBytes)
|
||||||
|
).
|
||||||
|
|
||||||
|
chars_equal_constant_time(As, Bs) :-
|
||||||
|
maplist(chars_xor, As, Bs, Xs),
|
||||||
|
sum_list(Xs, Sum),
|
||||||
|
Sum =:= 0.
|
||||||
|
|
||||||
|
chars_xor(A, B, Xor) :-
|
||||||
|
char_code(A, CA),
|
||||||
|
char_code(B, CB),
|
||||||
|
Xor is xor(CA,CB).
|
||||||
|
|
||||||
hmac_algorithm(sha256).
|
hmac_algorithm(sha256).
|
||||||
hmac_algorithm(sha384).
|
hmac_algorithm(sha384).
|
||||||
|
|||||||
Reference in New Issue
Block a user