modify test payload, add test to help with server verification
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user