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

algorithm: add support for Ed25519, Ed448 #45

Merged
merged 1 commit into from
Jan 1, 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
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
);
}
Loading