Skip to content

Commit

Permalink
algorithm: add support for Ed25519, Ed448
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrrt committed Jan 1, 2024
1 parent 5545cd0 commit f75d806
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/vendors/jwt/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type AlgorithmIdentifier =
| Algs.RSAPSS
| Algs.ES256K
| Algs.Ed25519
| Algs.X25519;
| Algs.Ed448

export interface IDecodedJwt {
iss?: string;
Expand Down
65 changes: 42 additions & 23 deletions src/vendors/jwt/jwt-sign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,48 @@ it("signs payload with pkcs8 private key - EdDSA", async () => {
expect(signedPayloadEddsa).toBeTruthy();
});

it("signs payload with pkcs8 private key - Ed25519", async () => {
const {
privateKey,
publicKey
} = await getKeyPair({
keyFormat: "pem",
algorithmIdentifier: Algs.Ed25519,
keySize: 4096
});

expect(privateKey).toBeTruthy();
expect(publicKey).toBeTruthy();

const signedPayloadEd25519 = await signJwtWithPrivateKey(
{ urn: "urn:test:test" },
Algs.EdDSA,
privateKey
);

expect(signedPayloadEd25519).toBeTruthy();
});


it("signs payload with pkcs8 private key - Ed448", async () => {
const keyPairEd448 = await getKeyPair({
keyFormat: "pem",
algorithmIdentifier: Algs.Ed448,
keySize: 4096
});

expect(keyPairEd448?.privateKey).toBeTruthy();

const signedPayloadEd448 = await signJwtWithPrivateKey(
{ urn: "urn:test:test" },
Algs.EdDSA,
keyPairEd448.privateKey
);

expect(signedPayloadEd448).toBeTruthy();
});


it("signs payload with pkcs8 private key - ES256k", async () => {
const keyPairES256k = await getKeyPair({
keyFormat: "pem",
Expand All @@ -436,29 +478,6 @@ it("signs payload with pkcs8 private key - ES256k", async () => {
expect(signedPayloadEs256k).toBeTruthy();
});

it("signs payload with pkcs8 private key - unsupported yet", async () => {
// works but not signin
// const keyPairEd25519 = await getKeyPair({
// keyFormat: "pem",
// algorithmIdentifier: Algs.Ed25519,
// keySize: 4096
// });
// expect(keyPairEd25519?.privateKey).toBeTruthy();
// const keyPairX25519 = await getKeyPair({
// keyFormat: "pem",
// // @ts-ignore
// algorithmIdentifier: "x25519",
// keySize: 4096
// });
// expect(keyPairX25519?.privateKey).toBeTruthy();
// const signedPayloadX25519 = await signJwtWithPrivateKey(
// { urn: "urn:test:test" },
// Algs.X25519,
// keyPairX25519.privateKey
// );
// expect(signedPayloadX25519).toBeTruthy();
});

it("experiment algorithm", async () => {
const generateKey = async ({ alg }): Promise<IKeyPair> => {
return new Promise((resolve: Function, reject: Function) => {
Expand Down
19 changes: 5 additions & 14 deletions src/vendors/jwt/jwt-sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { JwtAlgorithmsEnum as Algs, JwtKeyTypes } from "../../enums";
import { importPKCS8, importJWK, SignJWT, JWTHeaderParameters } from "jose";
import { IGetKeyPair, IKeyPair } from "./interfaces";
import * as c from "../../constants";
import { strToUint8Array } from "./utils";
import { isNodeJs, strToUint8Array } from "./utils";

interface ISignJwtOpts {
kid?: string;
Expand Down Expand Up @@ -75,14 +75,6 @@ const algorithmsDict = [
{
algType: JwtKeyTypes.OKP,
algIds: Object.values([Algs?.EdDSA])
},
{
algType: Algs.Ed25519,
algIds: Object.values([Algs?.Ed25519])
},
{
algType: Algs.X25519,
algIds: Object.values([Algs?.X25519])
}
];

Expand All @@ -102,12 +94,11 @@ export const getKeyPair = async ({

const useCurve = algType === JwtKeyTypes.EC;

// if platform is nodejs
// @ts-ignore
if (typeof window === "undefined") {
const IS_NODE = isNodeJs();
if (IS_NODE) {
const { generateKeyPairSync, randomBytes } = require("crypto");

const { publicKey, privateKey } = generateKeyPairSync(algType, {
const { publicKey, privateKey } = generateKeyPairSync(algType ?? algorithmIdentifier?.toLowerCase(), {
namedCurve: useCurve
? c.namedCurves?.[algorithmIdentifier.toLowerCase()]
: undefined,
Expand All @@ -126,4 +117,4 @@ export const getKeyPair = async ({
return resolve({ publicKey, privateKey, kid });
}
});
};
};
115 changes: 110 additions & 5 deletions src/vendors/jwt/jwt-verify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,6 @@ it("verifies a token with checkTokenValidness signed with ES512 key - jwk", asyn
algorithmIdentifier: Algs.ES512,
keySize: 4096
});

// const regExpPathAppJwks = new RegExp(
// `api\/${c.AUTHDOG_JWKS_API_ID}\/${tenantUuid2}\/${applicationUuid2}\/.well-known\/jwks.json*`
// );

const keys = [keyPairES512.publicKey];

const jwks = {
Expand Down Expand Up @@ -308,6 +303,116 @@ it("verifies a token with checkTokenValidness signed with ES512 key - jwk", asyn
scopeNock.persist(false);
});


it("verifies a token with checkTokenValidness signed with Ed25519 key - jwk", async () => {
const keyPairEd25519 = await getKeyPair({
algorithmIdentifier: Algs.Ed25519,
keySize: 4096
});

const keys = [keyPairEd25519.publicKey];

const jwks = {
keys: [
{
crv: "P-256",
x: "fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U",
y: "nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8",
kty: "EC",
kid: "one"
},
...keys
]
};

const scopeNock = nock("https://as.example.com")
.get("/jwks")
.once()
.reply(200, jwks);

const signedPayloadEd25519 = await signJwtWithPrivateKey(
{
urn: "urn:test:test"
},
Algs.EdDSA,
keyPairEd25519.privateKey,
{
kid: keyPairEd25519?.kid
}
);

const jwksUri = `https://as.example.com/jwks`;

const tokenInJwksStoreValidness = await checkTokenValidness(
signedPayloadEd25519,
{
jwksUri
}
);

expect(tokenInJwksStoreValidness).toBeTruthy();

scopeNock.persist(false);

});


it("verifies a token with checkTokenValidness signed with Ed448 key - jwk", async () => {
const keyPairEd448 = await getKeyPair({
algorithmIdentifier: Algs.Ed448,
keySize: 4096
});

const keys = [keyPairEd448.publicKey];

const jwks = {
keys: [
{
crv: "P-256",
x: "fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U",
y: "nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8",
kty: "EC",
kid: "one"
},
...keys
]
};

const scopeNock = nock("https://as.example.com")
.get("/jwks")
.once()
.reply(200, jwks);

const signedPayloadEd448 = await signJwtWithPrivateKey(
{
urn: "urn:test:test"
},
Algs.EdDSA,
keyPairEd448.privateKey,
{
kid: keyPairEd448?.kid
}
);

const jwksUri = `https://as.example.com/jwks`;

const tokenInJwksStoreValidness = await checkTokenValidness(
signedPayloadEd448,
{
jwksUri
}
);

expect(tokenInJwksStoreValidness).toBeTruthy();

scopeNock.persist(false);

});





it("throws an error while verifying token with public uri whose key is missing from set", async () => {
const tenantUuid2 = "d84ddef4-81dd-4ce6-9594-03ac52cac367";
const applicationUuid2 = "b867db48-4e11-4cae-bb03-086dc97c8ddd";
Expand Down
2 changes: 2 additions & 0 deletions src/vendors/jwt/jwt-verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export const checkTokenValidness = async (
case algEnums.PS512:
case algEnums.EdDSA:
case algEnums.ES256K:
case algEnums.Ed25519:
case algEnums.Ed448:
if (!adhoc && !jwksUri) {
missingCredentials.push("jwksUri");
}
Expand Down
8 changes: 8 additions & 0 deletions src/vendors/jwt/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export const strToUint8Array = (str: string) => {

export const uint8ArrayToStr = (buf: Uint8Array) =>
String.fromCharCode.apply(null, buf);

export const isNodeJs = () => {
return (
typeof process !== "undefined" &&
process.versions != null &&
process.versions.node != null
);
}

0 comments on commit f75d806

Please sign in to comment.