From be622efcfba03025353135a94b47d9e7245ac0fe Mon Sep 17 00:00:00 2001 From: Joshua Schmid Date: Wed, 8 Mar 2023 08:18:36 -0800 Subject: [PATCH] fix(jwt): offload ECDSA signature format conversion before this patch we transformed signature formats in the `jwt_parser` module to the specified signature format See The RFC for more details: https://www.rfc-editor.org/rfc/rfc7518#section-3.4 `lua-resty-openssl` 0.8.18 can do do this directly. Fixes: https://konghq.atlassian.net/browse/KAG-844 Signed-off-by: Joshua Schmid --- kong-3.2.1-0.rockspec | 2 +- kong/plugins/jwt/jwt_parser.lua | 59 ++++++++++++++------------------- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/kong-3.2.1-0.rockspec b/kong-3.2.1-0.rockspec index 573fad220f43..aef54d1bbbca 100644 --- a/kong-3.2.1-0.rockspec +++ b/kong-3.2.1-0.rockspec @@ -36,7 +36,7 @@ dependencies = { "lua-resty-healthcheck == 1.6.2", "lua-resty-mlcache == 2.6.0", "lua-messagepack == 0.5.2", - "lua-resty-openssl == 0.8.17", + "lua-resty-openssl == 0.8.18", "lua-resty-counter == 0.2.1", "lua-resty-ipmatcher == 0.6.1", "lua-resty-acme == 0.10.1", diff --git a/kong/plugins/jwt/jwt_parser.lua b/kong/plugins/jwt/jwt_parser.lua index a289358f0774..5552f2e3b4bb 100644 --- a/kong/plugins/jwt/jwt_parser.lua +++ b/kong/plugins/jwt/jwt_parser.lua @@ -53,29 +53,19 @@ local alg_sign = { end, ES256 = function(data, key) local pkey = openssl_pkey.new(key) - local digest = openssl_digest.new("sha256") - assert(digest:update(data)) - local signature = assert(pkey:sign(digest)) - - local derSequence = asn_sequence.parse_simple_sequence(signature) - local r = asn_sequence.unsign_integer(derSequence[1], 32) - local s = asn_sequence.unsign_integer(derSequence[2], 32) - assert(#r == 32) - assert(#s == 32) - return r .. s + local sig = assert(pkey:sign(data, "sha256", nil, { ecdsa_use_raw = true })) + if not sig then + return nil + end + return sig end, ES384 = function(data, key) local pkey = openssl_pkey.new(key) - local digest = openssl_digest.new("sha384") - assert(digest:update(data)) - local signature = assert(pkey:sign(digest)) - - local derSequence = asn_sequence.parse_simple_sequence(signature) - local r = asn_sequence.unsign_integer(derSequence[1], 48) - local s = asn_sequence.unsign_integer(derSequence[2], 48) - assert(#r == 48) - assert(#s == 48) - return r .. s + local sig = assert(pkey:sign(data, "sha384", nil, { ecdsa_use_raw = true })) + if not sig then + return nil + end + return sig end } @@ -106,28 +96,29 @@ local alg_verify = { assert(digest:update(data)) return pkey:verify(signature, digest) end, + -- https://www.rfc-editor.org/rfc/rfc7518#section-3.4 ES256 = function(data, signature, key) local pkey, _ = openssl_pkey.new(key) assert(pkey, "Consumer Public Key is Invalid") + -- The ECDSA P-256 SHA-256 digital signature for a JWS is validated as + -- follows: + + -- 1. The JWS Signature value MUST be a 64-octet sequence. If it is + -- not a 64-octet sequence, the validation has failed. assert(#signature == 64, "Signature must be 64 bytes.") - local asn = {} - asn[1] = asn_sequence.resign_integer(sub(signature, 1, 32)) - asn[2] = asn_sequence.resign_integer(sub(signature, 33, 64)) - local signatureAsn = asn_sequence.create_simple_sequence(asn) - local digest = openssl_digest.new("sha256") - assert(digest:update(data)) - return pkey:verify(signatureAsn, digest) + return pkey:verify(signature, data, "sha256", nil, { ecdsa_use_raw = true }) end, ES384 = function(data, signature, key) + -- Signing and validation with the ECDSA P-384 SHA-384 and ECDSA P-521 + -- SHA-512 algorithms is performed identically to the procedure for + -- ECDSA P-256 SHA-256 -- just using the corresponding hash algorithms + -- with correspondingly larger result values. For ECDSA P-384 SHA-384, + -- R and S will be 384 bits each, resulting in a 96-octet sequence. For + -- ECDSA P-521 SHA-512, R and S will be 521 bits each, resulting in a + -- 132-octet sequence. local pkey, _ = openssl_pkey.new(key) assert(#signature == 96, "Signature must be 96 bytes.") - local asn = {} - asn[1] = asn_sequence.resign_integer(sub(signature, 1, 48)) - asn[2] = asn_sequence.resign_integer(sub(signature, 49, 96)) - local signatureAsn = asn_sequence.create_simple_sequence(asn) - local digest = openssl_digest.new("sha384") - assert(digest:update(data)) - return pkey:verify(signatureAsn, digest) + return pkey:verify(signature, data, "sha384", nil, { ecdsa_use_raw = true }) end }