mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 09:49:24 +02:00
refactor(credential): improve error handling and documentation
This commit is contained in:
parent
b45b0ba979
commit
dafbde3a8e
3 changed files with 90 additions and 27 deletions
|
@ -1,11 +1,35 @@
|
||||||
export interface TangoPrfSource {
|
import type { MaybePromiseLike } from "@yume-chan/async";
|
||||||
create(input: Uint8Array<ArrayBuffer>): Promise<{
|
|
||||||
output: BufferSource;
|
|
||||||
id: Uint8Array<ArrayBuffer>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
interface TangoPrfCreationResult {
|
||||||
|
/**
|
||||||
|
* The generated PRF output
|
||||||
|
*/
|
||||||
|
output: BufferSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the created secret key
|
||||||
|
*/
|
||||||
|
id: Uint8Array<ArrayBuffer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TangoPrfSource {
|
||||||
|
/**
|
||||||
|
* Creates a new secret key and generate PRF output using the key and input data.
|
||||||
|
*
|
||||||
|
* @param input The input data
|
||||||
|
*/
|
||||||
|
create(
|
||||||
|
input: Uint8Array<ArrayBuffer>,
|
||||||
|
): MaybePromiseLike<TangoPrfCreationResult>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates PRF output using the secret key and input data.
|
||||||
|
*
|
||||||
|
* @param id ID of the secret key
|
||||||
|
* @param input The input data
|
||||||
|
*/
|
||||||
get(
|
get(
|
||||||
id: BufferSource,
|
id: BufferSource,
|
||||||
input: Uint8Array<ArrayBuffer>,
|
input: Uint8Array<ArrayBuffer>,
|
||||||
): Promise<BufferSource>;
|
): MaybePromiseLike<BufferSource>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,11 +64,20 @@ const Bundle = struct(
|
||||||
{ littleEndian: true },
|
{ littleEndian: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `TangoDataStorage` that encrypts and decrypts data using PRF
|
||||||
|
*/
|
||||||
export class TangoPrfStorage implements TangoKeyStorage {
|
export class TangoPrfStorage implements TangoKeyStorage {
|
||||||
readonly #storage: TangoKeyStorage;
|
readonly #storage: TangoKeyStorage;
|
||||||
readonly #source: TangoPrfSource;
|
readonly #source: TangoPrfSource;
|
||||||
#prevId: Uint8Array<ArrayBuffer> | undefined;
|
#prevId: Uint8Array<ArrayBuffer> | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of `TangoPrfStorage`
|
||||||
|
*
|
||||||
|
* @param storage Another `TangoDataStorage` to store and retrieve the encrypted data
|
||||||
|
* @param source The `TangoPrfSource` to generate PRF output
|
||||||
|
*/
|
||||||
constructor(storage: TangoKeyStorage, source: TangoPrfSource) {
|
constructor(storage: TangoKeyStorage, source: TangoPrfSource) {
|
||||||
this.#storage = storage;
|
this.#storage = storage;
|
||||||
this.#source = source;
|
this.#source = source;
|
||||||
|
|
|
@ -25,16 +25,24 @@ class NotSupportedError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssertionFailedError extends Error {
|
class OperationCancelledError extends Error {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Assertion failed");
|
super("The operation is either cancelled by user or timed out");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
static NotSupportedError = NotSupportedError;
|
static NotSupportedError = NotSupportedError;
|
||||||
static AssertionFailedError = AssertionFailedError;
|
static OperationCancelledError = OperationCancelledError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the runtime supports WebAuthn PRF extension.
|
||||||
|
*
|
||||||
|
* Note that using the extension also requires a supported authenticator.
|
||||||
|
* Whether an authenticator supports the extension can only be checked
|
||||||
|
* during the `create` process.
|
||||||
|
* @returns `true` if the runtime supports WebAuthn PRF extension
|
||||||
|
*/
|
||||||
static async isSupported(): Promise<boolean> {
|
static async isSupported(): Promise<boolean> {
|
||||||
if (typeof PublicKeyCredential === "undefined") {
|
if (typeof PublicKeyCredential === "undefined") {
|
||||||
return false;
|
return false;
|
||||||
|
@ -57,7 +65,8 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
readonly #userName: string;
|
readonly #userName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of TangoWebAuthnPrfSource
|
* Creates a new instance of `TangoWebAuthnPrfSource`
|
||||||
|
*
|
||||||
* @param appName Name of your website shows in Passkey manager
|
* @param appName Name of your website shows in Passkey manager
|
||||||
* @param userName Display name of the credential shows in Passkey manager
|
* @param userName Display name of the credential shows in Passkey manager
|
||||||
*/
|
*/
|
||||||
|
@ -66,6 +75,14 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
this.#userName = userName;
|
this.#userName = userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new credential and generate PRF output using the credential and input data.
|
||||||
|
*
|
||||||
|
* @param input The input data
|
||||||
|
* @returns The credential ID and PRF output
|
||||||
|
* @throws `NotSupportedError` if the runtime or authenticator doesn't support PRF extension
|
||||||
|
* @throws `OperationCancelledError` if the attestation is either cancelled by user or timed out
|
||||||
|
*/
|
||||||
async create(input: Uint8Array<ArrayBuffer>): Promise<{
|
async create(input: Uint8Array<ArrayBuffer>): Promise<{
|
||||||
output: BufferSource;
|
output: BufferSource;
|
||||||
id: Uint8Array<ArrayBuffer>;
|
id: Uint8Array<ArrayBuffer>;
|
||||||
|
@ -73,22 +90,27 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
const challenge = new Uint8Array(32);
|
const challenge = new Uint8Array(32);
|
||||||
crypto.getRandomValues(challenge);
|
crypto.getRandomValues(challenge);
|
||||||
|
|
||||||
const attestation = await navigator.credentials.create({
|
let attestation;
|
||||||
publicKey: {
|
try {
|
||||||
challenge,
|
attestation = await navigator.credentials.create({
|
||||||
extensions: { prf: { eval: { first: input } } },
|
publicKey: {
|
||||||
pubKeyCredParams: [
|
challenge,
|
||||||
{ type: "public-key", alg: -7 },
|
extensions: { prf: { eval: { first: input } } },
|
||||||
{ type: "public-key", alg: -257 },
|
pubKeyCredParams: [
|
||||||
],
|
{ type: "public-key", alg: -7 },
|
||||||
rp: { name: this.#appName },
|
{ type: "public-key", alg: -257 },
|
||||||
user: {
|
],
|
||||||
id: challenge,
|
rp: { name: this.#appName },
|
||||||
name: this.#userName,
|
user: {
|
||||||
displayName: this.#userName,
|
id: challenge,
|
||||||
|
name: this.#userName,
|
||||||
|
displayName: this.#userName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
} catch {
|
||||||
|
throw new OperationCancelledError();
|
||||||
|
}
|
||||||
|
|
||||||
checkCredential(attestation);
|
checkCredential(attestation);
|
||||||
|
|
||||||
|
@ -108,6 +130,14 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
return { output, id };
|
return { output, id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates PRF output using a credential and input data.
|
||||||
|
*
|
||||||
|
* @param id ID of a previously created credential
|
||||||
|
* @param input The input data
|
||||||
|
* @returns PRF output
|
||||||
|
* @throws `OperationCancelledError` if the attestation is either cancelled by user or timed out
|
||||||
|
*/
|
||||||
async get(
|
async get(
|
||||||
id: BufferSource,
|
id: BufferSource,
|
||||||
input: Uint8Array<ArrayBuffer>,
|
input: Uint8Array<ArrayBuffer>,
|
||||||
|
@ -125,7 +155,7 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
throw new AssertionFailedError();
|
throw new OperationCancelledError();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkCredential(assertion);
|
checkCredential(assertion);
|
||||||
|
@ -141,5 +171,5 @@ export class TangoWebAuthnPrfSource implements TangoPrfSource {
|
||||||
|
|
||||||
export namespace TangoWebAuthnPrfSource {
|
export namespace TangoWebAuthnPrfSource {
|
||||||
export type NotSupportedError = typeof NotSupportedError;
|
export type NotSupportedError = typeof NotSupportedError;
|
||||||
export type AssertionFailedError = typeof AssertionFailedError;
|
export type OperationCancelledError = typeof OperationCancelledError;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue