PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/ox/_esm/core
Просмотр файла: WebAuthnP256.js
import * as Base64 from './Base64.js';
import * as Bytes from './Bytes.js';
import * as Errors from './Errors.js';
import * as Hash from './Hash.js';
import * as Hex from './Hex.js';
import * as internal from './internal/webauthn.js';
import * as P256 from './P256.js';
export const createChallenge = Uint8Array.from([
105, 171, 180, 181, 160, 222, 75, 198, 42, 42, 32, 31, 141, 37, 186, 233,
]);
/**
* Creates a new WebAuthn P256 Credential, which can be stored and later used for signing.
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const credential = await WebAuthnP256.createCredential({ name: 'Example' }) // [!code focus]
* // @log: {
* // @log: id: 'oZ48...',
* // @log: publicKey: { x: 51421...5123n, y: 12345...6789n },
* // @log: raw: PublicKeyCredential {},
* // @log: }
*
* const { metadata, signature } = await WebAuthnP256.sign({
* credentialId: credential.id,
* challenge: '0xdeadbeef',
* })
* ```
*
* @param options - Credential creation options.
* @returns A WebAuthn P256 credential.
*/
export async function createCredential(options) {
const { createFn = window.navigator.credentials.create.bind(window.navigator.credentials), ...rest } = options;
const creationOptions = getCredentialCreationOptions(rest);
try {
const credential = (await createFn(creationOptions));
if (!credential)
throw new CredentialCreationFailedError();
const response = credential.response;
const publicKey = await internal.parseCredentialPublicKey(response);
return {
id: credential.id,
publicKey,
raw: credential,
};
}
catch (error) {
throw new CredentialCreationFailedError({
cause: error,
});
}
}
/**
* Gets the authenticator data which contains information about the
* processing of an authenticator request (ie. from `WebAuthnP256.sign`).
*
* :::warning
*
* This function is mainly for testing purposes or for manually constructing
* autenticator data. In most cases you will not need this function.
* `authenticatorData` is typically returned as part of the
* {@link ox#WebAuthnP256.(sign:function)} response (ie. an authenticator response).
*
* :::
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const authenticatorData = WebAuthnP256.getAuthenticatorData({
* rpId: 'example.com',
* signCount: 420,
* })
* // @log: "0xa379a6f6eeafb9a55e378c118034e2751e682fab9f2d30ab13d2125586ce194705000001a4"
* ```
*
* @param options - Options to construct the authenticator data.
* @returns The authenticator data.
*/
export function getAuthenticatorData(options = {}) {
const { flag = 5, rpId = window.location.hostname, signCount = 0 } = options;
const rpIdHash = Hash.sha256(Hex.fromString(rpId));
const flag_bytes = Hex.fromNumber(flag, { size: 1 });
const signCount_bytes = Hex.fromNumber(signCount, { size: 4 });
return Hex.concat(rpIdHash, flag_bytes, signCount_bytes);
}
/**
* Constructs the Client Data in stringified JSON format which represents client data that
* was passed to `credentials.get()` in {@link ox#WebAuthnP256.(sign:function)}.
*
* :::warning
*
* This function is mainly for testing purposes or for manually constructing
* client data. In most cases you will not need this function.
* `clientDataJSON` is typically returned as part of the
* {@link ox#WebAuthnP256.(sign:function)} response (ie. an authenticator response).
*
* :::
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const clientDataJSON = WebAuthnP256.getClientDataJSON({
* challenge: '0xdeadbeef',
* origin: 'https://example.com',
* })
* // @log: "{"type":"webauthn.get","challenge":"3q2-7w","origin":"https://example.com","crossOrigin":false}"
* ```
*
* @param options - Options to construct the client data.
* @returns The client data.
*/
export function getClientDataJSON(options) {
const { challenge, crossOrigin = false, extraClientData, origin = window.location.origin, } = options;
return JSON.stringify({
type: 'webauthn.get',
challenge: Base64.fromHex(challenge, { url: true, pad: false }),
origin,
crossOrigin,
...extraClientData,
});
}
/**
* Returns the creation options for a P256 WebAuthn Credential to be used with
* the Web Authentication API.
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const options = WebAuthnP256.getCredentialCreationOptions({ name: 'Example' })
*
* const credential = await window.navigator.credentials.create(options)
* ```
*
* @param options - Options.
* @returns The credential creation options.
*/
export function getCredentialCreationOptions(options) {
const { attestation = 'none', authenticatorSelection = {
residentKey: 'preferred',
requireResidentKey: false,
userVerification: 'required',
}, challenge = createChallenge, excludeCredentialIds, extensions, name: name_, rp = {
id: window.location.hostname,
name: window.document.title,
}, user, } = options;
const name = (user?.name ?? name_);
return {
publicKey: {
attestation,
authenticatorSelection,
challenge,
...(excludeCredentialIds
? {
excludeCredentials: excludeCredentialIds?.map((id) => ({
id: Base64.toBytes(id),
type: 'public-key',
})),
}
: {}),
pubKeyCredParams: [
{
type: 'public-key',
alg: -7, // p256
},
],
...(extensions && { extensions }),
rp,
user: {
id: user?.id ?? Hash.keccak256(Bytes.fromString(name), { as: 'Bytes' }),
name,
displayName: user?.displayName ?? name,
},
},
};
}
/**
* Returns the request options to sign a challenge with the Web Authentication API.
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const options = WebAuthnP256.getCredentialRequestOptions({
* challenge: '0xdeadbeef',
* })
*
* const credential = await window.navigator.credentials.get(options)
* ```
*
* @param options - Options.
* @returns The credential request options.
*/
export function getCredentialRequestOptions(options) {
const { credentialId, challenge, extensions, rpId = window.location.hostname, userVerification = 'required', } = options;
return {
publicKey: {
...(credentialId
? {
allowCredentials: Array.isArray(credentialId)
? credentialId.map((id) => ({
id: Base64.toBytes(id),
type: 'public-key',
}))
: [
{
id: Base64.toBytes(credentialId),
type: 'public-key',
},
],
}
: {}),
challenge: Bytes.fromHex(challenge),
...(extensions && { extensions }),
rpId,
userVerification,
},
};
}
/**
* Constructs the final digest that was signed and computed by the authenticator. This payload includes
* the cryptographic `challenge`, as well as authenticator metadata (`authenticatorData` + `clientDataJSON`).
* This value can be also used with raw P256 verification (such as {@link ox#P256.(verify:function)} or
* {@link ox#WebCryptoP256.(verify:function)}).
*
* :::warning
*
* This function is mainly for testing purposes or for manually constructing
* signing payloads. In most cases you will not need this function and
* instead use {@link ox#WebAuthnP256.(sign:function)}.
*
* :::
*
* @example
* ```ts twoslash
* import { WebAuthnP256, WebCryptoP256 } from 'ox'
*
* const { metadata, payload } = WebAuthnP256.getSignPayload({ // [!code focus]
* challenge: '0xdeadbeef', // [!code focus]
* }) // [!code focus]
* // @log: {
* // @log: metadata: {
* // @log: authenticatorData: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000",
* // @log: challengeIndex: 23,
* // @log: clientDataJSON: "{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"http://localhost:5173","crossOrigin":false}",
* // @log: typeIndex: 1,
* // @log: userVerificationRequired: true,
* // @log: },
* // @log: payload: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763050000000045086dcb06a5f234db625bcdc94e657f86b76b6fd3eb9c30543eabc1e577a4b0",
* // @log: }
*
* const { publicKey, privateKey } = await WebCryptoP256.createKeyPair()
*
* const signature = await WebCryptoP256.sign({
* payload,
* privateKey,
* })
* ```
*
* @param options - Options to construct the signing payload.
* @returns The signing payload.
*/
export function getSignPayload(options) {
const { challenge, crossOrigin, extraClientData, flag, origin, rpId, signCount, userVerification = 'required', } = options;
const authenticatorData = getAuthenticatorData({
flag,
rpId,
signCount,
});
const clientDataJSON = getClientDataJSON({
challenge,
crossOrigin,
extraClientData,
origin,
});
const clientDataJSONHash = Hash.sha256(Hex.fromString(clientDataJSON));
const challengeIndex = clientDataJSON.indexOf('"challenge"');
const typeIndex = clientDataJSON.indexOf('"type"');
const metadata = {
authenticatorData,
clientDataJSON,
challengeIndex,
typeIndex,
userVerificationRequired: userVerification === 'required',
};
const payload = Hex.concat(authenticatorData, clientDataJSONHash);
return { metadata, payload };
}
/**
* Signs a challenge using a stored WebAuthn P256 Credential. If no Credential is provided,
* a prompt will be displayed for the user to select an existing Credential
* that was previously registered.
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const credential = await WebAuthnP256.createCredential({
* name: 'Example',
* })
*
* const { metadata, signature } = await WebAuthnP256.sign({ // [!code focus]
* credentialId: credential.id, // [!code focus]
* challenge: '0xdeadbeef', // [!code focus]
* }) // [!code focus]
* // @log: {
* // @log: metadata: {
* // @log: authenticatorData: '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000',
* // @log: clientDataJSON: '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"http://localhost:5173","crossOrigin":false}',
* // @log: challengeIndex: 23,
* // @log: typeIndex: 1,
* // @log: userVerificationRequired: true,
* // @log: },
* // @log: signature: { r: 51231...4215n, s: 12345...6789n },
* // @log: }
* ```
*
* @param options - Options.
* @returns The signature.
*/
export async function sign(options) {
const { getFn = window.navigator.credentials.get.bind(window.navigator.credentials), ...rest } = options;
const requestOptions = getCredentialRequestOptions(rest);
try {
const credential = (await getFn(requestOptions));
if (!credential)
throw new CredentialRequestFailedError();
const response = credential.response;
const clientDataJSON = String.fromCharCode(...new Uint8Array(response.clientDataJSON));
const challengeIndex = clientDataJSON.indexOf('"challenge"');
const typeIndex = clientDataJSON.indexOf('"type"');
const signature = internal.parseAsn1Signature(new Uint8Array(response.signature));
return {
metadata: {
authenticatorData: Hex.fromBytes(new Uint8Array(response.authenticatorData)),
clientDataJSON,
challengeIndex,
typeIndex,
userVerificationRequired: requestOptions.publicKey.userVerification === 'required',
},
signature,
raw: credential,
};
}
catch (error) {
throw new CredentialRequestFailedError({
cause: error,
});
}
}
/**
* Verifies a signature using the Credential's public key and the challenge which was signed.
*
* @example
* ```ts twoslash
* import { WebAuthnP256 } from 'ox'
*
* const credential = await WebAuthnP256.createCredential({
* name: 'Example',
* })
*
* const { metadata, signature } = await WebAuthnP256.sign({
* credentialId: credential.id,
* challenge: '0xdeadbeef',
* })
*
* const result = await WebAuthnP256.verify({ // [!code focus]
* metadata, // [!code focus]
* challenge: '0xdeadbeef', // [!code focus]
* publicKey: credential.publicKey, // [!code focus]
* signature, // [!code focus]
* }) // [!code focus]
* // @log: true
* ```
*
* @param options - Options.
* @returns Whether the signature is valid.
*/
export function verify(options) {
const { challenge, hash = true, metadata, publicKey, signature } = options;
const { authenticatorData, challengeIndex, clientDataJSON, typeIndex, userVerificationRequired, } = metadata;
const authenticatorDataBytes = Bytes.fromHex(authenticatorData);
// Check length of `authenticatorData`.
if (authenticatorDataBytes.length < 37)
return false;
const flag = authenticatorDataBytes[32];
// Verify that the UP bit of the flags in authData is set.
if ((flag & 0x01) !== 0x01)
return false;
// If user verification was determined to be required, verify that
// the UV bit of the flags in authData is set. Otherwise, ignore the
// value of the UV flag.
if (userVerificationRequired && (flag & 0x04) !== 0x04)
return false;
// If the BE bit of the flags in authData is not set, verify that
// the BS bit is not set.
if ((flag & 0x08) !== 0x08 && (flag & 0x10) === 0x10)
return false;
// Check that response is for an authentication assertion
const type = '"type":"webauthn.get"';
if (type !== clientDataJSON.slice(Number(typeIndex), type.length + 1))
return false;
// Check that hash is in the clientDataJSON.
const match = clientDataJSON
.slice(Number(challengeIndex))
.match(/^"challenge":"(.*?)"/);
if (!match)
return false;
// Validate the challenge in the clientDataJSON.
const [_, challenge_extracted] = match;
if (Hex.fromBytes(Base64.toBytes(challenge_extracted)) !== challenge)
return false;
const clientDataJSONHash = Hash.sha256(Bytes.fromString(clientDataJSON), {
as: 'Bytes',
});
const payload = Bytes.concat(authenticatorDataBytes, clientDataJSONHash);
return P256.verify({
hash,
payload,
publicKey,
signature,
});
}
/** Thrown when a WebAuthn P256 credential creation fails. */
export class CredentialCreationFailedError extends Errors.BaseError {
constructor({ cause } = {}) {
super('Failed to create credential.', {
cause,
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'WebAuthnP256.CredentialCreationFailedError'
});
}
}
/** Thrown when a WebAuthn P256 credential request fails. */
export class CredentialRequestFailedError extends Errors.BaseError {
constructor({ cause } = {}) {
super('Failed to request credential.', {
cause,
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'WebAuthnP256.CredentialRequestFailedError'
});
}
}
//# sourceMappingURL=WebAuthnP256.js.mapВыполнить команду
Для локальной разработки. Не используйте в интернете!