Skip to content

Commit

Permalink
fix: refactor JWK implementation into a pollux task
Browse files Browse the repository at this point in the history
Signed-off-by: Francisco Javier Ribo Labrador <[email protected]>
  • Loading branch information
elribonazo committed Feb 19, 2025
1 parent cae10ab commit 4b593a3
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 25 deletions.
2 changes: 0 additions & 2 deletions src/apollo/Apollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { X25519PublicKey } from "./utils/X25519PublicKey";
import { isEmpty, notEmptyString } from "../utils";
import ApolloPKG from "@hyperledger/identus-apollo";
import { PrismDerivationPath } from "../domain/models/derivation/schemas/PrismDerivation";

const ApolloSDK = ApolloPKG.org.hyperledger.identus.apollo;
const Mnemonic = ApolloSDK.derivation.Mnemonic.Companion;
const HDKey = ApolloSDK.derivation.HDKey;
Expand Down Expand Up @@ -115,7 +114,6 @@ export default class Apollo implements ApolloInterface, KeyRestoration {
static Ed25519PrivateKey = Ed25519PrivateKey;
static X25519PrivateKey = X25519PrivateKey;


/**
* Creates a random set of mnemonic phrases that can be used as a seed for generating a private key.
*
Expand Down
2 changes: 1 addition & 1 deletion src/apollo/utils/Secp256k1PublicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class Secp256k1PublicKey extends PublicKey implements StorableKey, Export
);
}

private get native() {
get native() {
return ApolloSDK.utils.KMMECSecp256k1PublicKey.Companion.secp256k1FromBytes(
Int8Array.from(this.raw)
);
Expand Down
16 changes: 12 additions & 4 deletions src/domain/models/errors/Apollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ export class KeyRestoratonFailed extends SDKError {
* thrown when given Key Curve is not supported
*/
export class InvalidKeyCurve extends SDKError {
constructor(keyCurve?: string) {
const options = Object.values(Curve);
constructor(keyCurve?: string, options = Object.values(Curve)) {
const msg = `Invalid key curve: ${keyCurve ?? "undefined"}. Valid options are: ${options.join(", ")}`;
super(16, msg);
}
Expand All @@ -64,8 +63,7 @@ export class InvalidKeyCurve extends SDKError {
* thrown when give Key Type is not supported
*/
export class InvalidKeyType extends SDKError {
constructor(keyType?: string) {
const options = Object.values(KeyTypes);
constructor(keyType?: string, options = Object.values(KeyTypes)) {
const msg = `Invalid key type: ${keyType ?? "undefined"}. Valid options are: ${options.join(", ")}`;
super(17, msg);
}
Expand All @@ -80,6 +78,16 @@ export class MissingKeyParameters extends SDKError {
}
}

export class InvalidECParameters extends SDKError {
constructor(parameters: string | string[]) {
if (typeof parameters === "string") {
super(19, `Missing or invalid JWK parameter: ${parameters}`);
} else {
super(19, `Missing or invalid JWK parameters: ${parameters.join(", ")}`);
}
}
}

/**
* thrown when failing to create a key
*/
Expand Down
1 change: 1 addition & 0 deletions src/domain/models/keyManagement/KeyTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum KeyTypes {
"EC" = "EC",
"Curve25519" = "Curve25519",
"OKP" = "OKP",
"unknown" = "unknown"
}
148 changes: 148 additions & 0 deletions src/pollux/utils/jwt/FromJWK.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { Domain } from "../../..";
import { expect, Task } from "../../../utils";

import { base64url } from "multiformats/bases/base64";
import { ApolloError, Curve, JWK, KeyPair, KeyProperties, KeyTypes, PrivateKey, PublicKey } from "../../../domain";
import { InvalidECParameters } from "../../../domain/models/errors/Apollo";


export function isECJWK(jwk: JWK): jwk is JWK.EC {
if (jwk.kty !== "EC") {
return false;
}
return true;
}

export function isOKPJWK(jwk: JWK): jwk is JWK.OKP {
if (jwk.kty !== "OKP") {
return false;
}
return true;
}


export function decodeJWKECParameter(coordinate: 'x' | 'y' | 'd', jwk: JWK.EC): Uint8Array {
if (!jwk[coordinate]) {
throw new InvalidECParameters(coordinate);
}
const coordinateValue = jwk[coordinate]!;

Check warning on line 28 in src/pollux/utils/jwt/FromJWK.ts

View workflow job for this annotation

GitHub Actions / Build and test

Forbidden non-null assertion
try {
const decoded = base64url.baseDecode(coordinateValue);
return new Uint8Array(decoded);
} catch (err) {
throw new InvalidECParameters(coordinate);
}
}



export interface Args {
jwk: Domain.JWK;
}


export class FromJWK extends Task<Domain.PublicKey | Domain.KeyPair, Args> {

private fromJWKEC(apollo: Domain.Apollo, jwk: JWK.EC): KeyPair | PrivateKey | PublicKey {
const crv = expect(jwk.crv);
const withCoordinates =
jwk.x !== undefined ||
jwk.y !== undefined;

if (withCoordinates) {
const decodedX = decodeJWKECParameter('x', jwk);
const decodedY = decodeJWKECParameter('y', jwk);

if (crv === Curve.SECP256K1) {
let pk: PublicKey;
let sk: PrivateKey;

pk = apollo.createPublicKey({

Check warning on line 60 in src/pollux/utils/jwt/FromJWK.ts

View workflow job for this annotation

GitHub Actions / Build and test

'pk' is never reassigned. Use 'const' instead
[KeyProperties.curve]: Curve.SECP256K1,
[KeyProperties.type]: KeyTypes.EC,
[KeyProperties.curvePointX]: decodedX,
[KeyProperties.curvePointY]: decodedY
});

if (jwk.d !== undefined) {
const decodedD = decodeJWKECParameter('d', jwk);
sk = apollo.createPrivateKey({
[KeyProperties.curve]: Curve.SECP256K1,
[KeyProperties.type]: KeyTypes.EC,
[KeyProperties.rawKey]: decodedD
});

const keypair: Domain.KeyPair = {
privateKey: sk,
publicKey: pk,
curve: Curve.SECP256K1
}
return keypair;
}
return pk;
}

throw new ApolloError.InvalidKeyCurve(crv, [Curve.SECP256K1]);
}

if (jwk.d !== undefined) {
if (crv !== Curve.SECP256K1 && crv !== Curve.ED25519 && crv !== Curve.X25519) {
throw new ApolloError.InvalidKeyCurve();
}
const decodedD = decodeJWKECParameter('d', jwk);
return apollo.createPrivateKey({
[KeyProperties.curve]: Curve.SECP256K1,
[KeyProperties.type]: KeyTypes.EC,
[KeyProperties.rawKey]: decodedD
})
}

throw new ApolloError.InvalidECParameters(['d', 'x and y']);
}

private fromJWKOKP(apollo: Domain.Apollo, jwk: JWK.OKP): KeyPair | PublicKey {
const crv = expect(jwk.crv);
const x = expect(jwk.d);

if (crv !== Curve.SECP256K1 && crv !== Curve.ED25519 && crv !== Curve.X25519) {
throw new ApolloError.InvalidKeyCurve();
}

const pk = apollo.createPublicKey({
[KeyProperties.curve]: crv,
[KeyProperties.type]: KeyTypes.OKP,
[KeyProperties.rawKey]: base64url.baseDecode(x)
});

if (jwk.d !== undefined) {
const sk = apollo.createPrivateKey({
[KeyProperties.curve]: crv,
[KeyProperties.type]: KeyTypes.OKP,
[KeyProperties.rawKey]: base64url.baseDecode(jwk.d)
});
const keypair: Domain.KeyPair = {
privateKey: sk,
publicKey: pk,
curve: crv
}
return keypair;
}
return pk;
}

async run(ctx: Task.Context): Promise<Domain.PublicKey | Domain.KeyPair> {
const jwk = this.args.jwk;
const kty = expect(jwk.kty);
const isEC = isECJWK(jwk);
const isOKP = isOKPJWK(jwk);

if (isEC) {
return this.fromJWKEC(ctx.Apollo, jwk);
}
if (isOKP) {
return this.fromJWKOKP(ctx.Apollo, jwk);
}
throw new ApolloError.InvalidKeyType(kty, [KeyTypes.EC, KeyTypes.OKP]);
}

}
27 changes: 9 additions & 18 deletions src/pollux/utils/jwt/PKInstance.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type * as DIDResolver from "did-resolver";
import { base58btc } from 'multiformats/bases/base58';
import { base64url } from "multiformats/bases/base64";
import * as Domain from "../../../domain";
import { Task, expect } from "../../../utils";
import { Task } from "../../../utils";
// TODO importing from Castor
import { VerificationKeyType } from "../../../castor/types";

import { FromJWK } from "./FromJWK";
export interface Args {
verificationMethod: DIDResolver.VerificationMethod;
}
Expand Down Expand Up @@ -36,21 +35,13 @@ export class PKInstance extends Task<Domain.PublicKey | undefined, Args> {
}

if (verificationMethod.publicKeyJwk) {
const crv = expect(verificationMethod.publicKeyJwk.crv);
const x = expect(verificationMethod.publicKeyJwk.x);

if (crv === Domain.Curve.ED25519) {
pk = ctx.Apollo.createPublicKey({
[Domain.KeyProperties.curve]: Domain.Curve.ED25519,
[Domain.KeyProperties.type]: Domain.KeyTypes.EC,
[Domain.KeyProperties.rawKey]: base64url.baseDecode(x)
});
} else if (crv === Domain.Curve.SECP256K1) {
pk = ctx.Apollo.createPublicKey({
[Domain.KeyProperties.curve]: Domain.Curve.SECP256K1,
[Domain.KeyProperties.type]: Domain.KeyTypes.EC,
[Domain.KeyProperties.rawKey]: base64url.baseDecode(x)
});
const keyPair = await ctx.run(
new FromJWK({ jwk: verificationMethod.publicKeyJwk as Domain.JWK })
);
if (keyPair instanceof Domain.KeyPair) {
pk = keyPair.publicKey;
} else {
pk = keyPair;
}
return pk;
}
Expand Down
Loading

0 comments on commit 4b593a3

Please sign in to comment.