nid-webauthn-emulator 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +10 -1
- package/README.md +10 -1
- package/dist/spec/authenticator/authenticator-emulator.spec.js +213 -65
- package/dist/spec/authenticator/ctap-model.spec.js +58 -53
- package/dist/spec/integration/integration.spec.js +7 -4
- package/dist/spec/integration/passkeys-api-client.d.ts +0 -1
- package/dist/spec/integration/webauthn-io.d.ts +0 -1
- package/dist/spec/libs/browser-injection.spec.js +55 -7
- package/dist/spec/libs/cbor.spec.js +57 -35
- package/dist/spec/libs/cose-key.spec.js +46 -40
- package/dist/spec/repository/credential-repository.spec.js +13 -12
- package/dist/spec/test-utils/unit-test.spec.js +28 -27
- package/dist/spec/webauthn/cose-key.spec.js +18 -17
- package/dist/spec/webauthn/webauthn-emulator.spec.js +376 -137
- package/dist/spec/webauthn/webauthn-model-json.spec.js +87 -42
- package/dist/spec/webauthn/webauthn-model.spec.js +59 -17
- package/dist/spec/webauthn/webauthn-test-server.d.ts +0 -1
- package/dist/spec/webauthn/webauthn-test-server.js +2 -2
- package/dist/src/authenticator/authenticator-emulator.d.ts +3 -0
- package/dist/src/authenticator/authenticator-emulator.js +71 -8
- package/dist/src/libs/cbor.d.ts +4 -0
- package/dist/src/libs/cbor.js +9 -0
- package/dist/src/libs/encode-utils.d.ts +2 -1
- package/dist/src/libs/encode-utils.js +1 -0
- package/dist/src/repository/credentials-file-repository.js +2 -1
- package/dist/src/repository/credentials-repository.d.ts +0 -1
- package/dist/src/test-utils/unit-test.d.ts +7 -5
- package/dist/src/webauthn/cose-key.d.ts +1 -0
- package/dist/src/webauthn/cose-key.js +6 -16
- package/dist/src/webauthn/webauthn-emulator.d.ts +4 -4
- package/dist/src/webauthn/webauthn-emulator.js +100 -3
- package/dist/src/webauthn/webauthn-model-json.d.ts +7 -94
- package/dist/src/webauthn/webauthn-model-json.js +90 -9
- package/dist/src/webauthn/webauthn-model.d.ts +2 -0
- package/dist/src/webauthn/webauthn-model.js +28 -7
- package/package.json +7 -10
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
4
8
|
const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
5
|
-
(0,
|
|
6
|
-
(0,
|
|
9
|
+
(0, node_test_1.describe)("CTAP Model Test", () => {
|
|
10
|
+
(0, node_test_1.test)("MakeCredentialRequest CTAP Object pack and unpack", async () => {
|
|
7
11
|
const testRequest = {
|
|
8
12
|
clientDataHash: new Uint8Array([99, 104, 97, 108, 108, 101, 110, 103, 101]),
|
|
9
13
|
rp: { name: "webauthn.io", id: "webauthn.io" },
|
|
@@ -18,10 +22,10 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
18
22
|
const packed = (0, ctap_model_1.packMakeCredentialRequest)(testRequest);
|
|
19
23
|
const unpacked = (0, ctap_model_1.unpackRequest)(packed);
|
|
20
24
|
const rePacked = (0, ctap_model_1.packMakeCredentialRequest)(unpacked.request);
|
|
21
|
-
(
|
|
22
|
-
|
|
25
|
+
strict_1.default.deepEqual(packed, rePacked);
|
|
26
|
+
strict_1.default.deepEqual(unpacked.request, testRequest);
|
|
23
27
|
});
|
|
24
|
-
(0,
|
|
28
|
+
(0, node_test_1.test)("GetAssertionRequest CTAP Object pack and unpack", async () => {
|
|
25
29
|
const testRequest = {
|
|
26
30
|
rpId: "webauthn.io",
|
|
27
31
|
clientDataHash: new Uint8Array([99, 104, 97, 108, 108, 101, 110, 103, 101]),
|
|
@@ -34,10 +38,10 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
34
38
|
const packed = (0, ctap_model_1.packGetAssertionRequest)(testRequest);
|
|
35
39
|
const unpacked = (0, ctap_model_1.unpackRequest)(packed);
|
|
36
40
|
const rePacked = (0, ctap_model_1.packGetAssertionRequest)(unpacked.request);
|
|
37
|
-
(
|
|
38
|
-
|
|
41
|
+
strict_1.default.deepEqual(packed, rePacked);
|
|
42
|
+
strict_1.default.deepEqual(unpacked.request, testRequest);
|
|
39
43
|
});
|
|
40
|
-
(0,
|
|
44
|
+
(0, node_test_1.test)("MakeCredentialResponse CTAP Object pack and unpack", async () => {
|
|
41
45
|
const testResponse = {
|
|
42
46
|
fmt: "none",
|
|
43
47
|
attStmt: {},
|
|
@@ -46,11 +50,12 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
46
50
|
const packed = (0, ctap_model_1.packMakeCredentialResponse)(testResponse);
|
|
47
51
|
const unpacked = (0, ctap_model_1.unpackMakeCredentialResponse)(packed);
|
|
48
52
|
const rePacked = (0, ctap_model_1.packMakeCredentialResponse)(unpacked);
|
|
49
|
-
(
|
|
50
|
-
(
|
|
53
|
+
strict_1.default.deepEqual(packed, rePacked);
|
|
54
|
+
strict_1.default.deepEqual(unpacked, testResponse);
|
|
51
55
|
});
|
|
52
|
-
(0,
|
|
56
|
+
(0, node_test_1.test)("GetAssertionResponse CTAP Object pack and unpack", async () => {
|
|
53
57
|
const testResponse = {
|
|
58
|
+
credential: undefined,
|
|
54
59
|
authData: new Uint8Array([97, 117, 116, 104, 68, 97, 116, 97]),
|
|
55
60
|
signature: new Uint8Array([115, 105, 103]),
|
|
56
61
|
user: { id: new Uint8Array([116, 101, 115, 116, 45, 117, 115, 101, 114]), name: "user", displayName: "user" },
|
|
@@ -59,10 +64,10 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
59
64
|
const packed = (0, ctap_model_1.packGetAssertionResponse)(testResponse);
|
|
60
65
|
const unpacked = (0, ctap_model_1.unpackGetAssertionResponse)(packed);
|
|
61
66
|
const rePacked = (0, ctap_model_1.packGetAssertionResponse)(unpacked);
|
|
62
|
-
(
|
|
63
|
-
(
|
|
67
|
+
strict_1.default.deepEqual(packed, rePacked);
|
|
68
|
+
strict_1.default.deepEqual(unpacked, testResponse);
|
|
64
69
|
});
|
|
65
|
-
(0,
|
|
70
|
+
(0, node_test_1.test)("GetInfoResponse CTAP Object pack and unpack", async () => {
|
|
66
71
|
const testResponse = {
|
|
67
72
|
versions: ["U2F_V2"],
|
|
68
73
|
extensions: ["credProtect"],
|
|
@@ -74,48 +79,48 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
74
79
|
const packed = (0, ctap_model_1.packGetInfoResponse)(testResponse);
|
|
75
80
|
const unpacked = (0, ctap_model_1.unpackGetInfoResponse)(packed);
|
|
76
81
|
const rePacked = (0, ctap_model_1.packGetInfoResponse)(unpacked);
|
|
77
|
-
(
|
|
78
|
-
(
|
|
82
|
+
strict_1.default.deepEqual(packed, rePacked);
|
|
83
|
+
strict_1.default.deepEqual(unpacked, testResponse);
|
|
79
84
|
});
|
|
80
|
-
(0,
|
|
85
|
+
(0, node_test_1.test)("Illegal data parse test _ failed to unpack", async () => {
|
|
81
86
|
const response = {};
|
|
82
87
|
const expected = "CTAP error: CTAP2_ERR_INVALID_CBOR (18)";
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackMakeCredentialResponse)(response), { message: expected });
|
|
89
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackGetAssertionResponse)(response), { message: expected });
|
|
90
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackGetInfoResponse)(response), { message: expected });
|
|
86
91
|
});
|
|
87
|
-
(0,
|
|
92
|
+
(0, node_test_1.test)("Illegal cbor data parse test _ failed to unpack", async () => {
|
|
88
93
|
const request = {
|
|
89
94
|
command: ctap_model_1.CTAP_COMMAND.authenticatorMakeCredential,
|
|
90
95
|
data: new Uint8Array([1, 2, 3, 4, 5]),
|
|
91
96
|
};
|
|
92
|
-
|
|
97
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackRequest)(request), { message: "CTAP error: CTAP2_ERR_INVALID_CBOR (18)" });
|
|
93
98
|
});
|
|
94
|
-
(0,
|
|
99
|
+
(0, node_test_1.test)("unpackRequest default branch returns undefined request for unhandled command", () => {
|
|
95
100
|
const req = { command: ctap_model_1.CTAP_COMMAND.authenticatorClientPIN }; // not explicitly handled in switch
|
|
96
101
|
const unpacked = (0, ctap_model_1.unpackRequest)(req);
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
strict_1.default.equal(unpacked.command, ctap_model_1.CTAP_COMMAND.authenticatorClientPIN);
|
|
103
|
+
strict_1.default.equal(unpacked.request, undefined);
|
|
99
104
|
});
|
|
100
|
-
(0,
|
|
105
|
+
(0, node_test_1.test)("credential management request with undefined optional params round-trips", () => {
|
|
101
106
|
const packed = (0, ctap_model_1.packCredentialManagementRequest)({
|
|
102
107
|
subCommand: ctap_model_1.CREDENTIAL_MANAGEMENT_SUBCOMMAND.enumerateCredentialsBegin,
|
|
103
108
|
// subCommandParams and pinUvAuth* omitted intentionally
|
|
104
109
|
});
|
|
105
110
|
const unpacked = (0, ctap_model_1.unpackCredentialManagementRequest)(packed);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
strict_1.default.equal(unpacked.subCommand, ctap_model_1.CREDENTIAL_MANAGEMENT_SUBCOMMAND.enumerateCredentialsBegin);
|
|
112
|
+
strict_1.default.equal(unpacked.subCommandParams, undefined);
|
|
113
|
+
strict_1.default.equal(unpacked.pinUvAuthParam, undefined);
|
|
114
|
+
strict_1.default.equal(unpacked.pinUvAuthProtocol, undefined);
|
|
110
115
|
});
|
|
111
|
-
(0,
|
|
116
|
+
(0, node_test_1.test)("credential management response optional binary fields handled when absent/present", () => {
|
|
112
117
|
// Absent case
|
|
113
118
|
const packedAbsent = (0, ctap_model_1.packCredentialManagementResponse)({});
|
|
114
119
|
const absent = (0, ctap_model_1.unpackCredentialManagementResponse)(packedAbsent);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
strict_1.default.equal(absent.credentialID, undefined);
|
|
121
|
+
strict_1.default.equal(absent.publicKey, undefined);
|
|
122
|
+
strict_1.default.equal(absent.rpIDHash, undefined);
|
|
123
|
+
strict_1.default.equal(absent.largeBlobKey, undefined);
|
|
119
124
|
// Present case
|
|
120
125
|
const bin = new Uint8Array([1, 2, 3]);
|
|
121
126
|
const packedPresent = (0, ctap_model_1.packCredentialManagementResponse)({
|
|
@@ -125,32 +130,32 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
125
130
|
largeBlobKey: bin,
|
|
126
131
|
});
|
|
127
132
|
const present = (0, ctap_model_1.unpackCredentialManagementResponse)(packedPresent);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
strict_1.default.notEqual(present.credentialID, undefined);
|
|
134
|
+
strict_1.default.notEqual(present.publicKey, undefined);
|
|
135
|
+
strict_1.default.notEqual(present.rpIDHash, undefined);
|
|
136
|
+
strict_1.default.notEqual(present.largeBlobKey, undefined);
|
|
132
137
|
if (present.credentialID && present.publicKey && present.rpIDHash && present.largeBlobKey) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
strict_1.default.deepEqual(Array.from(present.credentialID), [1, 2, 3]);
|
|
139
|
+
strict_1.default.deepEqual(Array.from(present.publicKey), [1, 2, 3]);
|
|
140
|
+
strict_1.default.deepEqual(Array.from(present.rpIDHash), [1, 2, 3]);
|
|
141
|
+
strict_1.default.deepEqual(Array.from(present.largeBlobKey), [1, 2, 3]);
|
|
137
142
|
}
|
|
138
143
|
});
|
|
139
|
-
(0,
|
|
144
|
+
(0, node_test_1.test)("unpackCredentialManagementRequest throws on invalid CBOR", () => {
|
|
140
145
|
const bad = {
|
|
141
146
|
command: ctap_model_1.CTAP_COMMAND.authenticatorCredentialManagement,
|
|
142
147
|
data: new Uint8Array([1, 2, 3]),
|
|
143
148
|
};
|
|
144
|
-
|
|
149
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackCredentialManagementRequest)(bad), /CTAP2_ERR_INVALID_CBOR/);
|
|
145
150
|
});
|
|
146
|
-
(0,
|
|
151
|
+
(0, node_test_1.test)("unpackCredentialManagementResponse throws on invalid CBOR", () => {
|
|
147
152
|
const bad = {
|
|
148
153
|
status: ctap_model_1.CTAP_STATUS_CODE.CTAP2_OK,
|
|
149
154
|
data: new Uint8Array([1, 2, 3]),
|
|
150
155
|
};
|
|
151
|
-
|
|
156
|
+
strict_1.default.throws(() => (0, ctap_model_1.unpackCredentialManagementResponse)(bad), /CTAP2_ERR_INVALID_CBOR/);
|
|
152
157
|
});
|
|
153
|
-
(0,
|
|
158
|
+
(0, node_test_1.test)("unpackRequest CM includes pinUvAuthParam when present", () => {
|
|
154
159
|
const pin = new Uint8Array([7, 7]);
|
|
155
160
|
const packed = (0, ctap_model_1.packCredentialManagementRequest)({
|
|
156
161
|
subCommand: ctap_model_1.CREDENTIAL_MANAGEMENT_SUBCOMMAND.enumerateCredentialsBegin,
|
|
@@ -160,12 +165,12 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
160
165
|
const unpacked = (0, ctap_model_1.unpackRequest)(packed);
|
|
161
166
|
const req = unpacked.request;
|
|
162
167
|
const isMgmt = (x) => typeof x === "object" && x !== null && "subCommand" in x;
|
|
163
|
-
|
|
168
|
+
strict_1.default.equal(isMgmt(req), true);
|
|
164
169
|
if (isMgmt(req)) {
|
|
165
|
-
|
|
170
|
+
strict_1.default.deepEqual(Array.from(req.pinUvAuthParam ?? new Uint8Array()), [7, 7]);
|
|
166
171
|
}
|
|
167
172
|
});
|
|
168
|
-
(0,
|
|
173
|
+
(0, node_test_1.test)("unpackCredentialManagementRequest includes pinUvAuthParam when present", () => {
|
|
169
174
|
const pin = new Uint8Array([9, 9]);
|
|
170
175
|
const packed = (0, ctap_model_1.packCredentialManagementRequest)({
|
|
171
176
|
subCommand: ctap_model_1.CREDENTIAL_MANAGEMENT_SUBCOMMAND.enumerateCredentialsBegin,
|
|
@@ -173,6 +178,6 @@ const ctap_model_1 = require("../../src/authenticator/ctap-model");
|
|
|
173
178
|
pinUvAuthParam: pin,
|
|
174
179
|
});
|
|
175
180
|
const req = (0, ctap_model_1.unpackCredentialManagementRequest)(packed);
|
|
176
|
-
|
|
181
|
+
strict_1.default.deepEqual(Array.from(req.pinUvAuthParam ?? new Uint8Array()), [9, 9]);
|
|
177
182
|
});
|
|
178
183
|
});
|
|
@@ -3,11 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const node_test_1 = require("node:test");
|
|
7
7
|
const index_1 = __importDefault(require("../../src/index"));
|
|
8
8
|
const webauthn_io_1 = require("./webauthn-io");
|
|
9
|
-
(0,
|
|
10
|
-
|
|
9
|
+
(0, node_test_1.describe)("Passkeys Integration Test by webauthn.io", () => {
|
|
10
|
+
(0, node_test_1.test)("Registration Ceremony and Authentication Ceremony", {
|
|
11
|
+
timeout: 60_000,
|
|
12
|
+
skip: process.env.RUN_WEBAUTHN_IO !== "1",
|
|
13
|
+
}, async () => {
|
|
11
14
|
const origin = "https://webauthn.io";
|
|
12
15
|
const emulator = new index_1.default();
|
|
13
16
|
const webauthnIO = await webauthn_io_1.WebAuthnIO.create();
|
|
@@ -28,5 +31,5 @@ const webauthn_io_1 = require("./webauthn-io");
|
|
|
28
31
|
console.log("Authentication credential", requestCredential);
|
|
29
32
|
await webauthnIO.getAuthenticationVerification(requestCredential);
|
|
30
33
|
console.log("Authentication verification completed");
|
|
31
|
-
}
|
|
34
|
+
});
|
|
32
35
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from "../../src/webauthn/webauthn-model-json";
|
|
2
1
|
import type { PasskeysApiClient, PasskeysUser } from "./passkeys-api-client";
|
|
3
2
|
/**
|
|
4
3
|
* Passkeys Api Client implementation for webauthn.io
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
8
|
+
const authenticator_emulator_1 = require("../../src/authenticator/authenticator-emulator");
|
|
9
|
+
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
4
10
|
const browser_injection_1 = require("../../src/test-utils/browser-injection");
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
const webauthn_emulator_1 = require("../../src/webauthn/webauthn-emulator");
|
|
12
|
+
(0, node_test_1.describe)("Browser Injection Test", () => {
|
|
13
|
+
(0, node_test_1.test)("Browser Injection Test", async () => {
|
|
7
14
|
const window = { navigator: { credentials: { create: undefined, get: undefined } } };
|
|
8
15
|
const PublicKeyCredential = {
|
|
9
16
|
isConditionalMediationAvailable: async () => false,
|
|
@@ -11,9 +18,50 @@ const browser_injection_1 = require("../../src/test-utils/browser-injection");
|
|
|
11
18
|
};
|
|
12
19
|
// biome-ignore lint/security/noGlobalEval: This is a test code.
|
|
13
20
|
eval(browser_injection_1.HookWebAuthnApis);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
strict_1.default.ok(await PublicKeyCredential.isConditionalMediationAvailable());
|
|
22
|
+
strict_1.default.notEqual(window.navigator.credentials.create, undefined);
|
|
23
|
+
strict_1.default.notEqual(window.navigator.credentials.get, undefined);
|
|
24
|
+
strict_1.default.notEqual(PublicKeyCredential.signalUnknownCredential, undefined);
|
|
25
|
+
});
|
|
26
|
+
// The hooked create/get run in-page, so every helper they reach must be exported.
|
|
27
|
+
(0, node_test_1.test)("hooked create and get round-trip prf through the emulator", async () => {
|
|
28
|
+
const origin = "https://test-rp.org";
|
|
29
|
+
const emulator = new webauthn_emulator_1.WebAuthnEmulator(new authenticator_emulator_1.AuthenticatorEmulator({ hmacSecret: "hmac-secret-mc" }));
|
|
30
|
+
const window = {
|
|
31
|
+
navigator: { credentials: { create: undefined, get: undefined } },
|
|
32
|
+
webAuthnEmulatorCreate: async (optionsJSON) => emulator.createJSON(origin, optionsJSON),
|
|
33
|
+
webAuthnEmulatorGet: async (optionsJSON) => emulator.getJSON(origin, optionsJSON),
|
|
34
|
+
};
|
|
35
|
+
const PublicKeyCredential = { isConditionalMediationAvailable: async () => false };
|
|
36
|
+
// biome-ignore lint/security/noGlobalEval: This is a test code.
|
|
37
|
+
eval(browser_injection_1.HookWebAuthnApis);
|
|
38
|
+
strict_1.default.ok(await PublicKeyCredential.isConditionalMediationAvailable());
|
|
39
|
+
const first = encode_utils_1.default.strToUint8Array("prf-input-first");
|
|
40
|
+
const create = window.navigator.credentials.create;
|
|
41
|
+
strict_1.default.ok(create);
|
|
42
|
+
const created = await create({
|
|
43
|
+
publicKey: {
|
|
44
|
+
rp: { id: "test-rp.org", name: "rp" },
|
|
45
|
+
user: { id: encode_utils_1.default.strToUint8Array("user"), name: "user", displayName: "user" },
|
|
46
|
+
challenge: encode_utils_1.default.strToUint8Array("challenge"),
|
|
47
|
+
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
|
|
48
|
+
extensions: { prf: { eval: { first } } },
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const createdPrf = created.getClientExtensionResults().prf?.results?.first;
|
|
52
|
+
strict_1.default.ok(createdPrf instanceof ArrayBuffer);
|
|
53
|
+
strict_1.default.equal(createdPrf.byteLength, 32);
|
|
54
|
+
const get = window.navigator.credentials.get;
|
|
55
|
+
strict_1.default.ok(get);
|
|
56
|
+
const asserted = await get({
|
|
57
|
+
publicKey: {
|
|
58
|
+
rpId: "test-rp.org",
|
|
59
|
+
challenge: encode_utils_1.default.strToUint8Array("challenge-2"),
|
|
60
|
+
extensions: { prf: { eval: { first } } },
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const assertedPrf = asserted.getClientExtensionResults().prf?.results?.first;
|
|
64
|
+
strict_1.default.ok(assertedPrf instanceof ArrayBuffer);
|
|
65
|
+
strict_1.default.deepEqual(new Uint8Array(assertedPrf), new Uint8Array(createdPrf));
|
|
18
66
|
});
|
|
19
67
|
});
|
|
@@ -3,21 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
7
8
|
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
8
|
-
(0,
|
|
9
|
-
(0,
|
|
9
|
+
(0, node_test_1.describe)("CBOR encode/decode edge cases", () => {
|
|
10
|
+
(0, node_test_1.test)("round-trips primitive values and large integer encodings", () => {
|
|
10
11
|
const encoded = encode_utils_1.default.encodeCbor({
|
|
11
12
|
1: "one",
|
|
12
13
|
nil: null,
|
|
13
14
|
values: [false, true, undefined, -2, 24, 0x100, 0x1_0000, 0x1_0000_0000],
|
|
14
15
|
});
|
|
15
16
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
strict_1.default.equal(decoded[1], "one");
|
|
18
|
+
strict_1.default.equal(decoded.nil, null);
|
|
19
|
+
strict_1.default.deepEqual(decoded.values, [false, true, undefined, -2, 24, 0x100, 0x1_0000, 0x1_0000_0000]);
|
|
19
20
|
});
|
|
20
|
-
(0,
|
|
21
|
+
(0, node_test_1.test)("handles ArrayBuffer values within objects and arrays", () => {
|
|
21
22
|
const ab = new Uint8Array([1, 2, 3]).buffer; // ArrayBuffer branch
|
|
22
23
|
const ua = new Uint8Array([4, 5]);
|
|
23
24
|
const obj = {
|
|
@@ -28,49 +29,70 @@ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
|
28
29
|
const encoded = encode_utils_1.default.encodeCbor(obj);
|
|
29
30
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
30
31
|
// ArrayBuffer is decoded back to Uint8Array
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
strict_1.default.ok(decoded.v1 instanceof Uint8Array);
|
|
33
|
+
strict_1.default.deepEqual(Array.from(decoded.v1), [1, 2, 3]);
|
|
33
34
|
// Existing Uint8Array round-trips
|
|
34
|
-
|
|
35
|
+
strict_1.default.deepEqual(Array.from(decoded.v2), [4, 5]);
|
|
35
36
|
// Mixed array elements preserved as Uint8Array
|
|
36
|
-
|
|
37
|
+
strict_1.default.equal(Array.isArray(decoded.arr), true);
|
|
37
38
|
const [a0, a1] = decoded.arr;
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
strict_1.default.deepEqual(Array.from(a0), [1, 2, 3]);
|
|
40
|
+
strict_1.default.deepEqual(Array.from(a1), [4, 5]);
|
|
40
41
|
});
|
|
41
|
-
(0,
|
|
42
|
+
(0, node_test_1.test)("handles Buffer values without conversion in encoder and to Uint8Array on decode", () => {
|
|
42
43
|
const buf = Buffer.from([9, 8]);
|
|
43
44
|
const obj = { b: buf };
|
|
44
45
|
const encoded = encode_utils_1.default.encodeCbor(obj);
|
|
45
46
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
strict_1.default.ok(decoded.b instanceof Uint8Array);
|
|
48
|
+
strict_1.default.deepEqual(Array.from(decoded.b), [9, 8]);
|
|
48
49
|
});
|
|
49
|
-
(0,
|
|
50
|
+
(0, node_test_1.test)("keeps non-exact integer object keys as text string map keys", () => {
|
|
50
51
|
const decoded = encode_utils_1.default.decodeCbor(encode_utils_1.default.encodeCbor({
|
|
51
52
|
"1": false,
|
|
52
53
|
"01": true,
|
|
53
54
|
"1abc": true,
|
|
54
55
|
}));
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
strict_1.default.equal(decoded[1], false);
|
|
57
|
+
strict_1.default.equal(decoded["01"], true);
|
|
58
|
+
strict_1.default.equal(decoded["1abc"], true);
|
|
58
59
|
});
|
|
59
|
-
(0,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
(0, node_test_1.test)("decodes explicit integer widths", () => {
|
|
61
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0x1a, 0x00, 0x01, 0x00, 0x00])), 0x1_0000);
|
|
62
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])), 0x1_0000_0000);
|
|
63
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0xf6])), null);
|
|
63
64
|
});
|
|
64
|
-
(0,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
(0, node_test_1.test)("decodeCborWithRemainder returns the first value and trailing bytes", () => {
|
|
66
|
+
const first = encode_utils_1.default.encodeCbor({ 1: "a", [-2]: new Uint8Array([9, 9]) });
|
|
67
|
+
const second = encode_utils_1.default.encodeCbor({ 2: "b" });
|
|
68
|
+
const combined = new Uint8Array([...first, ...second]);
|
|
69
|
+
const { value, remainder } = encode_utils_1.default.decodeCborWithRemainder(combined);
|
|
70
|
+
strict_1.default.equal(value[1], "a");
|
|
71
|
+
strict_1.default.deepEqual(value[-2], new Uint8Array([9, 9]));
|
|
72
|
+
strict_1.default.deepEqual(remainder, second);
|
|
73
|
+
});
|
|
74
|
+
(0, node_test_1.test)("rejects unsupported values and malformed input", () => {
|
|
75
|
+
strict_1.default.throws(() => encode_utils_1.default.encodeCbor(Symbol("unsupported")), {
|
|
76
|
+
message: "Unsupported CBOR value type: symbol",
|
|
77
|
+
});
|
|
78
|
+
strict_1.default.throws(() => encode_utils_1.default.encodeCbor({ value: Number.NaN }), { message: "CBOR only supports safe integers" });
|
|
79
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array()), { message: "Insufficient data" });
|
|
80
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x42, 0x01])), { message: "Insufficient data" });
|
|
81
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x5f])), {
|
|
82
|
+
message: "Unsupported indefinite-length CBOR data",
|
|
83
|
+
});
|
|
84
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x01, 0x02])), {
|
|
85
|
+
message: "Unexpected trailing CBOR data",
|
|
86
|
+
});
|
|
87
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xc1, 0x01])), {
|
|
88
|
+
message: "Unsupported CBOR tagged value",
|
|
89
|
+
});
|
|
90
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xf8])), {
|
|
91
|
+
message: "Unsupported CBOR simple value: 24",
|
|
92
|
+
});
|
|
93
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xa1, 0x41, 0x01, 0x01])), {
|
|
94
|
+
message: "Unsupported CBOR map key type",
|
|
95
|
+
});
|
|
96
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x1b, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), { message: "CBOR integer exceeds JavaScript safe integer range" });
|
|
75
97
|
});
|
|
76
98
|
});
|
|
@@ -1,47 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
4
8
|
const authenticator_emulator_1 = require("../../src/authenticator/authenticator-emulator");
|
|
5
9
|
const cose_key_1 = require("../../src/webauthn/cose-key");
|
|
6
10
|
const webauthn_model_1 = require("../../src/webauthn/webauthn-model");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
for (const alg of [-7, -8, -257]) {
|
|
12
|
+
(0, node_test_1.describe)(`CoseKey Test: ${alg}`, () => {
|
|
13
|
+
const getKey = () => {
|
|
14
|
+
const authenticator = new authenticator_emulator_1.AuthenticatorEmulator();
|
|
15
|
+
const rpId = new webauthn_model_1.RpId("example.com");
|
|
16
|
+
const userHandle = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
|
|
17
|
+
const credential = authenticator.authenticatorMakeCredential({
|
|
18
|
+
rp: { id: rpId.value, name: "example" },
|
|
19
|
+
user: { id: userHandle, name: "example", displayName: "example" },
|
|
20
|
+
pubKeyCredParams: [{ alg, type: "public-key" }],
|
|
21
|
+
clientDataHash: new Uint8Array(32),
|
|
22
|
+
});
|
|
23
|
+
return (0, webauthn_model_1.unpackAuthenticatorData)(credential.authData).attestedCredentialData?.credentialPublicKey;
|
|
24
|
+
};
|
|
25
|
+
(0, node_test_1.test)("Der format _ serialize and deserialize test", async () => {
|
|
26
|
+
const testCoseKey = getKey();
|
|
27
|
+
const serialized = testCoseKey.toDer();
|
|
28
|
+
const deserialized = cose_key_1.CoseKey.fromDer(testCoseKey.toDer());
|
|
29
|
+
const reSerialized = deserialized.toDer();
|
|
30
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
31
|
+
strict_1.default.deepEqual(testCoseKey, deserialized);
|
|
32
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
33
|
+
});
|
|
34
|
+
(0, node_test_1.test)("Jwk format _ serialize and deserialize test", async () => {
|
|
35
|
+
const testCoseKey = getKey();
|
|
36
|
+
const serialized = testCoseKey.toJwk();
|
|
37
|
+
const deserialized = cose_key_1.CoseKey.fromJwk(testCoseKey.toJwk()).toJwk();
|
|
38
|
+
const reSerialized = cose_key_1.CoseKey.fromJwk(deserialized).toJwk();
|
|
39
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
40
|
+
strict_1.default.deepEqual(serialized, deserialized);
|
|
41
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
42
|
+
});
|
|
43
|
+
(0, node_test_1.test)("KeyObject format _ serialize and deserialize test", async () => {
|
|
44
|
+
const testCoseKey = getKey();
|
|
45
|
+
const serialized = testCoseKey.toKeyObject();
|
|
46
|
+
const deserialized = cose_key_1.CoseKey.fromKeyObject(testCoseKey.toKeyObject()).toKeyObject();
|
|
47
|
+
const reSerialized = cose_key_1.CoseKey.fromKeyObject(deserialized).toKeyObject();
|
|
48
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
49
|
+
strict_1.default.deepEqual(serialized, deserialized);
|
|
50
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
17
51
|
});
|
|
18
|
-
return (0, webauthn_model_1.unpackAuthenticatorData)(credential.authData).attestedCredentialData?.credentialPublicKey;
|
|
19
|
-
};
|
|
20
|
-
(0, globals_1.test)("Der format _ serialize and deserialize test", async () => {
|
|
21
|
-
const testCoseKey = getKey();
|
|
22
|
-
const serialized = testCoseKey.toDer();
|
|
23
|
-
const deserialized = cose_key_1.CoseKey.fromDer(testCoseKey.toDer());
|
|
24
|
-
const reSerialized = deserialized.toDer();
|
|
25
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
26
|
-
(0, globals_1.expect)(testCoseKey).toEqual(deserialized);
|
|
27
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
28
|
-
});
|
|
29
|
-
(0, globals_1.test)("Jwk format _ serialize and deserialize test", async () => {
|
|
30
|
-
const testCoseKey = getKey();
|
|
31
|
-
const serialized = testCoseKey.toJwk();
|
|
32
|
-
const deserialized = cose_key_1.CoseKey.fromJwk(testCoseKey.toJwk()).toJwk();
|
|
33
|
-
const reSerialized = cose_key_1.CoseKey.fromJwk(deserialized).toJwk();
|
|
34
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
35
|
-
(0, globals_1.expect)(serialized).toEqual(deserialized);
|
|
36
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
37
|
-
});
|
|
38
|
-
(0, globals_1.test)("KeyObject format _ serialize and deserialize test", async () => {
|
|
39
|
-
const testCoseKey = getKey();
|
|
40
|
-
const serialized = testCoseKey.toKeyObject();
|
|
41
|
-
const deserialized = cose_key_1.CoseKey.fromKeyObject(testCoseKey.toKeyObject()).toKeyObject();
|
|
42
|
-
const reSerialized = cose_key_1.CoseKey.fromKeyObject(deserialized).toKeyObject();
|
|
43
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
44
|
-
(0, globals_1.expect)(serialized).toEqual(deserialized);
|
|
45
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
46
52
|
});
|
|
47
|
-
}
|
|
53
|
+
}
|