Skip to content
This repository has been archived by the owner on Oct 2, 2024. It is now read-only.

Commit

Permalink
Merge pull request #11 from Sphereon-Opensource/MYC-146
Browse files Browse the repository at this point in the history
MYC-146 added SuppliedSignature functionality
  • Loading branch information
BtencateSphereon authored Feb 10, 2022
2 parents fb88b82 + 51b4866 commit 9184eeb
Show file tree
Hide file tree
Showing 14 changed files with 1,603 additions and 45 deletions.
96 changes: 92 additions & 4 deletions generator/schemaGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,102 @@
const fs = require("fs");
const tsj = require("ts-json-schema-generator");
import fs from 'fs';
import {
createFormatter,
createParser,
createProgram, MutableTypeFormatter,
SchemaGenerator
} from 'ts-json-schema-generator';
import {
BaseType,
Definition,
FunctionType,
SubTypeFormatter
} from 'ts-json-schema-generator';

class CustomTypeFormatter implements SubTypeFormatter {
public supportsType(type: FunctionType): boolean {
return type instanceof FunctionType;
}

public getDefinition(): Definition {
// Return a custom schema for the function property.
return {
type: "object",
properties: {
isFunction: {
type: "boolean",
const: true,
},
},
};
}

public getChildren(): BaseType[] {
return [];
}
}

function writeSchema(config) {
const schema = tsj.createGenerator(config).createSchema(config.type);
const schemaString = JSON.stringify(schema, null, 2);
const formatter = createFormatter(config, (fmt: MutableTypeFormatter) => {
fmt.addTypeFormatter(new CustomTypeFormatter());
});

const program = createProgram(config);
const schema = new SchemaGenerator(program, createParser(program, config), formatter, config).createSchema(config.type);

let schemaString = JSON.stringify(schema, null, 2);
schemaString = correctSchema(schemaString)

fs.writeFile(config.outputPath, `export const ${config.outputConstName} = ${schemaString};`, (err) => {
if (err) throw err;
});
}

function correctSchema(schemaString: string) {
return schemaString.replace(
"\"SuppliedSignature\": {\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
" \"signature\": {\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
" \"isFunction\": {\n" +
" \"type\": \"boolean\",\n" +
" \"const\": true\n" +
" }\n" +
" }\n" +
" },\n" +
" \"did\": {\n" +
" \"type\": \"string\"\n" +
" },\n" +
" \"kid\": {\n" +
" \"type\": \"string\"\n" +
" }\n" +
" },\n" +
" \"required\": [\n" +
" \"signature\",\n" +
" \"did\",\n" +
" \"kid\"\n" +
" ],\n" +
" \"additionalProperties\": false\n" +
" },",
"\"SuppliedSignature\": {\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
" \"did\": {\n" +
" \"type\": \"string\"\n" +
" },\n" +
" \"kid\": {\n" +
" \"type\": \"string\"\n" +
" }\n" +
" },\n" +
" \"required\": [\n" +
" \"did\",\n" +
" \"kid\"\n" +
" ],\n" +
" \"additionalProperties\": true\n" +
" },")
}

const requestOptsConf = {
path: "../src/main/types/SIOP.types.ts",
tsconfig: "tsconfig.json",
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js → jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
"!src/schemas/**",
"!src/**/*.d.ts",
"!**/node_modules/**",
"!jest.config.js",
"!jest.config.cjs",
"!generator/**",
"!index.ts",

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"name": "@sphereon/did-auth-siop",
"version": "0.2.6-unstable.0",
"version": "0.2.12-bram",
"main": "dist/main/index.js",
"types": "dist/main/index.d.ts",
"license": "Apache-2.0",
"type": "module",
"repository": {
"url": "https://github.com/Sphereon-Opensource/did-auth-siop.git"
},
Expand All @@ -12,7 +13,7 @@
"scripts": {
"build": "run-p build:*",
"build:main": "tsc -p tsconfig.build.json",
"build:schemaGenerator": "node generator/schemaGenerator.ts",
"build:schemaGenerator": "node --loader ts-node/esm generator/schemaGenerator.ts",
"fix": "run-s fix:*",
"fix:prettier": "prettier \"{src,test}/**/*.ts\" --write",
"fix:lint": "eslint . --ext .ts --fix",
Expand Down
7 changes: 5 additions & 2 deletions src/main/AuthenticationResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ async function createThumbprintAndJWK(resOpts: SIOP.AuthenticationResponseOpts):
}
thumbprint = getThumbprintFromJwk(didDocument.verificationMethod[0].publicKeyJwk as JWK, resOpts.did);
subJwk = didDocument.verificationMethod[0].publicKeyJwk as JWK;*/
} else if (SIOP.isSuppliedSignature(resOpts.signatureType)) {
return { thumbprint, subJwk };
} else {
throw new Error(SIOPErrors.SIGNATURE_OBJECT_TYPE_NOT_SET);
}
Expand Down Expand Up @@ -216,7 +218,6 @@ async function createSIOPResponsePayload(
throw new Error(SIOPErrors.VERIFY_BAD_PARAMS);
}
const isDidSupported = verifiedJwt.payload.registration?.subject_identifiers_supported?.includes(SubjectIdentifierType.DID);

