Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jwt-plugin): Add support for EdDSA #12726

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/unreleased/kong/feat-jwt-eddsa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
message: |
Addded support for EdDSA algorithms in JWT plugin
type: feature
scope: Plugin
2 changes: 2 additions & 0 deletions kong/plugins/jwt/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ return {
"PS256",
"PS384",
"PS512",
"EdDSA",
},
}, },
{ tags = typedefs.tags },
Expand All @@ -55,6 +56,7 @@ return {
"^PS256$",
"^PS384$",
"^PS512$",
"^EdDSA$",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not directly related to the EdDSA support, however I wonder why the pkey checks are not applied for ES256, ES384 and ES512.

Wouldn't it make sense to apply this check for all algorithms and exclude the symmetric ones?

if_match = {
  not_match =  "^HS", 
},

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, this list has grown over time and indeed it would make sense to apply pkey checks to all asymmetrical keys. I'll add this to my pile. Thanks for brining that up @27ascii

}, },
},
then_field = "rsa_public_key",
Expand Down
12 changes: 11 additions & 1 deletion kong/plugins/jwt/jwt_parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ local alg_sign = {
return nil
end
return sig
end,
EdDSA = function(data, key)
local pkey = assert(openssl_pkey.new(key))
return assert(pkey:sign(data))
end
}

Expand Down Expand Up @@ -185,7 +189,13 @@ local alg_verify = {
assert(#signature == 256, "Signature must be 256 bytes")
return pkey:verify(signature, data, "sha512", openssl_pkey.PADDINGS.RSA_PKCS1_PSS_PADDING)
end,

EdDSA = function(data, signature, key)
-- Support of EdDSA alg typ according to RFC 8037
-- https://www.rfc-editor.org/rfc/rfc8037
local pkey, _ = openssl_pkey.new(key)
assert(pkey, "Consumer Public Key is Invalid")
return pkey:verify(signature, data)
end
}


Expand Down
24 changes: 24 additions & 0 deletions spec/03-plugins/16-jwt/01-jwt_parser_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,30 @@ describe("Plugin: jwt (parser)", function()
assert.True(jwt:verify_signature(fixtures.ps512_public_key))
end)

it("should encode using EdDSA with Ed25519 key", function()
local token = jwt_parser.encode({
sub = "5656565656",
name = "Jane Doe",
admin = true
}, fixtures.ed25519_private_key, 'EdDSA')

assert.truthy(token)
local jwt = assert(jwt_parser:new(token))
assert.True(jwt:verify_signature(fixtures.ed25519_public_key))
end)

it("should encode using EdDSA with Ed448 key", function()
local token = jwt_parser.encode({
sub = "5656565656",
name = "Jane Doe",
admin = true
}, fixtures.ed448_private_key, 'EdDSA')

assert.truthy(token)
local jwt = assert(jwt_parser:new(token))
assert.True(jwt:verify_signature(fixtures.ed448_public_key))
end)

end)
describe("Decoding", function()
it("throws an error if not given a string", function()
Expand Down
70 changes: 70 additions & 0 deletions spec/03-plugins/16-jwt/03-access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ for _, strategy in helpers.each_strategy() do
local rsa_jwt_secret_7
local rsa_jwt_secret_8
local rsa_jwt_secret_9
local rsa_jwt_secret_10
local rsa_jwt_secret_11
local hs_jwt_secret_1
local hs_jwt_secret_2
local proxy_client
Expand Down Expand Up @@ -74,6 +76,8 @@ for _, strategy in helpers.each_strategy() do
local consumer12 = consumers:insert({ username = "jwt_tests_rsa_consumer_12"})
local consumer13 = consumers:insert({ username = "jwt_tests_rsa_consumer_13"})
local consumer14 = consumers:insert({ username = "jwt_tests_rsa_consumer_14"})
local consumer15 = consumers:insert({ username = "jwt_tests_rsa_consumer_15"})
local consumer16 = consumers:insert({ username = "jwt_tests_rsa_consumer_16"})
local anonymous_user = consumers:insert({ username = "no-body" })

local plugins = bp.plugins
Expand Down Expand Up @@ -234,6 +238,18 @@ for _, strategy in helpers.each_strategy() do
rsa_public_key = fixtures.ps512_public_key
}

rsa_jwt_secret_10 = bp.jwt_secrets:insert {
consumer = { id = consumer15.id },
algorithm = "EdDSA",
rsa_public_key = fixtures.ed25519_public_key
}

rsa_jwt_secret_11 = bp.jwt_secrets:insert {
consumer = { id = consumer16.id },
algorithm = "EdDSA",
rsa_public_key = fixtures.ed448_public_key
}

hs_jwt_secret_1 = bp.jwt_secrets:insert {
consumer = { id = consumer7.id },
algorithm = "HS384",
Expand Down Expand Up @@ -973,6 +989,60 @@ for _, strategy in helpers.each_strategy() do
end)
end)

