Browse Source

modify test payload, add test to help with server verification

pull/116/head
Trent Larson 5 months ago
parent
commit
dc04c88c33
  1. 60
      src/libs/didPeer.ts
  2. 83
      src/views/TestView.vue

60
src/libs/didPeer.ts

@ -23,7 +23,8 @@ import {
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers"; import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
const PEER_DID_PREFIX = "did:peer:0"; const PEER_DID_PREFIX = "did:peer:";
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0";
export interface JWK { export interface JWK {
kty: string; kty: string;
crv: string; crv: string;
@ -99,11 +100,11 @@ export function createPeerDid(publicKeyBytes: Uint8Array) {
"base58btc", "base58btc",
"p256-pub", "p256-pub",
); );
return PEER_DID_PREFIX + methodSpecificId; return PEER_DID_MULTIBASE_PREFIX + methodSpecificId;
} }
function peerDidToPublicKeyBytes(did: string) { function peerDidToPublicKeyBytes(did: string) {
return multibaseToBytes(did.substring(PEER_DID_PREFIX.length)); return multibaseToBytes(did.substring(PEER_DID_MULTIBASE_PREFIX.length));
} }
export class PeerSetup { export class PeerSetup {
@ -112,10 +113,19 @@ export class PeerSetup {
public clientDataJsonBase64Url?: Base64URLString; public clientDataJsonBase64Url?: Base64URLString;
public signature?: Base64URLString; public signature?: Base64URLString;
public async createJwtSimplewebauthn(fullPayload: object, credIdHex: string) { public async createJwtSimplewebauthn(
issuerDid: string,
payload: object,
credIdHex: string,
) {
const credentialId = arrayBufferToBase64URLString( const credentialId = arrayBufferToBase64URLString(
Buffer.from(credIdHex, "hex").buffer, Buffer.from(credIdHex, "hex").buffer,
); );
const fullPayload = {
...payload,
iat: Math.floor(Date.now() / 1000),
iss: issuerDid,
};
this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload))); this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload)));
// const payloadHash: Uint8Array = sha256(this.challenge); // const payloadHash: Uint8Array = sha256(this.challenge);
const options: PublicKeyCredentialRequestOptionsJSON = const options: PublicKeyCredentialRequestOptionsJSON =
@ -147,18 +157,33 @@ export class PeerSetup {
.replace(/=+$/, ""); .replace(/=+$/, "");
const dataInJwt = { const dataInJwt = {
AuthenticationData: authenticatorDataBase64Url, AuthenticationDataB64URL: authenticatorDataBase64Url,
ClientDataJSON: this.clientDataJsonBase64Url, ClientDataJSONB64URL: this.clientDataJsonBase64Url,
iat: Math.floor(Date.now() / 1000),
iss: issuerDid,
}; };
const dataInJwtString = JSON.stringify(dataInJwt); const dataInJwtString = JSON.stringify(dataInJwt);
const payloadBase64 = Buffer.from(dataInJwtString).toString("base64"); const payloadBase64 = Buffer.from(dataInJwtString)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const signature = clientAuth.response.signature; const signature = clientAuth.response.signature;
return headerBase64 + "." + payloadBase64 + "." + signature; return headerBase64 + "." + payloadBase64 + "." + signature;
} }
public async createJwtNavigator(fullPayload: object, credIdHex: string) { public async createJwtNavigator(
issuerDid: string,
payload: object,
credIdHex: string,
) {
const fullPayload = {
...payload,
iat: Math.floor(Date.now() / 1000),
iss: issuerDid,
};
const dataToSignString = JSON.stringify(fullPayload); const dataToSignString = JSON.stringify(fullPayload);
const dataToSignBuffer = Buffer.from(dataToSignString); const dataToSignBuffer = Buffer.from(dataToSignString);
const credentialId = Buffer.from(credIdHex, "hex"); const credentialId = Buffer.from(credIdHex, "hex");
@ -200,8 +225,10 @@ export class PeerSetup {
.replace(/=+$/, ""); .replace(/=+$/, "");
const dataInJwt = { const dataInJwt = {
AuthenticationData: authenticatorDataBase64Url, AuthenticationDataB64URL: authenticatorDataBase64Url,
ClientDataJSON: this.clientDataJsonBase64Url, ClientDataJSONB64URL: this.clientDataJsonBase64Url,
iat: Math.floor(Date.now() / 1000),
iss: issuerDid,
}; };
const dataInJwtString = JSON.stringify(dataInJwt); const dataInJwtString = JSON.stringify(dataInJwt);
const payloadBase64 = Buffer.from(dataInJwtString) const payloadBase64 = Buffer.from(dataInJwtString)
@ -284,7 +311,7 @@ export class PeerSetup {
// import { p256 } from "@noble/curves/p256"; // import { p256 } from "@noble/curves/p256";
export async function verifyJwtP256( export async function verifyJwtP256(
credIdHex: string, credIdHex: string,
did: string, issuerDid: string,
authenticatorData: ArrayBuffer, authenticatorData: ArrayBuffer,
challenge: Uint8Array, challenge: Uint8Array,
clientDataJsonBase64Url: Base64URLString, clientDataJsonBase64Url: Base64URLString,
@ -294,7 +321,7 @@ export async function verifyJwtP256(
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64"); const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64"); const sigBuffer = Buffer.from(signature, "base64");
const finalSigBuffer = unwrapEC2Signature(sigBuffer); const finalSigBuffer = unwrapEC2Signature(sigBuffer);
const publicKeyBytes = peerDidToPublicKeyBytes(did); const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
// Hash the client data // Hash the client data
const hash = sha256(clientDataFromBase); const hash = sha256(clientDataFromBase);
@ -312,14 +339,14 @@ export async function verifyJwtP256(
export async function verifyJwtSimplewebauthn( export async function verifyJwtSimplewebauthn(
credIdHex: string, credIdHex: string,
did: string, issuerDid: string,
authenticatorData: ArrayBuffer, authenticatorData: ArrayBuffer,
challenge: Uint8Array, challenge: Uint8Array,
clientDataJsonBase64Url: Base64URLString, clientDataJsonBase64Url: Base64URLString,
signature: Base64URLString, signature: Base64URLString,
) { ) {
const authData = arrayToBase64Url(Buffer.from(authenticatorData)); const authData = arrayToBase64Url(Buffer.from(authenticatorData));
const publicKeyBytes = peerDidToPublicKeyBytes(did); const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
const credId = arrayBufferToBase64URLString( const credId = arrayBufferToBase64URLString(
Buffer.from(credIdHex, "hex").buffer, Buffer.from(credIdHex, "hex").buffer,
); );
@ -351,7 +378,7 @@ export async function verifyJwtSimplewebauthn(
export async function verifyJwtWebCrypto( export async function verifyJwtWebCrypto(
credId: Base64URLString, credId: Base64URLString,
did: string, issuerDid: string,
authenticatorData: ArrayBuffer, authenticatorData: ArrayBuffer,
challenge: Uint8Array, challenge: Uint8Array,
clientDataJsonBase64Url: Base64URLString, clientDataJsonBase64Url: Base64URLString,
@ -368,12 +395,13 @@ export async function verifyJwtWebCrypto(
// Construct the preimage // Construct the preimage
const preimage = Buffer.concat([authDataFromBase, hash]); const preimage = Buffer.concat([authDataFromBase, hash]);
const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
const WebCrypto = await getWebCrypto(); const WebCrypto = await getWebCrypto();
const verifyAlgorithm = { const verifyAlgorithm = {
name: "ECDSA", name: "ECDSA",
hash: { name: "SHA-256" }, hash: { name: "SHA-256" },
}; };
const publicKeyBytes = peerDidToPublicKeyBytes(did);
const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk; const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk;
const keyAlgorithm = { const keyAlgorithm = {
name: "ECDSA", name: "ECDSA",

83
src/views/TestView.vue

@ -221,6 +221,12 @@
p256 - broken p256 - broken
</button> </button>
</div> </div>
<button
@click="verifyMyJwt()"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Verify Mine
</button>
</div> </div>
</section> </section>
</template> </template>
@ -241,11 +247,20 @@ import {
verifyJwtSimplewebauthn, verifyJwtSimplewebauthn,
verifyJwtWebCrypto, verifyJwtWebCrypto,
} from "@/libs/didPeer"; } from "@/libs/didPeer";
import { JWTPayload } from "did-jwt";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
const inputFileNameRef = ref<Blob>(); const inputFileNameRef = ref<Blob>();
const TEST_PAYLOAD = {
vc: {
credentialSubject: {
"@context": "https://schema.org",
"@type": "GiveAction",
description: "pizza",
},
},
};
@Component({ components: { QuickNav } }) @Component({ components: { QuickNav } })
export default class Help extends Vue { export default class Help extends Vue {
// for file import // for file import
@ -265,8 +280,10 @@ export default class Help extends Vue {
this.userName = settings?.firstName as string; this.userName = settings?.firstName as string;
await accountsDB.open(); await accountsDB.open();
const account: { identity?: string } | undefined = const account: { identity?: string } | undefined = await accountsDB.accounts
await accountsDB.accounts.where("did").equals(this.activeDid).first(); .where("did")
.equals(this.activeDid)
.first();
if (this.activeDid) { if (this.activeDid) {
if (account) { if (account) {
this.credIdHex = account.passkeyCredIdHex as string; this.credIdHex = account.passkeyCredIdHex as string;
@ -318,50 +335,24 @@ export default class Help extends Vue {
did: this.activeDid, did: this.activeDid,
passkeyCredIdHex: this.credIdHex, passkeyCredIdHex: this.credIdHex,
publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"), publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"),
}); });``
// await db.settings.update(MASTER_SETTINGS_KEY, {
// activeDid: this.did,
// });
} }
public async createJwtSimplewebauthn() { public async createJwtSimplewebauthn() {
const payload = {
"@context": "https://schema.org",
type: "GiveAction",
description: "pizza",
};
// from createJWT in did-jwt/src/JWT.ts
const timestamps: Partial<JWTPayload> = {
iat: Math.floor(Date.now() / 1000),
};
const fullPayload = { ...timestamps, ...payload, iss: this.activeDid };
this.peerSetup = new PeerSetup(); this.peerSetup = new PeerSetup();
this.jwt = await this.peerSetup.createJwtSimplewebauthn( this.jwt = await this.peerSetup.createJwtSimplewebauthn(
fullPayload, this.activeDid as string,
TEST_PAYLOAD,
this.credIdHex as string, this.credIdHex as string,
); );
console.log("simple jwt4url", this.jwt); console.log("simple jwt4url", this.jwt);
} }
public async createJwtNavigator() { public async createJwtNavigator() {
console.log("generated peer did", this.activeDid);
const payload = {
"@context": "https://schema.org",
type: "GiveAction",
description: "pizza",
};
// from createJWT in did-jwt/src/JWT.ts
const timestamps: Partial<JWTPayload> = {
iat: Math.floor(Date.now() / 1000),
};
const fullPayload = { ...timestamps, ...payload, iss: this.activeDid };
this.peerSetup = new PeerSetup(); this.peerSetup = new PeerSetup();
this.jwt = await this.peerSetup.createJwtNavigator( this.jwt = await this.peerSetup.createJwtNavigator(
fullPayload, this.activeDid as string,
TEST_PAYLOAD,
this.credIdHex as string, this.credIdHex as string,
); );
console.log("lower jwt4url", this.jwt); console.log("lower jwt4url", this.jwt);
@ -402,5 +393,29 @@ export default class Help extends Vue {
); );
console.log("decoded", decoded); console.log("decoded", decoded);
} }
public async verifyMyJwt() {
const jwt =
"eyJ0eXAiOiJKV0FOVCIsImFsZyI6IkVTMjU2In0.eyJBdXRoZW50aWNhdGlvbkRhdGFCNjRVUkwiOiJTWllONVlnT2pHaDBOQmNQWkhaZ1c0X2tycm1paGpMSG1Wenp1b01kbDJNRkFBQUFBQSIsIkNsaWVudERhdGFKU09OQjY0VVJMIjoiZXlKMGVYQmxJam9pZDJWaVlYVjBhRzR1WjJWMElpd2lZMmhoYkd4bGJtZGxJam9pWlhsS01sbDVTVFpsZVVwcVkyMVdhMXBYTlRCaFYwWnpWVE5XYVdGdFZtcGtRMGsyWlhsS1FWa3lPWFZrUjFZMFpFTkpOa2x0YURCa1NFSjZUMms0ZG1NeVRtOWFWekZvVEcwNWVWcDVTWE5KYTBJd1pWaENiRWxxYjJsU01td3lXbFZHYW1SSGJIWmlhVWx6U1cxU2JHTXlUbmxoV0VJd1lWYzVkVWxxYjJsalIydzJaVzFGYVdaWU1ITkpiV3hvWkVOSk5rMVVZM2hQUkZVMFRtcHJOVTFEZDJsaFdFNTZTV3B2YVZwSGJHdFBia0pzV2xoSk5rMUljRXhVVlZweFpHeFdibGRZU2s1TlYyaFpaREJTYW1GV2JFbGhWVVUxVkZob1dXUkZjRkZYUnpWVFZFVndNbU5YT1U1VWEwWk1ZakJTVFZkRWJIZFRNREZZVkVkSmVsWnJVbnBhTTFab1RWaEJlV1ZzWTNobFJtaFRZekp3WVZVeFVrOWpNbG95VkZjMVQyVlZNVlJPTWxKRFRrZHpNMVJyUm05U2JtUk5UVE5DV1ZGdVNrTlhSMlExVjFWdk5XTnRhMmxtVVNJc0ltOXlhV2RwYmlJNkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk9EQTRNQ0lzSW1OeWIzTnpUM0pwWjJsdUlqcG1ZV3h6WlgwIiwiaWF0IjoxNzE4NTg2OTkyLCJpc3MiOiJkaWQ6cGVlcjowektNRmp2VWdZck0xaFh3RGNpWUhpQTlNeFh0SlBYblJMSnZxb01OQUtvRExYOXBLTVdMYjNWRHNndWExcDJ6VzF4WFJzalpTVE5zZnZNbk55TVM3ZEI0azdOQWhGd0wzcFhCckJYZ3lZSjlyaSJ9.MEUCIQDJyCTbMPIFnuBoW3FYnlgtDEIHZ2OrkCEvqVnHU7kJDQIgVxjBjfW1TwQfcSOYwK8Z7AdCWGJlyxtLEsrnPif7caE";
const pieces = jwt.split(".");
console.log("pieces", typeof pieces[1], pieces);
const payload = JSON.parse(Buffer.from(pieces[1], "base64").toString());
const authData = Buffer.from(payload["AuthenticationDataB64URL"], "base64");
const clientJSON = Buffer.from(
payload["ClientDataJSONB64URL"],
"base64",
).toString();
const clientData = JSON.parse(clientJSON);
const challenge = clientData.challenge;
const signatureB64URL = pieces[2];
const decoded = await verifyJwtWebCrypto(
this.credIdHex as Base64URLString,
this.activeDid as string,
authData,
challenge,
payload["ClientDataJSONB64URL"],
signatureB64URL,
);
console.log("decoded", decoded);
}
} }
</script> </script>

Loading…
Cancel
Save