Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed Jul 6, 2023
1 parent 1cd7250 commit e2a9624
Show file tree
Hide file tree
Showing 14 changed files with 773 additions and 10 deletions.
347 changes: 337 additions & 10 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"typescript": "^4.9.4"
},
"dependencies": {
"@transmute/cose": "^0.0.13",
"axios": "^1.2.2",
"jose": "^4.11.1"
}
Expand Down
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import cose from "@transmute/cose";

import did from "./did";

import w3c from "./w3c";
import scitt from "./scitt";

import {
encryptToKey,
decryptWithKey,
Expand All @@ -12,6 +17,9 @@ import {
const transmute = {
did,
jose: { alg, enc },
cose,
w3c,
scitt,
sign: signWithKey,
verify: verifyWithKey,
encrypt: encryptToKey,
Expand Down
8 changes: 8 additions & 0 deletions src/jose/getKidFromJwt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as jose from 'jose'

const getKidFromJwt = (jwt: string) => {
const decoded = jose.decodeProtectedHeader(jwt)
return decoded.kid
}

export default getKidFromJwt
6 changes: 6 additions & 0 deletions src/scitt/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { signer } from "./signer"
import { verifier } from "./verifier"

const scitt = { signer, verifier }

export default scitt
3 changes: 3 additions & 0 deletions src/scitt/signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import cose from "@transmute/cose";

export const signer = cose.detached.signer
26 changes: 26 additions & 0 deletions src/scitt/verifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import cose, { PublicKeyJwk } from "@transmute/cose";


import { RequestVerifierFromResolver } from "../types/W3C";
import { DetachedSignature } from "@transmute/cose/dist/types/DetachedSignature";

export const verifier = ({ resolver }: RequestVerifierFromResolver) => {
return {
verify: async ({ payload, signature }: DetachedSignature): Promise<boolean> => {
try {
const kid = cose.getKid(signature)
if (!kid) {
throw new Error('No kid found, unable to locate key material')
}
const publicKey = await resolver.dereference(kid)
const verifier = await cose.detached.verifier({
publicKeyJwk: publicKey as PublicKeyJwk
})
return verifier.verify({ payload, signature })
} catch (e) {
console.log(e)
return false
}
}
}
}
107 changes: 107 additions & 0 deletions src/types/W3C.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@


import { CompactJsonWebSignature } from "../jose/CompactJsonWebSignature"
import { PrivateKeyJwk } from "../jose/PrivateKeyJwk"
import { PublicKeyJwk } from "../jose/PublicKeyJwk"


export type VcLdJwtHeader = Record<string, unknown> & {
alg: string
kid: string
typ?: 'vc+ld+jwt'
cty?: 'vc+ld+json'
}

export type VcIssuer = string | Record<string, unknown> & {
id: string
}

export type VcHolder = string | Record<string, unknown> & {
id: string
}


export type VcCredentialSubject = Record<string, unknown> & {
id?: string
}

export type VcType = string | string[]

export type VcCredentialSchema = Record<string, unknown> & {
id: string
type: string
}

export type VcCredentialStatus = Record<string, unknown> & {
id: string
type: string
}

export type VcProof = Record<string, unknown> & {
id?: string
type?: string
}

export type Context = string | string[] | Record<string, unknown>[]

export type VcLd = Record<string, unknown> & {
'@context': Context
type: VcType
issuer: VcIssuer,
credentialSubject: VcCredentialSubject | VcCredentialSubject[]

id?: string
validFrom?: string
validUntil?: string
credentialSchema?: VcCredentialSchema | VcCredentialSchema[]
credentialStatus?: VcCredentialStatus | VcCredentialStatus[]
proof?: VcProof | VcProof[]
}

export type VpLd = Record<string, unknown> & {
'@context': Context
type: VcType
holder?: VcHolder,
verifiableCredential?: VcLd | VcLd[]
proof?: VcProof | VcProof[]
}

export type RequestJwt = {
header: VcLdJwtHeader
claimset: VcLd | VpLd
}

export type JwtSigner = {
sign: (req: RequestJwt) => Promise<string>
}

export type RequestVerifyJwt = {
jwt: CompactJsonWebSignature
}

export type JwtVerifier = {
verify: (req: RequestVerifyJwt) => Promise<VcLd>
}
export type RequestVcLdJwt = {
header: VcLdJwtHeader
claimset: VcLd
signer: JwtSigner
}


export type RequestW3CVcLdJwtSigner = {
privateKey: PrivateKeyJwk
}

export type RequestVerifierFromPublicKey = {
publicKey: PublicKeyJwk
}


export type RequestVerifierFromResolver = {
resolver: {
dereference: (id: string) => Promise<PublicKeyJwk>
}
}

export type RequestW3CVerifier = RequestVerifierFromPublicKey | RequestVerifierFromResolver
6 changes: 6 additions & 0 deletions src/w3c/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { signer } from "./signer"
import { verifier } from "./verifier"

const w3c = { signer, verifier }

export default w3c
15 changes: 15 additions & 0 deletions src/w3c/signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { signWithKey } from '../jose/signWithKey'

import { RequestW3CVcLdJwtSigner, RequestJwt } from '../types/W3C'

export const signer = ({ privateKey }: RequestW3CVcLdJwtSigner) => {
return {
sign: ({ header, claimset }: RequestJwt) => {
return signWithKey({
privateKey: privateKey,
protectedHeader: header,
payload: new TextEncoder().encode(JSON.stringify(claimset)),
});
}
}
}
35 changes: 35 additions & 0 deletions src/w3c/verifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { verifyWithKey } from '../jose';

import { RequestW3CVerifier, RequestVerifyJwt, VcLd, RequestVerifierFromPublicKey, RequestVerifierFromResolver } from '../types/W3C'


import getKidFromJwt from '../jose/getKidFromJwt'

export const verifier = (req: RequestW3CVerifier) => {
return {
verify: async ({ jwt }: RequestVerifyJwt) => {
let publicKey;
if ((req as RequestVerifierFromPublicKey).publicKey) {
publicKey = (req as RequestVerifierFromPublicKey).publicKey
}
if ((req as RequestVerifierFromResolver).resolver) {
const kid = getKidFromJwt(jwt)
if (!kid) {
throw new Error('No kid found, unable to locate key material')
}
publicKey = await (req as RequestVerifierFromResolver).resolver.dereference(kid)
}
if (!publicKey) {
throw new Error('No public key available to verify')
}
const verified = await verifyWithKey({
publicKey: publicKey,
jws: jwt
})
return {
protectedHeader: verified.protectedHeader,
claimset: JSON.parse(new TextDecoder().decode(verified.payload)) as VcLd
}
}
}
}
63 changes: 63 additions & 0 deletions test/cose.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import transmute, {

} from "../src";


const actor: any = {
"did": "did:jwk:eyJraWQiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6andrLXRodW1icHJpbnQ6c2hhLTI1Njo5T1dOcFNWME9SX0t2NzQ1VGZzNllaOGtNY3UtX2EtSG9TNDdvNkNqcUVZIiwia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImFsZyI6IkVTMjU2IiwieCI6IjIwYWdKSnhFaHRMdC1qbGRRNk9BWEtCVzhlVm5pZ0F3TWZPSDdnWjhxeVUiLCJ5IjoiRWRCdGlsLW9QVm1ZdHFIUG84TXZUdURPQzYyUXhsaVVGb2ZBa0c3OUNYVSJ9",
"key": {
"publicKey": {
"kid": "urn:ietf:params:oauth:jwk-thumbprint:sha-256:9OWNpSV0OR_Kv745Tfs6YZ8kMcu-_a-HoS47o6CjqEY",
"kty": "EC",
"crv": "P-256",
"alg": "ES256",
"x": "20agJJxEhtLt-jldQ6OAXKBW8eVnigAwMfOH7gZ8qyU",
"y": "EdBtil-oPVmYtqHPo8MvTuDOC62QxliUFofAkG79CXU"
},
"privateKey": {
"kid": "urn:ietf:params:oauth:jwk-thumbprint:sha-256:9OWNpSV0OR_Kv745Tfs6YZ8kMcu-_a-HoS47o6CjqEY",
"kty": "EC",
"crv": "P-256",
"alg": "ES256",
"x": "20agJJxEhtLt-jldQ6OAXKBW8eVnigAwMfOH7gZ8qyU",
"y": "EdBtil-oPVmYtqHPo8MvTuDOC62QxliUFofAkG79CXU",
"d": "n_yCLgJ0hk0LAYeXXUknPb8i85I6E1IkTMwbq4jbilE"
}
}
}


const message = "It’s a dangerous business, Frodo, going out your door. 🧠💎";
const payload = new TextEncoder().encode(message);


it("attached sign and verify", async () => {
const signer = await transmute.cose.signer({ privateKeyJwk: actor.key.privateKey })
const verifier = await transmute.cose.verifier({ publicKeyJwk: actor.key.publicKey })
const signature = await signer.sign({
protectedHeader: {
kid: 'did:example:123#key-2',
alg: actor.key.publicKey.alg
},
payload
})
const verified = await verifier.verify(signature)
expect(verified).toBeDefined();
});

it("detached sign and verify", async () => {
const signer = await transmute.cose.detached.signer({ privateKeyJwk: actor.key.privateKey })
const verifier = await transmute.cose.detached.verifier({ publicKeyJwk: actor.key.publicKey })
const detached = await signer.sign({
protectedHeader: {
kid: 'did:example:123#key-2',
alg: actor.key.publicKey.alg
},
payload
})
const verified = await verifier.verify({
payload: payload,
signature: detached.signature
})
expect(verified).toBe(true);
});
44 changes: 44 additions & 0 deletions test/scitt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import transmute, { DidJwkUrl } from "../src";

const message = "It’s a dangerous business, Frodo, going out your door. 🧠💎";
const payload = new TextEncoder().encode(message);

it("with resolver", async () => {
const { did, key: { privateKey } } = await transmute.did.jwk.exportable({
alg: "ES256",
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const signer = await transmute.scitt.signer({ privateKeyJwk: privateKey as any });
const detached = await signer.sign({
protectedHeader: {
kid: 'did:example:123#key-42',
alg: privateKey.alg,
},
payload
});
const verifier = transmute.scitt.verifier({
resolver: {
dereference: async (id: string) => {
// You might implement an allowed issuers policy here...
if (id !== 'did:example:123#key-42') {
throw new Error('Unsupported issuer')
}
try {
const vm = await transmute.did.jwk.dereference({
// normally this would be id
// its hard coded here to pass the test
// this simulates "knowing (somehow)"
// which key are associated with an issuer...
id: did + '#0' as DidJwkUrl,
documentLoader: transmute.did.jwk.documentLoader,
});
return vm.publicKeyJwk;
} catch (e) {
throw new Error('Unsupported issuer')
}
},
},
});
const verified = await verifier.verify({ payload, signature: detached.signature });
expect(verified).toBe(true);
});
Loading

0 comments on commit e2a9624

Please sign in to comment.