describe("EdDSA", function()
it("verifies JWT with Ed25519 key", function()
PAYLOAD.iss = rsa_jwt_secret_10.key
local jwt = jwt_encoder.encode(PAYLOAD, fixtures.ed25519_private_key, "EdDSA")
local authorization = "Bearer " .. jwt
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
["Authorization"] = authorization,
["Host"] = "jwt1.test",
}
})
local body = cjson.decode(assert.res_status(200, res))
assert.equal(authorization, body.headers.authorization)
assert.equal(rsa_jwt_secret_10.key, body.headers["x-credential-identifier"])
assert.equal(nil, body.headers["x-credential-username"])
end)
it("verifies JWT with Ed448 key", function()
PAYLOAD.iss = rsa_jwt_secret_11.key
local jwt = jwt_encoder.encode(PAYLOAD, fixtures.ed448_private_key, "EdDSA")
local authorization = "Bearer " .. jwt
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
["Authorization"] = authorization,
["Host"] = "jwt1.test",
}
})
local body = cjson.decode(assert.res_status(200, res))
assert.equal(authorization, body.headers.authorization)
assert.equal(rsa_jwt_secret_11.key, body.headers["x-credential-identifier"])
assert.equal(nil, body.headers["x-credential-username"])
end)
it("identifies Consumer", function()
PAYLOAD.iss = rsa_jwt_secret_10.key
local jwt = jwt_encoder.encode(PAYLOAD, fixtures.ed25519_private_key, "EdDSA")
local authorization = "Bearer " .. jwt
local res = assert(proxy_client:send {
method = "GET",
path = "/request",
headers = {
["Authorization"] = authorization,
["Host"] = "jwt1.test",
}
})
local body = cjson.decode(assert.res_status(200, res))
assert.equal(authorization, body.headers.authorization)
assert.equal("jwt_tests_rsa_consumer_15", body.headers["x-consumer-username"])
assert.equal(nil, body.headers["x-credential-username"])
end)
end)

describe("HS386", function()
it("proxies the request with token and consumer headers if it was verified", function()
PAYLOAD.iss = hs_jwt_secret_1.key
Expand Down
22 changes: 22 additions & 0 deletions spec/03-plugins/16-jwt/fixtures.lua
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,28 @@ Vv1PBULdZ0AMZzzBFW77zIA3kxthcBLB3C0N8mvPLjgfimyD4dK9j8v7lZoheCKC
5QIDAQAB
-----END PUBLIC KEY-----
]],
ed448_private_key = [[
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOV3hg//s9c2Ahjrhrf4Wz2u16RyZm7xKj9bTreD7z3Hr
ravo3fvLad9VY0eUjuhfplE7PJ8HVnInaw==
-----END PRIVATE KEY-----
]],
ed448_public_key = [[
-----BEGIN PUBLIC KEY-----
MEMwBQYDK2VxAzoAeFbeVK5Kv6jnE06XuaQk7aUCV+TjyyB1PI4cHWxCEuWZMHrw
+Q2jl6VsEZ1h792RxRE8E0OBJjmA
-----END PUBLIC KEY-----
]],
ed25519_private_key = [[
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIPojZUis9iVUYwbo+PMs7CeF294UmQqW417VNgaZ2AZ3
-----END PRIVATE KEY-----
]],
ed25519_public_key = [[
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAoJ7Hm7fVc7IQh6RqgR9+Dw0pvB0iqEaGXZex6FlwyGk=
-----END PUBLIC KEY-----
]],
hs384_secret = u([[
zxhk1H1Y11ax99xO20EGf00FDAOuPb9kEOmOQZMpR1BElx7sWjBIX2okAJiqjulH
OZpsjcgbzfCq69apm6f2K28PTvIvS8ni_CG46_huUTBqosCmdEr-kZDvKBLsppfG
Expand Down
Loading