Initial commit with JWK parsing implementation.

This commit is contained in:
2021-11-22 15:33:59 -06:00
commit 0c53843e9b
16 changed files with 21457 additions and 0 deletions

View File

@ -0,0 +1,2 @@
switch("path", "../../main")
switch("verbosity", "0")

3
src/test/unit/runner.nim Normal file
View File

@ -0,0 +1,3 @@
import std/unittest
import ./tjwk

217
src/test/unit/tjwk.nim Normal file
View File

@ -0,0 +1,217 @@
import std/json, std/options, std/unittest
import jwt/jwk
suite "jwt/jwk":
const rfc7517A1ExamplePubKeysStr = """
{"keys":
[
{"kty":"EC",
"crv":"P-256",
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"use":"enc",
"kid":"1"},
{"kty":"RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e":"AQAB",
"alg":"RS256",
"kid":"2011-04-29"}
]
}"""
let rfc7517A1ExamplePubKeysJson = parseJson(rfc7517A1ExamplePubKeysStr)
const rfc7517A2ExamplePrvKeysStr = """
{"keys":
[
{"kty":"EC",
"crv":"P-256",
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
"use":"enc",
"kid":"1"},
{"kty":"RSA",
"n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e":"AQAB",
"d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q",
"p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs",
"q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk",
"dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0",
"dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk",
"qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU",
"alg":"RS256",
"kid":"2011-04-29"}
]
}"""
let rfc7517A2ExamplePrvKeysJson = parseJson(rfc7517A2ExamplePrvKeysStr)
test "parseEcPubKey parses valid keys":
let keyNode = rfc7517A1ExamplePubKeysJson["keys"][0]
let jwk = parseJwk(keyNode)
check:
jwk.kty == $ktyEC
jwk.keyKind == EcPublic
jwk.use.isSome
jwk.use.get == $jwkuEncrypt
jwk.kid.isSome
jwk.kid.get == "1"
jwk.key_ops.isNone
jwk.ecPub.crv == $P256
jwk.ecPub.x == "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"
jwk.ecPub.y.isSome
jwk.ecPub.y.get == "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
test "parseEcPubKey rejects keys missing required values":
let keyNode = rfc7517A1ExamplePubKeysJson["keys"][0]
let withoutCrv = parseJson($keyNode)
withoutCrv.delete("crv")
expect ValueError: discard parseJwk(withoutCrv)
let withoutX = parseJson($keyNode)
withoutX.delete("x")
expect ValueError: discard parseJwk(withoutX)
let withoutY = parseJson($keyNode)
withoutY.delete("y")
let jwkWithoutY = parseJwk(withoutY)
check:
jwkWithoutY.kty == $ktyEc
jwkWithoutY.keyKind == EcPublic
jwkWithoutY.ecPub.y.isNone
test "parseRsaPubKey parses valid keys":
let keyNode = rfc7517A1ExamplePubKeysJson["keys"][1]
let jwk = parseJwk(keyNode)
check:
jwk.kty == $ktyRSA
jwk.keyKind == RsaPublic
jwk.alg.isSome
jwk.alg.get == $RS256
jwk.use.isNone
jwk.kid.isSome
jwk.kid.get == "2011-04-29"
jwk.key_ops.isNone
jwk.rsaPub.e == "AQAB"
jwk.rsaPub.n == "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
test "parseRsaPubKey rejects keys missing required values":
let keyNode = rfc7517A1ExamplePubKeysJson["keys"][1]
let withoutN = parseJson($keyNode)
withoutN.delete("n")
expect ValueError: discard parseJwk(withoutN)
let withoutE = parseJson($keyNode)
withoutE.delete("e")
expect ValueError: discard parseJwk(withoutE)
test "parseEcPrvKey parses valid keys":
let keyNode = rfc7517A2ExamplePrvKeysJson["keys"][0]
let jwk = parseJwk(keyNode)
check:
jwk.kty == $ktyEC
jwk.keyKind == EcPrivate
jwk.use.isSome
jwk.use.get == $jwkuEncrypt
jwk.kid.isSome
jwk.kid.get == "1"
jwk.key_ops.isNone
jwk.ecPrv.crv == $P256
jwk.ecPrv.x == "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"
jwk.ecPrv.y.isSome
jwk.ecPrv.y.get == "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
jwk.ecPrv.d == "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"
test "parseEcPrvKey rejects keys missing required values":
let keyNode = rfc7517A2ExamplePrvKeysJson["keys"][0]
let withoutCrv = parseJson($keyNode)
withoutCrv.delete("crv")
expect ValueError: discard parseJwk(withoutCrv)
let withoutX = parseJson($keyNode)
withoutX.delete("x")
expect ValueError: discard parseJwk(withoutX)
let withoutY = parseJson($keyNode)
withoutY.delete("y")
let jwkWithoutY = parseJwk(withoutY)
check:
jwkWithoutY.kty == $ktyEc
jwkWithoutY.keyKind == EcPrivate
jwkWithoutY.ecPrv.y.isNone
test "parseRsaPrvKey parses valid keys":
let keyNode = rfc7517A2ExamplePrvKeysJson["keys"][1]
let jwk = parseJwk(keyNode)
check:
jwk.kty == $ktyRSA
jwk.keyKind == RsaPrivate
jwk.alg.isSome
jwk.alg.get == $RS256
jwk.use.isNone
jwk.kid.isSome
jwk.kid.get == "2011-04-29"
jwk.key_ops.isNone
jwk.rsaPrv.e == "AQAB"
jwk.rsaPrv.n == "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
jwk.rsaPrv.d == "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"
jwk.rsaPrv.p.isSome
jwk.rsaPrv.p.get == "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs"
jwk.rsaPrv.q.isSome
jwk.rsaPrv.q.get == "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk"
jwk.rsaPrv.dp.isSome
jwk.rsaPrv.dp.get == "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0"
jwk.rsaPrv.dq.isSome
jwk.rsaPrv.dq.get == "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk"
jwk.rsaPrv.qi.isSome
jwk.rsaPrv.qi.get == "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU"
jwk.rsaPrv.oth.isNone
test "parseRsaPrvKey rejects keys missing required values":
let keyNode = rfc7517A2ExamplePrvKeysJson["keys"][1]
let withoutN = parseJson($keyNode)
withoutN.delete("n")
expect ValueError: discard parseJwk(withoutN)
let withoutE = parseJson($keyNode)
withoutE.delete("e")
expect ValueError: discard parseJwk(withoutE)
let withoutP = parseJson($keyNode)
withoutP.delete("p")
let jwkWithoutP = parseJwk(withoutP)
check jwkWithoutP.rsaPrv.p.isNone
test "parseJwkSet (public key examples)":
let jwkSet = parseJwkSet(rfc7517A1ExamplePubKeysJson)
check:
jwkSet.len == 2
jwkSet[0].kty == $ktyEC
jwkSet[0].keyKind == EcPublic
jwkSet[1].kty == $ktyRSA
jwkSet[1].keyKind == RsaPublic
test "parseJwkSet (private key examples)":
let jwkSet = parseJwkSet(rfc7517A2ExamplePrvKeysJson)
check:
jwkSet.len == 2
jwkSet[0].kty == $ktyEC
jwkSet[0].keyKind == EcPrivate
jwkSet[1].kty == $ktyRSA
jwkSet[1].keyKind == RsaPrivate