Skip to content

Commit

Permalink
...
Browse files Browse the repository at this point in the history
  • Loading branch information
dagnelies committed Oct 3, 2022
1 parent ba55c5a commit bbffff2
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 102 deletions.
2 changes: 1 addition & 1 deletion dist/webauthn.min.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dist/webauthn.min.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "@passwordless-id/webauthn",
"version": "0.0.6",
"version": "0.0.7",
"description": "A small wrapper around the webauthn protocol to make one's life easier.",
"main": "src/webauthn.ts",
"main": "src/index.ts",
"scripts": {
"build": "esbuild src/webauthn.ts --platform=neutral --bundle --sourcemap --minify --target=es2022 --outfile=dist/webauthn.min.js",
"build": "esbuild src/index.ts --platform=neutral --bundle --sourcemap --minify --target=es2022 --outfile=dist/webauthn.min.js",
"dev": "http-server"
},
"repository": {
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './types'
export * from './webauthn'
export * from './parsers'
export * from './validation'
13 changes: 12 additions & 1 deletion src/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as authenticators from './authenticators'
import * as utils from './utils'

const utf8Decoder = new TextDecoder('utf-8')

Expand All @@ -11,6 +12,16 @@ export function parseAuthenticatorData(buffer :ArrayBuffer) {
return authenticators.parseAuthData(buffer)
}


export function parseAttestationData(buffer :ArrayBuffer) {
return 'Really complex to parse. Good luck with that one!'
}
}

export function parseClientBase64(txt :string) {
return parseClientData( utils.parseBase64url(txt) )
}


export function parseAuthenticatorBase64(txt :string) {
return parseAuthenticatorData( utils.parseBase64url(txt) )
}
21 changes: 21 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

export type AuthType = 'auto' | 'local' | 'extern' | 'both'


// TODO: although algo "-8" is currently only used optionally by a few security keys,
// it would not harm to support it for the sake of completeness
export type NumAlgo = -7 | -257
export type NamedAlgo = 'RS256' | 'ES256'


export interface LoginOptions {
userVerification ?:UserVerificationRequirement,
authenticatorType ?:AuthType,
timeout ?:number,
debug ?:boolean
}


export interface RegisterOptions extends LoginOptions {
attestation?: boolean
}
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function parseBase64url(txt :string) :ArrayBuffer {


export async function sha256(buffer :ArrayBuffer) :Promise<ArrayBuffer> {
return await window.crypto.subtle.digest('SHA-256', buffer)
return await crypto.subtle.digest('SHA-256', buffer)
}

export function bufferToHex (buffer :ArrayBuffer) :string {
Expand Down
8 changes: 7 additions & 1 deletion src/validation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import * as utils from './utils'
import * as parsers from './parsers'
import { NamedAlgo } from './types'





type VerifyParams = {
Expand Down Expand Up @@ -56,4 +62,4 @@ export async function verify({algorithm, publicKey, authenticatorData, clientDat
let validity = verifySignature(algoParams, cryptoKey, signature, comboBuffer)

return validity
}
}
92 changes: 1 addition & 91 deletions src/webauthn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as utils from './utils'
import * as parsers from './parsers'
import { AuthType, LoginOptions, NamedAlgo, NumAlgo, RegisterOptions } from './types'

/**
* Returns whether passwordless authentication is available on this browser/platform or not.
Expand All @@ -16,12 +17,7 @@ export async function isLocalAuthenticator() :Promise<boolean> {
}


// Used mainly for the playground
export function parseAuthenticatorData(authData :string) {
return parsers.parseAuthenticatorData(utils.parseBase64url(authData))
}

type AuthType = 'auto' | 'local' | 'extern' | 'both'

async function getAuthAttachment(authType :AuthType) :Promise<AuthenticatorAttachment|undefined> {
if(authType === "local")
Expand All @@ -45,10 +41,6 @@ async function getAuthAttachment(authType :AuthType) :Promise<AuthenticatorAttac
}


// TODO: although algo "-8" is currently only used optionally by a few security keys,
// it would not harm to support it for the sake of completeness
type NumAlgo = -7 | -257
type NamedAlgo = 'RS256' | 'ES256'

function getAlgoName(num :NumAlgo) :NamedAlgo {
switch(num) {
Expand All @@ -60,18 +52,6 @@ function getAlgoName(num :NumAlgo) :NamedAlgo {
}


interface LoginOptions {
userVerification ?:UserVerificationRequirement,
authenticatorType ?:AuthType,
timeout ?:number,
debug ?:boolean
}


interface RegisterOptions extends LoginOptions {
attestation?: boolean
}


/**
* Creates a cryptographic key pair, in order to register the public key for later passwordless authentication.
Expand Down Expand Up @@ -229,73 +209,3 @@ export async function login(credentialIds :string[], challenge :string, options?
return loginResult
}


export function parseClientBase64(txt :string) {
return parsers.parseClientData( utils.parseBase64url(txt) )
}


export function parseAuthenticatorBase64(txt :string) {
return parsers.parseAuthenticatorData( utils.parseBase64url(txt) )
}




type VerifyParams = {
algorithm :NamedAlgo,
publicKey :string, // Base64url encoded
authenticatorData :string, // Base64url encoded
clientData :string, // Base64url encoded
signature :string, // Base64url encoded
}


function getAlgoParams(algorithm :NamedAlgo) :any {
switch (algorithm) {
case 'RS256':
return {
name:'RSASSA-PKCS1-v1_5',
hash:'SHA-256'
};
case 'ES256':
return {
name: 'ECDSA',
namedCurve: 'P-256',
hash: 'SHA-256',
};
default:
throw new Error(`Unknown or unsupported crypto algorithm: ${algorithm}. Only 'RS256' and 'ES256' are supported.`)
}
}

type AlgoParams = AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm

async function parseCryptoKey(algoParams :AlgoParams, publicKey :string) :Promise<CryptoKey> {
const buffer = utils.parseBase64url(publicKey)
return crypto.subtle.importKey('spki', buffer, algoParams, false, ['verify'])
}

async function verifySignature(algoParams :AlgoParams, cryptoKey :CryptoKey, signature :string, payload :ArrayBuffer) :Promise<boolean> {
const signatureBuffer = utils.parseBase64url(signature)
return crypto.subtle.verify(algoParams, cryptoKey, signatureBuffer, payload)
}

// https://w3c.github.io/webauthn/#sctn-verifying-assertion
export async function verify({algorithm, publicKey, authenticatorData, clientData, signature} :VerifyParams) :Promise<boolean> {
const algoParams = getAlgoParams(algorithm)
let cryptoKey = await parseCryptoKey(algoParams, publicKey)
console.debug(cryptoKey)

let clientHash = await utils.sha256( utils.parseBase64url(clientData) );
console.debug(clientHash)

// during "login", the authenticatorData is exactly 37 bytes
let comboBuffer = utils.concatenateBuffers(utils.parseBase64url(authenticatorData), clientHash)
console.debug(comboBuffer)

// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/verify
let validity = verifySignature(algoParams, cryptoKey, signature, comboBuffer)

return validity
}

0 comments on commit bbffff2

Please sign in to comment.