feat: 초기 프로젝트 설정 및 룰.md 파일 추가
This commit is contained in:
110
api.hyungi.net/node_modules/@simplewebauthn/server/script/helpers/parseAuthenticatorData.js
generated
vendored
Normal file
110
api.hyungi.net/node_modules/@simplewebauthn/server/script/helpers/parseAuthenticatorData.js
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports._parseAuthenticatorDataInternals = void 0;
|
||||
exports.parseAuthenticatorData = parseAuthenticatorData;
|
||||
const decodeAuthenticatorExtensions_js_1 = require("./decodeAuthenticatorExtensions.js");
|
||||
const index_js_1 = require("./iso/index.js");
|
||||
/**
|
||||
* Make sense of the authData buffer contained in an Attestation
|
||||
*/
|
||||
function parseAuthenticatorData(authData) {
|
||||
if (authData.byteLength < 37) {
|
||||
throw new Error(`Authenticator data was ${authData.byteLength} bytes, expected at least 37 bytes`);
|
||||
}
|
||||
let pointer = 0;
|
||||
const dataView = index_js_1.isoUint8Array.toDataView(authData);
|
||||
const rpIdHash = authData.slice(pointer, pointer += 32);
|
||||
const flagsBuf = authData.slice(pointer, pointer += 1);
|
||||
const flagsInt = flagsBuf[0];
|
||||
// Bit positions can be referenced here:
|
||||
// https://www.w3.org/TR/webauthn-2/#flags
|
||||
const flags = {
|
||||
up: !!(flagsInt & (1 << 0)), // User Presence
|
||||
uv: !!(flagsInt & (1 << 2)), // User Verified
|
||||
be: !!(flagsInt & (1 << 3)), // Backup Eligibility
|
||||
bs: !!(flagsInt & (1 << 4)), // Backup State
|
||||
at: !!(flagsInt & (1 << 6)), // Attested Credential Data Present
|
||||
ed: !!(flagsInt & (1 << 7)), // Extension Data Present
|
||||
flagsInt,
|
||||
};
|
||||
const counterBuf = authData.slice(pointer, pointer + 4);
|
||||
const counter = dataView.getUint32(pointer, false);
|
||||
pointer += 4;
|
||||
let aaguid = undefined;
|
||||
let credentialID = undefined;
|
||||
let credentialPublicKey = undefined;
|
||||
if (flags.at) {
|
||||
aaguid = authData.slice(pointer, pointer += 16);
|
||||
const credIDLen = dataView.getUint16(pointer);
|
||||
pointer += 2;
|
||||
credentialID = authData.slice(pointer, pointer += credIDLen);
|
||||
/**
|
||||
* Firefox 117 incorrectly CBOR-encodes authData when EdDSA (-8) is used for the public key.
|
||||
* A CBOR "Map of 3 items" (0xa3) should be "Map of 4 items" (0xa4), and if we manually adjust
|
||||
* the single byte there's a good chance the authData can be correctly parsed.
|
||||
*
|
||||
* This browser release also incorrectly uses the string labels "OKP" and "Ed25519" instead of
|
||||
* their integer representations for kty and crv respectively. That's why the COSE public key
|
||||
* in the hex below looks so odd.
|
||||
*/
|
||||
// Bytes decode to `{ 1: "OKP", 3: -8, -1: "Ed25519" }` (it's missing key -2 a.k.a. COSEKEYS.x)
|
||||
const badEdDSACBOR = index_js_1.isoUint8Array.fromHex('a301634f4b500327206745643235353139');
|
||||
const bytesAtCurrentPosition = authData.slice(pointer, pointer + badEdDSACBOR.byteLength);
|
||||
let foundBadCBOR = false;
|
||||
if (index_js_1.isoUint8Array.areEqual(badEdDSACBOR, bytesAtCurrentPosition)) {
|
||||
// Change the bad CBOR 0xa3 to 0xa4 so that the credential public key can be recognized
|
||||
foundBadCBOR = true;
|
||||
authData[pointer] = 0xa4;
|
||||
}
|
||||
// Decode the next CBOR item in the buffer, then re-encode it back to a Buffer
|
||||
const firstDecoded = index_js_1.isoCBOR.decodeFirst(authData.slice(pointer));
|
||||
const firstEncoded = Uint8Array.from(
|
||||
/**
|
||||
* Casting to `Map` via `as unknown` here because TS doesn't make it possible to define Maps
|
||||
* with discrete keys and properties with known types per pair, and CBOR libs typically parse
|
||||
* CBOR Major Type 5 to `Map` because you can have numbers for keys. A `COSEPublicKey` can be
|
||||
* generalized as "a Map with numbers for keys and either numbers or bytes for values" though.
|
||||
* If this presumption falls apart then other parts of verification later on will fail so we
|
||||
* should be safe doing this here.
|
||||
*/
|
||||
index_js_1.isoCBOR.encode(firstDecoded));
|
||||
if (foundBadCBOR) {
|
||||
// Restore the bit we changed so that `authData` is the same as it came in and won't break
|
||||
// signature verification.
|
||||
authData[pointer] = 0xa3;
|
||||
}
|
||||
credentialPublicKey = firstEncoded;
|
||||
pointer += firstEncoded.byteLength;
|
||||
}
|
||||
let extensionsData = undefined;
|
||||
let extensionsDataBuffer = undefined;
|
||||
if (flags.ed) {
|
||||
const firstDecoded = index_js_1.isoCBOR.decodeFirst(authData.slice(pointer));
|
||||
extensionsDataBuffer = Uint8Array.from(index_js_1.isoCBOR.encode(firstDecoded));
|
||||
extensionsData = (0, decodeAuthenticatorExtensions_js_1.decodeAuthenticatorExtensions)(extensionsDataBuffer);
|
||||
pointer += extensionsDataBuffer.byteLength;
|
||||
}
|
||||
// Pointer should be at the end of the authenticator data, otherwise too much data was sent
|
||||
if (authData.byteLength > pointer) {
|
||||
throw new Error('Leftover bytes detected while parsing authenticator data');
|
||||
}
|
||||
return exports._parseAuthenticatorDataInternals.stubThis({
|
||||
rpIdHash,
|
||||
flagsBuf,
|
||||
flags,
|
||||
counter,
|
||||
counterBuf,
|
||||
aaguid,
|
||||
credentialID,
|
||||
credentialPublicKey,
|
||||
extensionsData,
|
||||
extensionsDataBuffer,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Make it possible to stub the return value during testing
|
||||
* @ignore Don't include this in docs output
|
||||
*/
|
||||
exports._parseAuthenticatorDataInternals = {
|
||||
stubThis: (value) => value,
|
||||
};
|
||||
Reference in New Issue
Block a user