Skip to content

Commit f6d237d

Browse files
HSMDBCHexagon
andauthored
Extension handling and type fixes (#105)
* extension handling + type fixes * test * -test * reverted dist * reverted dist #2 * reverted dist #3 * code tests for extensions - extension tests (with and without AT-Flag) - fixed missing Uint8Array for extension processing * Update dependencies + Revert package name, fixing PR (#2) * Revert package name to fido2-lib * Update dependencies * Re-fix versions to patch Co-authored-by: Hexagon <robinnilsson@gmail.com>
1 parent d2f6629 commit f6d237d

13 files changed

+453
-155
lines changed

deno-lock.json

Lines changed: 86 additions & 86 deletions
Large diffs are not rendered by default.

import_map.dist.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
"std/": "https://deno.land/std@0.136.0/",
66

7-
"@hexagon/base64": "https://deno.land/x/b64@1.1.21/dist/base64.min.mjs",
7+
"@hexagon/base64": "https://deno.land/x/b64@1.1.23/dist/base64.min.mjs",
88

99
"sinon": "https://unpkg.com/sinon@14.0.0/pkg/sinon-esm.js",
1010
"test_suite": "https://deno.land/x/test_suite@0.16.1/mod.ts",

import_map.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"imports": {
3-
"tldts": "https://unpkg.com/tldts@5.7.82/dist/index.esm.min.js",
3+
"tldts": "https://unpkg.com/tldts@5.7.90/dist/index.esm.min.js",
44
"punycode": "https://deno.land/x/punycode@v2.1.1/punycode.js",
5-
"jose": "https://deno.land/x/jose@v4.8.3/index.ts?module",
5+
"jose": "https://deno.land/x/jose@v4.9.1/index.ts?module",
66
"asn1js": "https://unpkg.com/asn1js@3.0.5?module",
7-
"cbor-x": "https://deno.land/x/cbor@v1.3.1/index.js?module",
7+
"cbor-x": "https://deno.land/x/cbor@v1.4.0/index.js?module",
88
"std/": "https://deno.land/std@0.136.0/",
9-
"pkijs": "https://unpkg.com/pkijs@3.0.5?module",
10-
"@hexagon/base64": "https://deno.land/x/b64@1.1.21/dist/base64.min.mjs",
9+
"pkijs": "https://unpkg.com/pkijs@3.0.7?module",
10+
"@hexagon/base64": "https://deno.land/x/b64@1.1.23/dist/base64.min.mjs",
1111

1212
"sinon": "https://unpkg.com/sinon@14.0.0/pkg/sinon-esm.js",
1313
"test_suite": "https://deno.land/x/test_suite@0.16.1/mod.ts",

lib/keyUtils.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,17 @@ class PublicKey {
407407

408408
let parsedCose;
409409
try {
410-
parsedCose = tools.cbor.decode(new Uint8Array(cose));
410+
// In the current state, the "cose" parameter can contain not only the actual cose (= public key) but also extensions.
411+
// Both are CBOR encoded entries, so you can treat and evaluate the "cose" parameter accordingly.
412+
// "fromCose" is called from a context that contains an active AT flag (attestation), so the first CBOR entry is the actual cose.
413+
// "tools.cbor.decode" will fail when multiple entries are provided (e.g. cose + at least one extension), so "decodeMultiple" is the sollution.
414+
tools.cbor.decodeMultiple(
415+
new Uint8Array(cose),
416+
cborObject => {
417+
parsedCose = cborObject;
418+
return false;
419+
}
420+
);
411421
} catch (err) {
412422
throw new Error(
413423
"couldn't parse authenticator.authData.attestationData CBOR: " +

lib/main.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Fido2Lib {
4141
* @param {String} [opts.rpName="Anonymous Service"] The name of the server
4242
* @param {String} [opts.rpIcon] A URL for the service's icon. Can be a [RFC 2397]{@link https://tools.ietf.org/html/rfc2397} data URL.
4343
* @param {Number} [opts.challengeSize=64] The number of bytes to use for the challenge
44-
* @param {Object} [opts.authenticatorSelectionCriteria] An object describing what types of authenticators are allowed to register with the service.
44+
* @param {Object} [opts.authenticatorSelection] An object describing what types of authenticators are allowed to register with the service.
4545
* See [AuthenticatorSelectionCriteria]{@link https://w3.org/TR/webauthn/#authenticatorSelection} in the WebAuthn spec for details.
4646
* @param {String} [opts.authenticatorAttachment] Indicates whether authenticators should be part of the OS ("platform"), or can be roaming authenticators ("cross-platform")
4747
* @param {Boolean} [opts.authenticatorRequireResidentKey] Indicates whether authenticators must store the key internally (true) or if they can use a KDF to generate keys
@@ -645,13 +645,13 @@ class Fido2Lib {
645645
* @property {Array} [pubKeyCredParams] A list of PublicKeyCredentialParameters objects, based on the `cryptoParams` that was passed to the constructor.
646646
* @property {Number} [timeout] The amount of time that the call should take before returning an error
647647
* @property {String} [attestation] Whether the client should request attestation from the authenticator or not
648-
* @property {Object} [authenticatorSelectionCriteria] A object describing which authenticators are preferred for registration
649-
* @property {String} [authenticatorSelectionCriteria.attachment] What type of attachement is acceptable for new authenticators.
648+
* @property {Object} [authenticatorSelection] A object describing which authenticators are preferred for registration
649+
* @property {String} [authenticatorSelection.attachment] What type of attachement is acceptable for new authenticators.
650650
* Allowed values are "platform", meaning that the authenticator is embedded in the operating system, or
651651
* "cross-platform", meaning that the authenticator is removeable (e.g. USB, NFC, or BLE).
652-
* @property {Boolean} [authenticatorSelectionCriteria.requireResidentKey] Indicates whether authenticators must store the keys internally, or if they can
652+
* @property {Boolean} [authenticatorSelection.requireResidentKey] Indicates whether authenticators must store the keys internally, or if they can
653653
* store them externally (using a KDF or key wrapping)
654-
* @property {String} [authenticatorSelectionCriteria.userVerification] Indicates whether user verification is required for authenticators. User verification
654+
* @property {String} [authenticatorSelection.userVerification] Indicates whether user verification is required for authenticators. User verification
655655
* means that an authenticator will validate a use through their biometrics (e.g. fingerprint) or knowledge (e.g. PIN). Allowed
656656
* values for `userVerification` are "required", meaning that registration will fail if no authenticator provides user verification;
657657
* "preferred", meaning that if multiple authenticators are available, the one(s) that provide user verification should be used; or

lib/parser.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,15 +337,25 @@ async function parseAuthenticatorData(authnrDataArrayBuffer) {
337337
authnrDataBuf.buffer.slice(offset, authnrDataBuf.buffer.byteLength),
338338
);
339339

340+
// TODO: does not only contain the COSE if the buffer contains extensions
340341
ret.set("credentialPublicKeyCose", await publicKey.toCose());
341342
ret.set("credentialPublicKeyJwk", await publicKey.toJwk());
342343
ret.set("credentialPublicKeyPem", await publicKey.toPem());
343344
}
344345

345-
// TODO: parse extensions
346346
if (extensions) {
347-
// extensionStart = offset
348-
throw new Error("authenticator extensions not supported");
347+
const cborObjects = tools.cbor.decodeMultiple(new Uint8Array(authnrDataBuf.buffer.slice(offset, authnrDataBuf.buffer.byteLength)));
348+
349+
// skip publicKey if present
350+
if (attestation) {
351+
cborObjects.shift();
352+
}
353+
354+
if (cborObjects.length === 0) {
355+
throw new Error("extensions missing");
356+
}
357+
358+
ret.set("webAuthnExtensions", cborObjects);
349359
}
350360

351361
return ret;

package-lock.json

Lines changed: 45 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fido2-lib",
3-
"version": "3.2.5",
3+
"version": "3.3.0",
44
"description": "A library for performing FIDO 2.0 / WebAuthn functionality",
55
"type": "module",
66
"main": "dist/main.cjs",
@@ -49,13 +49,13 @@
4949
"sinon": "^14.0.0"
5050
},
5151
"dependencies": {
52-
"@hexagon/base64": "~1.1.21",
52+
"@hexagon/base64": "~1.1.23",
5353
"@peculiar/webcrypto": "~1.4.0",
5454
"asn1js": "~3.0.2",
55-
"cbor-x": "~1.3.1",
56-
"jose": "~4.8.3",
57-
"pkijs": "~3.0.5",
58-
"tldts": "~5.7.82"
55+
"cbor-x": "~1.4.0",
56+
"jose": "~4.9.1",
57+
"pkijs": "~3.0.7",
58+
"tldts": "~5.7.90"
5959
},
6060
"eslintConfig": {
6161
"root": true,

rollup.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ const tests = [
2828
"test/parseClientData.test.js",
2929
"test/parseExpectations.test.js",
3030
"test/parseNoneAttestationData.test.js",
31+
"test/parseNoneAttestationDataExtensions.test.js",
3132
"test/parsePackedAttestationData.test.js",
3233
"test/parsePackedSelfAttestationData.test.js",
3334
"test/parseTpmAttestationData.test.js",
3435
"test/parseU2fAttestationData.test.js",
36+
"test/parseJustExtensions.test.js",
3537
"test/parseAppleAttestationData.test.js",
3638
"test/response.test.js",
3739
"test/toolbox.test.js",

test/helpers/fido2-helpers.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,20 @@ const challengeResponseAttestationNoneMsgB64Url = {
186186
},
187187
};
188188

189+
const challengeResponseNoneAttestationDataExtensionsMsgB64Url = {
190+
response: {
191+
attestationObject:
192+
"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVkBNkmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjwQAAAAAAAAAAAAAAAAAAAAAAAAAAAKIACKLdXqwahqjNbtNs1piUlonluvxOsF9Feeh9k7qXay5zdrm239cW4WQUD_l5ptTzRLU9bSbghnv0FLaRA7tly7La9_QRKDXwZMsbWajlhKQh2ovYnjh6C37qtyPs151ITDFr-67FRgG0c2dJCoOa2hQB8z0tJYuXrkGMpVk0ZSn1qjfeYxJ1V9BDRsfN7r0lVC8sF_w5OJlSomw64qampRylAQIDJiABIVgguxHN3W6ehp0VWXKaMNie1J82MVJCFZYScau74o17cx8iWCDb1jkTLi7lYZZbgwUwpqAk8QmIiPMTVQUVkhGEyGrKw7kAAWtjcmVkUHJvdGVjdAE",
193+
},
194+
};
195+
196+
const challengeResponseJustExtensionsMsgB64Url = {
197+
response: {
198+
attestationObject:
199+
"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVkANUmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjgQAAAAC5AAFrY3JlZFByb3RlY3QB",
200+
},
201+
};
202+
189203
const getOptionsRequest = {
190204
username: "bubba",
191205
displayName: "Bubba Smith",
@@ -484,6 +498,26 @@ const makeCredentialAttestationSafetyNetResponse = {
484498
},
485499
};
486500

501+
const makeNoneAttestationDataExtensionsResponse = {
502+
response: {
503+
attestationObject: base64.toArrayBuffer(
504+
challengeResponseNoneAttestationDataExtensionsMsgB64Url.response
505+
.attestationObject,
506+
true,
507+
),
508+
},
509+
};
510+
511+
const makeJustExtensionsResponse = {
512+
response: {
513+
attestationObject: base64.toArrayBuffer(
514+
challengeResponseJustExtensionsMsgB64Url.response
515+
.attestationObject,
516+
true,
517+
),
518+
},
519+
};
520+
487521
const assertionResponse = {
488522
id: assertionResponseMsgB64Url.id,
489523
rawId: base64.toArrayBuffer(assertionResponseMsgB64Url.rawId, true),
@@ -549,6 +583,8 @@ const lib = {
549583
makeCredentialAttestationPackedResponseWindowsHello,
550584
makeCredentialAttestationTpmResponse,
551585
makeCredentialAttestationSafetyNetResponse,
586+
makeNoneAttestationDataExtensionsResponse,
587+
makeJustExtensionsResponse,
552588
assertionResponse,
553589
assertionResponseWindowsHello,
554590
assnPublicKey,

0 commit comments

Comments
 (0)