const { thumbprint, subJwk } = await createThumbprintAndJWK(resOpts);
const state = resOpts.state || State.getState(verifiedJwt.payload.state);
const nonce = resOpts.nonce || State.getNonce(state, resOpts.nonce);
Expand Down Expand Up @@ -247,7 +248,9 @@ async function createSIOPResponsePayload(
function assertValidResponseOpts(opts: SIOP.AuthenticationResponseOpts) {
if (!opts /*|| !opts.redirectUri*/ || !opts.signatureType /*|| !opts.nonce*/ || !opts.did) {
throw new Error(SIOPErrors.BAD_PARAMS);
} else if (!(SIOP.isInternalSignature(opts.signatureType) || SIOP.isExternalSignature(opts.signatureType))) {
} else if (
!(SIOP.isInternalSignature(opts.signatureType) || SIOP.isExternalSignature(opts.signatureType) || SIOP.isSuppliedSignature(opts.signatureType))
) {
throw new Error(SIOPErrors.SIGNATURE_OBJECT_TYPE_NOT_SET);
}
}
Expand Down
22 changes: 18 additions & 4 deletions src/main/OPBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { getUniResolver } from '@sphereon/did-uni-client';
import { EcdsaSignature } from 'did-jwt/lib/util';
import { Resolvable, Resolver } from 'did-resolver';

import { OP } from './OP';
import { DIDJwt } from './functions';
import { CredentialFormat, ExternalSignature, InternalSignature, PassBy, ResponseMode, ResponseRegistrationOpts } from './types/SIOP.types';
import {
CredentialFormat,
ExternalSignature,
InternalSignature,
PassBy,
ResponseMode,
ResponseRegistrationOpts,
SuppliedSignature,
} from './types/SIOP.types';

export default class OPBuilder {
didMethods: string[] = [];
resolvers: Map<string, Resolvable> = new Map<string, Resolvable>();
signatureType: InternalSignature | ExternalSignature;
signatureType: InternalSignature | ExternalSignature | SuppliedSignature;
credentialFormats: CredentialFormat[] = [];
responseRegistration: ResponseRegistrationOpts;
responseMode?: ResponseMode;
Expand Down Expand Up @@ -66,8 +75,8 @@ export default class OPBuilder {
idTokenSigningAlgValuesSupported?: KeyAlgo[] | KeyAlgo;
requestObjectSigningAlgValuesSupported?: SigningAlgo[] | SigningAlgo;
*/
// Only internal supported for now
signature(signatureType: InternalSignature): OPBuilder {
// Only internal | supplied supported for now
signature(signatureType: InternalSignature | SuppliedSignature): OPBuilder {
this.signatureType = signatureType;
return this;
}
Expand All @@ -77,6 +86,11 @@ export default class OPBuilder {
return this;
}

suppliedSignature(signature: (data: string | Uint8Array) => Promise<EcdsaSignature | string>, did: string, kid: string): OPBuilder {
this.signature({ signature, did, kid });
return this;
}

build(): OP {
// this.responseRegistration.didMethodsSupported = this.didMethods;
// this.responseRegistration.subjectIdentifiersSupported = this.subjectIdentifierTypes;
Expand Down
13 changes: 10 additions & 3 deletions src/main/RPBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getUniResolver } from '@sphereon/did-uni-client';
import { EcdsaSignature } from 'did-jwt/lib/util';
import { Resolvable, Resolver } from 'did-resolver';

import { RP } from './RP';
Expand All @@ -16,6 +17,7 @@ import {
ResponseContext,
ResponseMode,
SubjectIdentifierType,
SuppliedSignature,
} from './types/SIOP.types';

export default class RPBuilder {
Expand All @@ -26,7 +28,7 @@ export default class RPBuilder {
requestRegistration: Partial<RequestRegistrationOpts> = {};
redirectUri: string;
requestObjectBy: ObjectBy;
signatureType: InternalSignature | ExternalSignature | NoSignature;
signatureType: InternalSignature | ExternalSignature | SuppliedSignature | NoSignature;
responseMode?: ResponseMode;
responseContext?: ResponseContext.RP;
claims?: ClaimOpts;
Expand Down Expand Up @@ -80,8 +82,8 @@ export default class RPBuilder {
return this;
}

// Only internal supported for now
signature(signatureType: InternalSignature): RPBuilder {
// Only internal | supplied supported for now
signature(signatureType: InternalSignature | SuppliedSignature): RPBuilder {
this.signatureType = signatureType;
return this;
}
Expand All @@ -91,6 +93,11 @@ export default class RPBuilder {
return this;
}

suppliedSignature(signature: (data: string | Uint8Array) => Promise<EcdsaSignature | string>, did: string, kid: string): RPBuilder {
this.signature({ signature, did, kid });
return this;
}

addPresentationDefinitionClaim(definitionOpt: PresentationDefinitionWithLocation): RPBuilder {
if (!this.claims || !this.claims.presentationDefinitions) {
this.claims = {
Expand Down
26 changes: 25 additions & 1 deletion src/main/functions/DidJWT.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EdDSASigner, ES256KSigner } from 'did-jwt';
import { EcdsaSignature } from 'did-jwt/lib/util';
import { Resolvable } from 'did-resolver';

import { createJWT, decodeJWT, JWTDecoded, JWTHeader, JWTOptions, JWTPayload, JWTVerifyOptions, verifyJWT } from '../../did-jwt-fork/JWT';
Expand All @@ -14,6 +15,7 @@ import {
isInternalSignature,
isResponseOpts,
isResponsePayload,
isSuppliedSignature,
KeyAlgo,
ResponseIss,
SignatureResponse,
Expand Down Expand Up @@ -89,6 +91,8 @@ export async function signDidJwtPayload(
return signDidJwtInternal(payload, isResponse ? payload.iss : opts.signatureType.did, opts.signatureType.hexPrivateKey, opts.signatureType.kid);
} else if (isExternalSignature(opts.signatureType)) {
return signDidJwtExternal(payload, opts.signatureType.signatureUri, opts.signatureType.authZToken, opts.signatureType.kid);
} else if (isSuppliedSignature(opts.signatureType)) {
return signDidJwtSupplied(payload, isResponse ? payload.iss : opts.signatureType.did, opts.signatureType.signature, opts.signatureType.kid);
} else {
throw new Error(SIOPErrors.BAD_SIGNATURE_PARAMS);
}
Expand All @@ -99,7 +103,7 @@ async function signDidJwtInternal(
issuer: string,
hexPrivateKey: string,
kid?: string
) {
): Promise<string> {
const algo = isEd25519DidKeyMethod(issuer) || isEd25519DidKeyMethod(payload.kid) || isEd25519JWK(payload.sub_jwk) ? KeyAlgo.EDDSA : KeyAlgo.ES256K;
// const request = !!payload.client_id;
const signer =
Expand Down Expand Up @@ -142,6 +146,26 @@ async function signDidJwtExternal(
return ((await response.json()) as SignatureResponse).jws;
}

async function signDidJwtSupplied(
payload: AuthenticationRequestPayload | AuthenticationResponsePayload,
issuer: string,
signer: (data: string | Uint8Array) => Promise<EcdsaSignature | string>,
kid: string
): Promise<string> {
const algo = isEd25519DidKeyMethod(issuer) || isEd25519DidKeyMethod(payload.kid) || isEd25519JWK(payload.sub_jwk) ? KeyAlgo.EDDSA : KeyAlgo.ES256K;
const header = {
alg: algo,
kid,
};
const options = {
issuer,
signer,
expiresIn: SIOP.expirationTime,
};

return await createDidJWT({ ...payload }, options, header);
}

export function getAudience(jwt: string) {
const { payload } = decodeJWT(jwt);
if (!payload) {
Expand Down
19 changes: 19 additions & 0 deletions src/main/schemas/AuthenticationRequestOpts.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const AuthenticationRequestOptsSchema = {
{
"$ref": "#/definitions/ExternalSignature"
},
{
"$ref": "#/definitions/SuppliedSignature"
},
{
"$ref": "#/definitions/NoSignature"
}
Expand Down Expand Up @@ -114,6 +117,22 @@ export const AuthenticationRequestOptsSchema = {
],
"additionalProperties": false
},
"SuppliedSignature": {
"type": "object",
"properties": {
"did": {
"type": "string"
},
"kid": {
"type": "string"
}
},
"required": [
"did",
"kid"
],
"additionalProperties": true
},
"NoSignature": {
"type": "object",
"properties": {
Expand Down
19 changes: 19 additions & 0 deletions src/main/schemas/AuthenticationResponseOpts.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const AuthenticationResponseOptsSchema = {
},
{
"$ref": "#/definitions/ExternalSignature"
},
{
"$ref": "#/definitions/SuppliedSignature"
}
]
},
Expand Down Expand Up @@ -94,6 +97,22 @@ export const AuthenticationResponseOptsSchema = {
],
"additionalProperties": false
},
"SuppliedSignature": {
"type": "object",
"properties": {
"did": {
"type": "string"
},
"kid": {
"type": "string"
}
},
"required": [
"did",
"kid"
],
"additionalProperties": true
},
"ResponseRegistrationOpts": {
"type": "object",
"properties": {
Expand Down
4 changes: 3 additions & 1 deletion src/main/types/JWT.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { EcdsaSignature } from 'did-jwt/lib/util';
import type { DIDResolutionResult, VerificationMethod } from 'did-resolver';
// import type {JWK} from "jose/types";

export type Signer = (data: string | Uint8Array) => Promise<string>;
// Signer interface conforming to the DID-JWT module
export type Signer = (data: string | Uint8Array) => Promise<EcdsaSignature | string>;

export interface JWTPayload {
iss?: string;
Expand Down
Loading

0 comments on commit 9184eeb

Please sign in to comment.