-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
773 additions
and
10 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)), | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
Oops, something went wrong.