ndwallet-core
Version:
Core cryptographic library for NDWallet browser environments
115 lines (114 loc) • 4.13 kB
JavaScript
/**
* Dynamically load browser dependencies
*/
export async function loadBrowserDependencies() {
console.log('Loading browser dependencies...');
const { startRegistration, startAuthentication } = await import('@simplewebauthn/browser');
return { startRegistration, startAuthentication };
}
/**
* Check if the WebAuthn PRF extension is supported by the browser
* @returns Promise resolving to a boolean indicating if PRF is supported
*/
export async function isPrfExtensionSupported() {
try {
// Check if PublicKeyCredential is available
if (typeof window === 'undefined' || !window.PublicKeyCredential) {
return false;
}
// Check if the PRF extension is supported
// @ts-ignore - isUserVerifyingPlatformAuthenticatorAvailable is not in the type definitions
return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
}
catch (error) {
console.error('Error checking PRF extension support:', error);
return false;
}
}
/**
* Generate WebAuthn registration options with PRF extension
* @param baseOptions - Base registration options from the server
* @param salt - Salt for PRF extension (optional, will generate if not provided)
* @returns Modified options with PRF extension
*/
export function addPrfExtensionToRegistrationOptions(baseOptions, salt) {
// Generate a random salt if not provided
const prfSalt = salt || crypto.getRandomValues(new Uint8Array(32));
// Create a copy of the options
const options = JSON.parse(JSON.stringify(baseOptions));
// Add PRF extension
if (!options.extensions) {
options.extensions = {};
}
options.extensions.prf = {
eval: {
first: prfSalt
}
};
return options;
}
/**
* Generate WebAuthn authentication options with PRF extension
* @param baseOptions - Base authentication options from the server
* @param salt - Salt for PRF extension (must be the same as used during registration)
* @returns Modified options with PRF extension
*/
export function addPrfExtensionToAuthenticationOptions(baseOptions, salt) {
// Create a copy of the options
const options = JSON.parse(JSON.stringify(baseOptions));
// Add PRF extension
if (!options.extensions) {
options.extensions = {};
}
options.extensions.prf = {
eval: {
first: salt
}
};
return options;
}
/**
* Extract PRF output from WebAuthn response
* @param response - WebAuthn response from registration or authentication
* @returns PRF output as Uint8Array or null if not available
*/
function _extractPrfOutput(response) {
try {
const prf = response.clientExtensionResults?.prf;
if (!prf || !prf.results || !prf.results.first) {
console.log('PRF extension not available in response:', prf);
return null;
}
return prf.results.first;
}
catch (error) {
console.error('Error extracting PRF output:', error);
return null;
}
}
/**
* Derive a master key using the WebAuthn PRF extension
* @param response - WebAuthn response from registration or authentication
* @returns Promise resolving to a CryptoKey that can be used for key derivation
* @throws {Error} If PRF extension is not supported or response is invalid
*/
export async function deriveMasterKeyFromPrf(response) {
try {
// Extract PRF output
const prfOutput = _extractPrfOutput(response);
if (!prfOutput) {
throw new Error('No PRF output found in response. Make sure PRF extension was requested and is supported.');
}
// Import as a CryptoKey for key derivation
return await crypto.subtle.importKey('raw', prfOutput, { name: 'HKDF' }, false, ['deriveKey']);
}
catch (error) {
console.error('Error deriving master key from PRF:', error);
if (error instanceof Error) {
throw error;
}
else {
throw new Error(`Failed to derive master key from PRF: ${String(error)}`);
}
}
}