You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2022/05/24 20:08:11 UTC
[couchdb] branch jwtf-es256-fail-erlang-20 created (now ba6d78d4a)
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a change to branch jwtf-es256-fail-erlang-20
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at ba6d78d4a Exclude jwtf EC algorithms on Erlang 20
This branch includes the following new commits:
new 793967191 Fix ES{256,384,512} support
new ba6d78d4a Exclude jwtf EC algorithms on Erlang 20
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[couchdb] 01/02: Fix ES{256,384,512} support
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch jwtf-es256-fail-erlang-20
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 793967191dab6056b4a2d3d831f4e76ec8870f3d
Author: Robert Newson <rn...@apache.org>
AuthorDate: Tue May 24 16:16:57 2022 +0100
Fix ES{256,384,512} support
DERp
---
src/jwtf/src/jwtf.erl | 39 +++++++++++++++++++++++--
src/jwtf/test/jwtf_tests.erl | 69 +++++++++++++++++++++++++++++++++++++++-----
2 files changed, 97 insertions(+), 11 deletions(-)
diff --git a/src/jwtf/src/jwtf.erl b/src/jwtf/src/jwtf.erl
index 2bb005bcb..f0a5bd6ca 100644
--- a/src/jwtf/src/jwtf.erl
+++ b/src/jwtf/src/jwtf.erl
@@ -27,6 +27,8 @@
verification_algorithm/1
]).
+-include_lib("public_key/include/public_key.hrl").
+
-define(ALGS, [
% RSA PKCS#1 signature with SHA-256
{<<"RS256">>, {public_key, sha256}},
@@ -70,7 +72,13 @@ encode(Header = {HeaderProps}, Claims, Key) ->
SignatureOrMac =
case verification_algorithm(Alg) of
{public_key, Algorithm} ->
- public_key:sign(Message, Algorithm, Key);
+ Signature = public_key:sign(Message, Algorithm, Key),
+ case Alg of
+ <<"ES", _/binary>> ->
+ der_to_jose(Alg, Signature);
+ _ ->
+ Signature
+ end;
{hmac, Algorithm} ->
hmac(Algorithm, Key, Message)
end,
@@ -268,12 +276,19 @@ key(Props, Checks, KS) ->
verify(Alg, Header, Payload, SignatureOrMac0, Key) ->
Message = <<Header/binary, $., Payload/binary>>,
SignatureOrMac1 = b64url:decode(SignatureOrMac0),
+ SignatureOrMac2 =
+ case Alg of
+ <<"ES", _/binary>> ->
+ jose_to_der(SignatureOrMac1);
+ _ ->
+ SignatureOrMac1
+ end,
{VerificationMethod, Algorithm} = verification_algorithm(Alg),
case VerificationMethod of
public_key ->
- public_key_verify(Algorithm, Message, SignatureOrMac1, Key);
+ public_key_verify(Algorithm, Message, SignatureOrMac2, Key);
hmac ->
- hmac_verify(Algorithm, Message, SignatureOrMac1, Key)
+ hmac_verify(Algorithm, Message, SignatureOrMac2, Key)
end.
public_key_verify(Algorithm, Message, Signature, PublicKey) ->
@@ -292,6 +307,24 @@ hmac_verify(Algorithm, Message, HMAC, SecretKey) ->
throw({bad_request, <<"Bad HMAC">>})
end.
+jose_to_der(Signature) ->
+ NumLen = 8 * byte_size(Signature) div 2,
+ <<R:NumLen, S:NumLen>> = Signature,
+ SigValue = #'ECDSA-Sig-Value'{r = R, s = S},
+ public_key:der_encode('ECDSA-Sig-Value', SigValue).
+
+der_to_jose(Alg, Signature) ->
+ #'ECDSA-Sig-Value'{r = R, s = S} = public_key:der_decode('ECDSA-Sig-Value', Signature),
+ Len = rs_len(Alg),
+ <<R:Len, S:Len>>.
+
+rs_len(<<"ES256">>) ->
+ 256;
+rs_len(<<"ES384">>) ->
+ 384;
+rs_len(<<"ES512">>) ->
+ 512.
+
split(EncodedToken) ->
case binary:split(EncodedToken, <<$.>>, [global]) of
[_, _, _] = Split -> Split;
diff --git a/src/jwtf/test/jwtf_tests.erl b/src/jwtf/test/jwtf_tests.erl
index e36ecbd23..b0b9f1fab 100644
--- a/src/jwtf/test/jwtf_tests.erl
+++ b/src/jwtf/test/jwtf_tests.erl
@@ -24,7 +24,7 @@ encode(Header0, Payload0) ->
valid_header() ->
{[{<<"typ">>, <<"JWT">>}, {<<"alg">>, <<"RS256">>}]}.
-jwt_io_pubkey() ->
+jwt_io_rsa_pubkey() ->
PublicKeyPEM = <<
"-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGH"
@@ -36,6 +36,16 @@ jwt_io_pubkey() ->
[PEMEntry] = public_key:pem_decode(PublicKeyPEM),
public_key:pem_entry_decode(PEMEntry).
+jwt_io_ec_pubkey() ->
+ PublicKeyPEM = <<
+ "-----BEGIN PUBLIC KEY-----\n"
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9"
+ "q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n"
+ "-----END PUBLIC KEY-----\n"
+ >>,
+ [PEMEntry] = public_key:pem_decode(PublicKeyPEM),
+ public_key:pem_entry_decode(PEMEntry).
+
b64_badarg_test() ->
Encoded = <<"0.0.0">>,
?assertEqual(
@@ -169,7 +179,7 @@ bad_rs256_sig_test() ->
{[{<<"typ">>, <<"JWT">>}, {<<"alg">>, <<"RS256">>}]},
{[]}
),
- KS = fun(<<"RS256">>, undefined) -> jwt_io_pubkey() end,
+ KS = fun(<<"RS256">>, undefined) -> jwt_io_rsa_pubkey() end,
?assertEqual(
{error, {bad_request, <<"Bad signature">>}},
jwtf:decode(Encoded, [], KS)
@@ -264,7 +274,28 @@ rs256_test() ->
>>,
Checks = [sig, alg],
- KS = fun(<<"RS256">>, undefined) -> jwt_io_pubkey() end,
+ KS = fun(<<"RS256">>, undefined) -> jwt_io_rsa_pubkey() end,
+
+ ExpectedPayload =
+ {[
+ {<<"sub">>, <<"1234567890">>},
+ {<<"name">>, <<"John Doe">>},
+ {<<"admin">>, true}
+ ]},
+
+ ?assertMatch({ok, ExpectedPayload}, jwtf:decode(EncodedToken, Checks, KS)).
+
+%% jwt.io generated
+es256_test() ->
+ EncodedToken = <<
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0N"
+ "TY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.1g"
+ "LptYop2guxSZHmf0ga292suPxwBdkijA1ZopCSSYLBdEl8Bg2fsxoU"
+ "cZuSGztMU9qAKV2p80NQn8czeGhHXA"
+ >>,
+
+ Checks = [sig, alg],
+ KS = fun(<<"ES256">>, undefined) -> jwt_io_ec_pubkey() end,
ExpectedPayload =
{[
@@ -292,10 +323,12 @@ encode_decode_test_() ->
encode_decode(Alg) ->
{EncodeKey, DecodeKey} =
- case jwtf:verification_algorithm(Alg) of
- {public_key, _Algorithm} ->
- create_keypair();
- {hmac, _Algorithm} ->
+ case Alg of
+ <<"RS", _/binary>> ->
+ create_rsa_keypair();
+ <<"ES", _/binary>> ->
+ create_ec_keypair();
+ <<"HS", _/binary>> ->
Key = <<"a-super-secret-key">>,
{Key, Key}
end,
@@ -319,7 +352,7 @@ claims() ->
{<<"exp">>, EpochSeconds + 3600}
]}.
-create_keypair() ->
+create_rsa_keypair() ->
%% https://tools.ietf.org/html/rfc7517#appendix-C
N = decode(<<
"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy"
@@ -349,6 +382,26 @@ create_keypair() ->
},
{RSAPrivateKey, RSAPublicKey}.
+create_ec_keypair() ->
+ PublicPEM = <<"-----BEGIN PUBLIC KEY-----\n"
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9"
+ "q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n"
+ "-----END PUBLIC KEY-----">>,
+ PrivatePEM = <<"-----BEGIN PRIVATE KEY-----\n"
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2"
+ "OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r"
+ "1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G\n"
+ "-----END PRIVATE KEY-----">>,
+
+ [PublicEntry] = public_key:pem_decode(PublicPEM),
+ ECPublicKey = public_key:pem_entry_decode(PublicEntry),
+
+ [PrivateEntry] = public_key:pem_decode(PrivatePEM),
+ ECPrivateKey = public_key:pem_entry_decode(PrivateEntry),
+
+ {ECPrivateKey, ECPublicKey}.
+
+
decode(Goop) ->
crypto:bytes_to_integer(b64url:decode(Goop)).
[couchdb] 02/02: Exclude jwtf EC algorithms on Erlang 20
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch jwtf-es256-fail-erlang-20
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit ba6d78d4a675988f96a27981f889e9108eefc1d6
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Tue May 24 16:06:34 2022 -0400
Exclude jwtf EC algorithms on Erlang 20
---
src/jwtf/src/jwtf.erl | 17 +++++++++++++++++
src/jwtf/test/jwtf_tests.erl | 4 ++++
2 files changed, 21 insertions(+)
diff --git a/src/jwtf/src/jwtf.erl b/src/jwtf/src/jwtf.erl
index f0a5bd6ca..e45a1ade1 100644
--- a/src/jwtf/src/jwtf.erl
+++ b/src/jwtf/src/jwtf.erl
@@ -29,6 +29,9 @@
-include_lib("public_key/include/public_key.hrl").
+
+-ifdef(OTP_RELEASE).
+
-define(ALGS, [
% RSA PKCS#1 signature with SHA-256
{<<"RS256">>, {public_key, sha256}},
@@ -42,6 +45,20 @@
{<<"HS512">>, {hmac, sha512}}
]).
+-else.
+
+-define(ALGS, [
+ % RSA PKCS#1 signature with SHA-256
+ {<<"RS256">>, {public_key, sha256}},
+ {<<"RS384">>, {public_key, sha384}},
+ {<<"RS512">>, {public_key, sha512}},
+ {<<"HS256">>, {hmac, sha256}},
+ {<<"HS384">>, {hmac, sha384}},
+ {<<"HS512">>, {hmac, sha512}}
+]).
+
+-endif.
+
-define(CHECKS, [
alg,
exp,
diff --git a/src/jwtf/test/jwtf_tests.erl b/src/jwtf/test/jwtf_tests.erl
index b0b9f1fab..57fb9ac8d 100644
--- a/src/jwtf/test/jwtf_tests.erl
+++ b/src/jwtf/test/jwtf_tests.erl
@@ -285,6 +285,8 @@ rs256_test() ->
?assertMatch({ok, ExpectedPayload}, jwtf:decode(EncodedToken, Checks, KS)).
+
+-ifdef(OTP_RELEASE).
%% jwt.io generated
es256_test() ->
EncodedToken = <<
@@ -306,6 +308,8 @@ es256_test() ->
?assertMatch({ok, ExpectedPayload}, jwtf:decode(EncodedToken, Checks, KS)).
+-endif.
+
encode_missing_alg_test() ->
?assertEqual(
{error, {bad_request, <<"Missing alg header parameter">>}},