1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
%% @doc Eliptic curve digital signature algorithm
%%
%% Helper functions for encoding/decoding ECDSA signature
%%
%% @end
-module(jwt_ecdsa).
-include_lib("jwt_ecdsa.hrl").
-include_lib("public_key/include/public_key.hrl").
-export([
signature/1,
signature/3
]).
%% @doc Signature for JWT verification
%%
%% Transcode the ECDSA Base64-encoded signature into ASN.1/DER format
%%
%% @end
signature(Base64Sig) ->
Signature = base64url:decode(Base64Sig),
SignatureLen = byte_size(Signature),
{RBin, SBin} = split_binary(Signature, (SignatureLen div 2)),
R = crypto:bytes_to_integer(RBin),
S = crypto:bytes_to_integer(SBin),
public_key:der_encode('ECDSA-Sig-Value', #'ECDSA-Sig-Value'{ r = R, s = S }).
%% @doc Signature to sign JWT
%%
%% Transcodes the JCA ASN.1/DER-encoded signature into the concatenated R + S format
%% a.k.a <em>raw</em> format
%%
%% @end
signature(Payload, Crypto, Key) ->
Der = public_key:sign(Payload, Crypto, Key),
raw(Der, Key).
raw(Der, #'ECPrivateKey'{parameters = {namedCurve, NamedCurve}}) ->
#'ECDSA-Sig-Value'{ r = R, s = S } = public_key:der_decode('ECDSA-Sig-Value', Der),
CurveName = pubkey_cert_records:namedCurves(NamedCurve),
GroupDegree = group_degree(CurveName),
Size = (GroupDegree + 7) div 8,
RBin = int_to_bin(R),
SBin = int_to_bin(S),
RPad = pad(RBin, Size),
SPad = pad(SBin, Size),
<<RPad/binary, SPad/binary>>.
%% @private
int_to_bin(X) when X < 0 ->
int_to_bin_neg(X, []);
int_to_bin(X) ->
int_to_bin_pos(X, []).
%% @private
int_to_bin_pos(0, Ds = [_|_]) ->
list_to_binary(Ds);
int_to_bin_pos(X, Ds) ->
int_to_bin_pos(X bsr 8, [(X band 255)|Ds]).
%% @private
int_to_bin_neg(-1, Ds = [MSB|_]) when MSB >= 16#80 ->
list_to_binary(Ds);
int_to_bin_neg(X, Ds) ->
int_to_bin_neg(X bsr 8, [(X band 255)|Ds]).
%% @private
pad(Bin, Size) when byte_size(Bin) =:= Size ->
Bin;
pad(Bin, Size) when byte_size(Bin) < Size ->
pad(<<0, Bin/binary>>, Size).
%% See the OpenSSL documentation for EC_GROUP_get_degree()
group_degree(CurveName) ->
maps:get(CurveName, ?EC_GROUP_DEGREE).
|