Browse Source

remove duplicate data elements, including public key which is in peer DID

pull/116/head
Trent Larson 3 months ago
parent
commit
c661dee520
  1. 29
      src/db/tables/accounts.ts
  2. 135
      src/libs/didPeer.ts
  3. 37
      src/views/TestView.vue

29
src/db/tables/accounts.ts

@ -3,41 +3,46 @@
*/
export type Account = {
/**
* Auto-generated ID by Dexie.
* Auto-generated ID by Dexie
*/
id?: number;
/**
* The date the account was created.
* The date the account was created
*/
dateCreated: string;
/**
* The derivation path for the account.
* The derivation path for the account, if this is from a mnemonic
*/
derivationPath: string;
derivationPath?: string;
/**
* Decentralized Identifier (DID) for the account.
* Decentralized Identifier (DID) for the account
*/
did: string;
/**
* Stringified JSON containing underlying key material.
* Based on the IIdentifier type from Veramo.
* Stringified JSON containing underlying key material, if generated from a mnemonic
* Based on the IIdentifier type from Veramo
* @see {@link https://github.com/uport-project/veramo/blob/next/packages/core-types/src/types/IIdentifier.ts}
*/
identity: string;
identity?: string;
/**
* The public key in hexadecimal format.
* The mnemonic phrase for the account, if this is from a mnemonic
*/
publicKeyHex: string;
mnemonic?: string;
/**
* The mnemonic passphrase for the account.
* The Webauthn credential ID, if this is from a passkey
*/
mnemonic: string;
passkeyCredId?: string;
/**
* The public key in hexadecimal format.
*/
publicKeyHex: string;
};
/**

135
src/libs/didPeer.ts

@ -1,10 +1,9 @@
import asn1 from "asn1-ber";
import { Buffer } from "buffer/";
import { decode as cborDecode } from "cbor-x";
import { JWTPayload } from "did-jwt";
import { bytesToMultibase, JWTPayload, multibaseToBytes } from "did-jwt";
import { DIDResolutionResult } from "did-resolver";
import { sha256 } from "ethereum-cryptography/sha256.js";
import { bytesToMultibase } from "@veramo/utils";
import {
startAuthentication,
startRegistration,
@ -24,6 +23,7 @@ import {
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
const PEER_DID_PREFIX = "did:peer:0";
export interface JWK {
kty: string;
crv: string;
@ -97,14 +97,16 @@ export function createPeerDid(publicKeyBytes: Uint8Array) {
"base58btc",
"p256-pub",
);
return "did:peer:0" + methodSpecificId;
return PEER_DID_PREFIX + methodSpecificId;
}
function peerDidToPublicKeyBytes(did: string) {
return multibaseToBytes(did.substring(PEER_DID_PREFIX.length));
}
export class PeerSetup {
public authenticatorData?: ArrayBuffer;
public authenticatorDataBase64Url?: Base64URLString;
public challenge?: Uint8Array;
public clientDataJsonDecoded?: object;
public clientDataJsonBase64Url?: Base64URLString;
public signature?: Base64URLString;
@ -124,17 +126,12 @@ export class PeerSetup {
const clientAuth = await startAuthentication(options);
// console.log("simple credential get", clientAuth);
this.authenticatorDataBase64Url = clientAuth.response.authenticatorData;
const authenticatorDataBase64Url = clientAuth.response.authenticatorData;
this.authenticatorData = Buffer.from(
clientAuth.response.authenticatorData,
"base64",
).buffer;
this.clientDataJsonBase64Url = clientAuth.response.clientDataJSON;
this.clientDataJsonDecoded = JSON.parse(
Buffer.from(clientAuth.response.clientDataJSON, "base64").toString(
"utf-8",
),
);
// console.log("simple authenticatorData for signing", this.authenticatorData);
this.signature = clientAuth.response.signature;
@ -147,7 +144,7 @@ export class PeerSetup {
.replace(/=+$/, "");
const dataInJwt = {
AuthenticationData: this.authenticatorDataBase64Url,
AuthenticationData: authenticatorDataBase64Url,
ClientDataJSON: this.clientDataJsonBase64Url,
};
const dataInJwtString = JSON.stringify(dataInJwt);
@ -175,20 +172,14 @@ export class PeerSetup {
const credential = await navigator.credentials.get(options);
// console.log("nav credential get", credential);
this.authenticatorDataBase64Url = bufferToBase64URLString(
credential?.response.authenticatorData,
this.authenticatorData = credential?.response.authenticatorData;
const authenticatorDataBase64Url = bufferToBase64URLString(
this.authenticatorData,
);
this.authenticatorData = Buffer.from(
this.authenticatorDataBase64Url as Base64URLString,
"base64",
).buffer;
this.clientDataJsonBase64Url = bufferToBase64URLString(
credential?.response.clientDataJSON,
);
this.clientDataJsonDecoded = JSON.parse(
new TextDecoder("utf-8").decode(credential?.response.clientDataJSON),
);
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
const header: JWTPayload = { typ: "JWANT", alg: "ES256" };
@ -199,7 +190,7 @@ export class PeerSetup {
.replace(/=+$/, "");
const dataInJwt = {
AuthenticationData: this.authenticatorDataBase64Url,
AuthenticationData: authenticatorDataBase64Url,
ClientDataJSON: this.clientDataJsonBase64Url,
};
const dataInJwtString = JSON.stringify(dataInJwt);
@ -276,20 +267,51 @@ export class PeerSetup {
// }
}
// I'd love to use this but it doesn't verify.
// Pequires:
// npm install @noble/curves
// ... and this import:
// import { p256 } from "@noble/curves/p256";
export async function verifyJwtP256(
credId: Base64URLString,
rawId: Uint8Array,
did: string,
authenticatorData: ArrayBuffer,
challenge: Uint8Array,
clientDataJsonBase64Url: Base64URLString,
signature: Base64URLString,
) {
const authDataFromBase = Buffer.from(authenticatorData);
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64");
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
const publicKeyBytes = peerDidToPublicKeyBytes(did);
// Hash the client data
const hash = sha256(clientDataFromBase);
// Construct the preimage
const preimage = Buffer.concat([authDataFromBase, hash]);
const isValid = p256.verify(
finalSigBuffer,
new Uint8Array(preimage),
publicKeyBytes,
);
return isValid;
}
export async function verifyJwtSimplewebauthn(
jwt: string,
credId: Base64URLString,
rawId: Uint8Array,
did: string,
authenticatorData: ArrayBuffer,
authenticatorDataBase64Url: Base64URLString,
challenge: Uint8Array,
clientDataJSON: object,
clientDataJsonBase64Url: Base64URLString,
publicKeyBytes: Uint8Array,
publicKeyJwk: JWK,
signature: Base64URLString,
) {
const authData = arrayToBase64Url(Buffer.from(authenticatorData));
const publicKeyBytes = peerDidToPublicKeyBytes(did);
const authOpts: VerifyAuthenticationResponseOpts = {
authenticator: {
credentialID: credId,
@ -306,9 +328,7 @@ export async function verifyJwtSimplewebauthn(
rawId: arrayToBase64Url(rawId),
response: {
authenticatorData: authData,
clientDataJSON: arrayToBase64Url(
Buffer.from(JSON.stringify(clientDataJSON)),
),
clientDataJSON: clientDataJsonBase64Url,
signature: signature,
},
type: "public-key",
@ -318,57 +338,16 @@ export async function verifyJwtSimplewebauthn(
return verification.verified;
}
// I'd love to use this but it doesn't verify.
// Pequires:
// npm install @noble/curves
// ... and this import:
// import { p256 } from "@noble/curves/p256";
export async function verifyJwtP256(
jwt: string,
credId: Base64URLString,
rawId: Uint8Array,
authenticatorData: ArrayBuffer,
authenticatorDataBase64Url: Base64URLString,
challenge: Uint8Array,
clientDataJSON: object,
clientDataJsonBase64Url: Base64URLString,
publicKeyBytes: Uint8Array,
publicKeyJwk: JWK,
signature: Base64URLString,
) {
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64");
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
// Hash the client data
const hash = sha256(clientDataFromBase);
// Construct the preimage
const preimage = Buffer.concat([authDataFromBase, hash]);
const isValid = p256.verify(
finalSigBuffer,
new Uint8Array(preimage),
publicKeyBytes,
);
return isValid;
}
export async function verifyJwtWebCrypto(
jwt: string,
credId: Base64URLString,
rawId: Uint8Array,
did: string,
authenticatorData: ArrayBuffer,
authenticatorDataBase64Url: Base64URLString,
challenge: Uint8Array,
clientDataJSON: object,
clientDataJsonBase64Url: Base64URLString,
publicKeyBytes: Uint8Array,
publicKeyJwk: JWK,
signature: Base64URLString,
) {
const authDataFromBase = Buffer.from(authenticatorDataBase64Url, "base64");
const authDataFromBase = Buffer.from(authenticatorData);
const clientDataFromBase = Buffer.from(clientDataJsonBase64Url, "base64");
const sigBuffer = Buffer.from(signature, "base64");
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
@ -384,6 +363,8 @@ export async function verifyJwtWebCrypto(
name: "ECDSA",
hash: { name: "SHA-256" },
};
const publicKeyBytes = peerDidToPublicKeyBytes(did);
const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk;
const keyAlgorithm = {
name: "ECDSA",
namedCurve: publicKeyJwk.crv,
@ -479,19 +460,19 @@ function base64urlEncode(buffer: ArrayBuffer) {
// from @simplewebauthn/browser
function bufferToBase64URLString(buffer) {
const bytes = new Uint8Array(buffer);
let str = '';
let str = "";
for (const charCode of bytes) {
str += String.fromCharCode(charCode);
}
const base64String = btoa(str);
return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
// from @simplewebauthn/browser
function base64URLStringToBuffer(base64URLString) {
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/');
const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
const padLength = (4 - (base64.length % 4)) % 4;
const padded = base64.padEnd(base64.length + padLength, '=');
const padded = base64.padEnd(base64.length + padLength, "=");
const binary = atob(padded);
const buffer = new ArrayBuffer(binary.length);
const bytes = new Uint8Array(buffer);

37
src/views/TestView.vue

@ -231,9 +231,9 @@ import { db } from "@/db/index";
import { generateRandomBytes } from "@/libs/crypto";
import {
createPeerDid,
JWK,
PeerSetup,
registerCredential, verifyJwtP256,
registerCredential,
verifyJwtP256,
verifyJwtSimplewebauthn,
verifyJwtWebCrypto,
} from "@/libs/didPeer";
@ -248,10 +248,9 @@ export default class Help extends Vue {
// for passkeys
credId?: Base64URLString;
did?: string;
jwt?: string;
peerSetup?: PeerSetup;
publicKeyJwk?: JWK;
publicKeyBytes?: Uint8Array;
rawId?: Uint8Array;
userId?: ArrayBuffer;
@ -290,15 +289,14 @@ export default class Help extends Vue {
const cred = await registerCredential(this.userId as Uint8Array);
console.log("public key", cred);
this.publicKeyJwk = cred.publicKeyJwk;
this.publicKeyBytes = cred.publicKeyBytes;
this.did = createPeerDid(this.publicKeyBytes as Uint8Array);
this.credId = cred.credId as string;
this.rawId = cred.rawId as Uint8Array;
}
public async createJwtSimplewebauthn() {
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
console.log("generated peer did", did);
console.log("generated peer did", this.did);
const payload = {
"@context": "https://schema.org",
@ -310,7 +308,7 @@ export default class Help extends Vue {
iat: Math.floor(Date.now() / 1000),
exp: undefined,
};
const fullPayload = { ...timestamps, ...payload, did };
const fullPayload = { ...timestamps, ...payload, iss: this.did };
this.peerSetup = new PeerSetup();
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
@ -321,8 +319,7 @@ export default class Help extends Vue {
}
public async createJwtNavigator() {
const did = createPeerDid(this.publicKeyBytes as Uint8Array);
console.log("generated peer did", did);
console.log("generated peer did", this.did);
const payload = {
"@context": "https://schema.org",
@ -334,7 +331,7 @@ export default class Help extends Vue {
iat: Math.floor(Date.now() / 1000),
exp: undefined,
};
const fullPayload = { ...timestamps, ...payload, did };
const fullPayload = { ...timestamps, ...payload, iss: this.did };
this.peerSetup = new PeerSetup();
this.jwt = await this.peerSetup.createJwtNavigator(
@ -346,16 +343,12 @@ export default class Help extends Vue {
public async verifyP256() {
const decoded = await verifyJwtP256(
this.jwt as string,
this.credId as Base64URLString,
this.rawId as Uint8Array,
this.did as string,
this.peerSetup.authenticatorData as ArrayBuffer,
this.peerSetup.authenticatorDataBase64Url as Base64URLString,
this.peerSetup.challenge as Uint8Array,
this.peerSetup.clientDataJsonDecoded,
this.peerSetup.clientDataJsonBase64Url as Base64URLString,
this.publicKeyBytes as Uint8Array,
this.publicKeyJwk as JWK,
this.peerSetup.signature as Base64URLString,
);
console.log("decoded", decoded);
@ -363,16 +356,12 @@ export default class Help extends Vue {
public async verifySimplewebauthn() {
const decoded = await verifyJwtSimplewebauthn(
this.jwt as string,
this.credId as Base64URLString,
this.rawId as Uint8Array,
this.did as string,
this.peerSetup.authenticatorData as ArrayBuffer,
this.peerSetup.authenticatorDataBase64Url as Base64URLString,
this.peerSetup.challenge as Uint8Array,
this.peerSetup.clientDataJsonDecoded,
this.peerSetup.clientDataJsonBase64Url as Base64URLString,
this.publicKeyBytes as Uint8Array,
this.publicKeyJwk as JWK,
this.peerSetup.signature as Base64URLString,
);
console.log("decoded", decoded);
@ -380,16 +369,12 @@ export default class Help extends Vue {
public async verifyWebCrypto() {
const decoded = await verifyJwtWebCrypto(
this.jwt as string,
this.credId as Base64URLString,
this.rawId as Uint8Array,
this.did as string,
this.peerSetup.authenticatorData as ArrayBuffer,
this.peerSetup.authenticatorDataBase64Url as Base64URLString,
this.peerSetup.challenge as Uint8Array,
this.peerSetup.clientDataJsonDecoded,
this.peerSetup.clientDataJsonBase64Url as Base64URLString,
this.publicKeyBytes as Uint8Array,
this.publicKeyJwk as JWK,
this.peerSetup.signature as Base64URLString,
);
console.log("decoded", decoded);

Loading…
Cancel
Save