@ -2,6 +2,7 @@ import { Buffer } from "buffer/";
import { JWTPayload } from "did-jwt" ;
import { DIDResolutionResult } from "did-resolver" ;
import { sha256 } from "ethereum-cryptography/sha256.js" ;
import { p256 } from "@noble/curves/p256" ;
import {
startAuthentication ,
startRegistration ,
@ -11,12 +12,13 @@ import {
generateRegistrationOptions ,
verifyAuthenticationResponse ,
verifyRegistrationResponse ,
VerifyAuthenticationResponseOpts ,
} from "@simplewebauthn/server" ;
import { VerifyAuthenticationResponseOpts } from "@simplewebauthn/server/esm/authentication/verifyAuthenticationResponse" ;
import {
Base64URLString ,
PublicKeyCredentialCreationOptionsJSON ,
PublicKeyCredentialRequestOptionsJSON ,
AuthenticatorAssertionResponse ,
} from "@simplewebauthn/types" ;
import { AppString } from "../../../constants/app" ;
@ -194,16 +196,17 @@ export class PeerSetup {
} ,
} ;
const credential = await navigator . credentials . get ( options ) ;
const credential = await navigator . credentials . get ( options ) as PublicKeyCredential ;
// console.log("nav credential get", credential);
this . authenticatorData = credential ? . response . authenticatorData ;
const response = credential ? . response as AuthenticatorAssertionResponse ;
this . authenticatorData = response ? . authenticatorData ;
const authenticatorDataBase64Url = arrayBufferToBase64URLString (
this . authenticatorData as ArrayBuffer ,
) ;
this . clientDataJsonBase64Url = arrayBufferToBase64URLString (
credential ? . response . clientDataJSON ,
response ? . clientDataJSON ,
) ;
// Our custom type of JWANT means the signature is based on a concatenation of the two Webauthn properties
@ -228,7 +231,7 @@ export class PeerSetup {
. replace ( /\//g , "_" )
. replace ( /=+$/ , "" ) ;
const origSignature = Buffer . from ( credential ? . response . signature ) . toString (
const origSignature = Buffer . from ( response ? . signature ) . toString (
"base64" ,
) ;
this . signature = origSignature
@ -403,102 +406,22 @@ export async function verifyJwtWebCrypto(
return verifyPeerSignature ( preimage , issuerDid , finalSigBuffer ) ;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function peerDidToDidDocument ( did : string ) : Promise < DIDResolutionResult > {
if ( ! did . startsWith ( "did:peer:0z" ) ) {
throw new Error (
"This only verifies a peer DID, method 0, encoded base58btc." ,
) ;
}
// this is basically hard-coded from https://www.w3.org/TR/did-core/#example-various-verification-method-types
// (another reference is the @aviarytech/did-peer resolver)
/ * *
* Looks like JsonWebKey2020 isn ' t too difficult :
* - change context security / suites link to jws - 2020 / v1
* - change publicKeyMultibase to publicKeyJwk generated with cborToKeys
* - change type to JsonWebKey2020
* /
const id = did . split ( ":" ) [ 2 ] ;
const multibase = id . slice ( 1 ) ;
const encnumbasis = multibase . slice ( 1 ) ;
const didDocument = {
"@context" : [
"https://www.w3.org/ns/did/v1" ,
"https://w3id.org/security/suites/secp256k1-2019/v1" ,
] ,
assertionMethod : [ did + "#" + encnumbasis ] ,
authentication : [ did + "#" + encnumbasis ] ,
capabilityDelegation : [ did + "#" + encnumbasis ] ,
capabilityInvocation : [ did + "#" + encnumbasis ] ,
id : did ,
keyAgreement : undefined ,
service : undefined ,
verificationMethod : [
{
controller : did ,
id : did + "#" + encnumbasis ,
publicKeyMultibase : multibase ,
type : "EcdsaSecp256k1VerificationKey2019" ,
} ,
] ,
} ;
return {
didDocument ,
didDocumentMetadata : { } ,
didResolutionMetadata : { contentType : "application/did+ld+json" } ,
} ;
}
// convert COSE public key to PEM format
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function COSEtoPEM ( cose : Buffer ) {
// const alg = cose.get(3); // Algorithm
const x = cose [ - 2 ] ; // x-coordinate
const y = cose [ - 3 ] ; // y-coordinate
// Remove unused functions:
// - peerDidToDidDocument
// - COSEtoPEM
// - base64urlDecodeArrayBuffer
// - base64urlEncodeArrayBuffer
// - pemToCryptoKey
// Ensure the coordinates are in the correct format
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error because it complains about the type of x and y
const pubKeyBuffer = Buffer . concat ( [ Buffer . from ( [ 0x04 ] ) , x , y ] ) ;
// Convert to PEM format
const pem = ` -----BEGIN PUBLIC KEY-----
$ { pubKeyBuffer . toString ( "base64" ) }
-- -- - END PUBLIC KEY -- -- - ` ;
return pem ;
}
// tried the base64url library but got an error using their Buffer
// Keep only the used functions:
export function base64urlDecodeString ( input : string ) {
return atob ( input . replace ( /-/g , "+" ) . replace ( /_/g , "/" ) ) ;
}
// tried the base64url library but got an error using their Buffer
export function base64urlEncodeString ( input : string ) {
return btoa ( input ) . replace ( /\+/g , "-" ) . replace ( /\//g , "_" ) . replace ( /=+$/ , "" ) ;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function base64urlDecodeArrayBuffer ( input : string ) {
input = input . replace ( /-/g , "+" ) . replace ( /_/g , "/" ) ;
const pad = input . length % 4 === 0 ? "" : "====" . slice ( input . length % 4 ) ;
const str = atob ( input + pad ) ;
const bytes = new Uint8Array ( str . length ) ;
for ( let i = 0 ; i < str . length ; i ++ ) {
bytes [ i ] = str . charCodeAt ( i ) ;
}
return bytes . buffer ;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function base64urlEncodeArrayBuffer ( buffer : ArrayBuffer ) {
const str = String . fromCharCode ( . . . new Uint8Array ( buffer ) ) ;
return base64urlEncodeString ( str ) ;
}
// from @simplewebauthn/browser
function arrayBufferToBase64URLString ( buffer : ArrayBuffer ) {
const bytes = new Uint8Array ( buffer ) ;
@ -523,28 +446,3 @@ function base64URLStringToArrayBuffer(base64URLString: string) {
}
return buffer ;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function pemToCryptoKey ( pem : string ) {
const binaryDerString = atob (
pem
. split ( "\n" )
. filter ( ( x ) = > ! x . includes ( "-----" ) )
. join ( "" ) ,
) ;
const binaryDer = new Uint8Array ( binaryDerString . length ) ;
for ( let i = 0 ; i < binaryDerString . length ; i ++ ) {
binaryDer [ i ] = binaryDerString . charCodeAt ( i ) ;
}
// console.log("binaryDer", binaryDer.buffer);
return await window . crypto . subtle . importKey (
"spki" ,
binaryDer . buffer ,
{
name : "RSASSA-PKCS1-v1_5" ,
hash : "SHA-256" ,
} ,
true ,
[ "verify" ] ,
) ;
}