aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/lib/jwt/src/jwt_ecdsa.erl
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/_build/default/lib/jwt/src/jwt_ecdsa.erl
i vibe coded itHEADmaster
Diffstat (limited to 'server/_build/default/lib/jwt/src/jwt_ecdsa.erl')
-rw-r--r--server/_build/default/lib/jwt/src/jwt_ecdsa.erl76
1 files changed, 76 insertions, 0 deletions
diff --git a/server/_build/default/lib/jwt/src/jwt_ecdsa.erl b/server/_build/default/lib/jwt/src/jwt_ecdsa.erl
new file mode 100644
index 0000000..7596e90
--- /dev/null
+++ b/server/_build/default/lib/jwt/src/jwt_ecdsa.erl
@@ -0,0 +1,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).