@revibase/ctap2-js 0.1.0 → 0.1.1
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/lib/ctap2/cborUtils.d.ts.map +1 -1
- package/lib/ctap2/cborUtils.js +4 -10
- package/package.json +1 -1
- package/src/ctap2/cborUtils.ts +105 -121
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cborUtils.d.ts","sourceRoot":"","sources":["../../src/ctap2/cborUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cborUtils.d.ts","sourceRoot":"","sources":["../../src/ctap2/cborUtils.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAYnE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAuD7D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAUzD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAS5D;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAClC,GAAG,EAAE,MAAM,GACV,OAAO,CAeT"}
|
package/lib/ctap2/cborUtils.js
CHANGED
|
@@ -9,6 +9,7 @@ const cbor_x_1 = require("cbor-x");
|
|
|
9
9
|
const ctapCborEncoder = new cbor_x_1.Encoder({
|
|
10
10
|
useTag259ForMaps: false,
|
|
11
11
|
useRecords: false,
|
|
12
|
+
variableMapSize: true,
|
|
12
13
|
mapsAsObjects: true,
|
|
13
14
|
structuredClone: false,
|
|
14
15
|
tagUint8Array: false,
|
|
@@ -42,10 +43,7 @@ function canonicalizeCtapValue(value) {
|
|
|
42
43
|
return value;
|
|
43
44
|
}
|
|
44
45
|
const t = typeof value;
|
|
45
|
-
if (t === "boolean" ||
|
|
46
|
-
t === "number" ||
|
|
47
|
-
t === "string" ||
|
|
48
|
-
t === "bigint") {
|
|
46
|
+
if (t === "boolean" || t === "number" || t === "string" || t === "bigint") {
|
|
49
47
|
return value;
|
|
50
48
|
}
|
|
51
49
|
if (value instanceof Uint8Array) {
|
|
@@ -65,9 +63,7 @@ function canonicalizeCtapValue(value) {
|
|
|
65
63
|
}
|
|
66
64
|
const allNum = keys.every((k) => typeof k === "number");
|
|
67
65
|
if (allNum) {
|
|
68
|
-
const sorted = keys
|
|
69
|
-
.slice()
|
|
70
|
-
.sort((a, b) => a - b);
|
|
66
|
+
const sorted = keys.slice().sort((a, b) => a - b);
|
|
71
67
|
const m = new Map();
|
|
72
68
|
for (const k of sorted) {
|
|
73
69
|
m.set(k, canonicalizeCtapValue(value.get(k)));
|
|
@@ -76,9 +72,7 @@ function canonicalizeCtapValue(value) {
|
|
|
76
72
|
}
|
|
77
73
|
const allStr = keys.every((k) => typeof k === "string");
|
|
78
74
|
if (allStr) {
|
|
79
|
-
const sorted = keys
|
|
80
|
-
.slice()
|
|
81
|
-
.sort(compareCtapTextMapKeys);
|
|
75
|
+
const sorted = keys.slice().sort(compareCtapTextMapKeys);
|
|
82
76
|
const o = {};
|
|
83
77
|
for (const k of sorted) {
|
|
84
78
|
o[k] = canonicalizeCtapValue(value.get(k));
|
package/package.json
CHANGED
package/src/ctap2/cborUtils.ts
CHANGED
|
@@ -5,12 +5,13 @@ import { Encoder } from "cbor-x";
|
|
|
5
5
|
* plain CBOR map (no tag), or authenticators return FIDO_ERR_CBOR_UNEXPECTED_TYPE (0x11).
|
|
6
6
|
*/
|
|
7
7
|
const ctapCborEncoder = new Encoder({
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
useTag259ForMaps: false,
|
|
9
|
+
useRecords: false,
|
|
10
|
+
variableMapSize: true,
|
|
11
|
+
mapsAsObjects: true,
|
|
12
|
+
structuredClone: false,
|
|
13
|
+
tagUint8Array: false,
|
|
14
|
+
bundleStrings: false,
|
|
14
15
|
} as ConstructorParameters<typeof Encoder>[0]);
|
|
15
16
|
|
|
16
17
|
const textEncoder = new TextEncoder();
|
|
@@ -20,17 +21,17 @@ const textEncoder = new TextEncoder();
|
|
|
20
21
|
* shorter UTF-8 length sorts first; same length → lower byte-wise lexical order.
|
|
21
22
|
*/
|
|
22
23
|
export function compareCtapTextMapKeys(a: string, b: string): number {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
const ab = textEncoder.encode(a);
|
|
25
|
+
const bb = textEncoder.encode(b);
|
|
26
|
+
if (ab.length !== bb.length) {
|
|
27
|
+
return ab.length - bb.length;
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < ab.length; i++) {
|
|
30
|
+
if (ab[i] !== bb[i]) {
|
|
31
|
+
return ab[i] - bb[i];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -38,118 +39,101 @@ export function compareCtapTextMapKeys(a: string, b: string): number {
|
|
|
38
39
|
* Integer-key maps (command parameters, COSE, etc.) are sorted numerically.
|
|
39
40
|
*/
|
|
40
41
|
export function canonicalizeCtapValue(value: unknown): unknown {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"CTAP CBOR: map keys must be all numbers or all strings for canonical encoding",
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
if (t === "object") {
|
|
99
|
-
const o = value as Record<string, unknown>;
|
|
100
|
-
const keys = Object.keys(o).sort(compareCtapTextMapKeys);
|
|
101
|
-
const out: Record<string, unknown> = {};
|
|
102
|
-
for (const k of keys) {
|
|
103
|
-
out[k] = canonicalizeCtapValue(o[k]);
|
|
104
|
-
}
|
|
105
|
-
return out;
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
42
|
+
if (value === null || value === undefined) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
const t = typeof value;
|
|
46
|
+
if (t === "boolean" || t === "number" || t === "string" || t === "bigint") {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
if (value instanceof Uint8Array) {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
|
53
|
+
const view = value as ArrayBufferView;
|
|
54
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
return value.map((v) => canonicalizeCtapValue(v));
|
|
58
|
+
}
|
|
59
|
+
if (value instanceof Map) {
|
|
60
|
+
const keys = [...value.keys()];
|
|
61
|
+
if (keys.length === 0) {
|
|
62
|
+
return new Map();
|
|
63
|
+
}
|
|
64
|
+
const allNum = keys.every((k) => typeof k === "number");
|
|
65
|
+
if (allNum) {
|
|
66
|
+
const sorted = (keys as number[]).slice().sort((a, b) => a - b);
|
|
67
|
+
const m = new Map<number, unknown>();
|
|
68
|
+
for (const k of sorted) {
|
|
69
|
+
m.set(k, canonicalizeCtapValue(value.get(k)));
|
|
70
|
+
}
|
|
71
|
+
return m;
|
|
72
|
+
}
|
|
73
|
+
const allStr = keys.every((k) => typeof k === "string");
|
|
74
|
+
if (allStr) {
|
|
75
|
+
const sorted = (keys as string[]).slice().sort(compareCtapTextMapKeys);
|
|
76
|
+
const o: Record<string, unknown> = {};
|
|
77
|
+
for (const k of sorted) {
|
|
78
|
+
o[k] = canonicalizeCtapValue(value.get(k));
|
|
79
|
+
}
|
|
80
|
+
return o;
|
|
81
|
+
}
|
|
82
|
+
throw new Error(
|
|
83
|
+
"CTAP CBOR: map keys must be all numbers or all strings for canonical encoding",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (t === "object") {
|
|
87
|
+
const o = value as Record<string, unknown>;
|
|
88
|
+
const keys = Object.keys(o).sort(compareCtapTextMapKeys);
|
|
89
|
+
const out: Record<string, unknown> = {};
|
|
90
|
+
for (const k of keys) {
|
|
91
|
+
out[k] = canonicalizeCtapValue(o[k]);
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
export function encodeCtapCbor(value: unknown): Uint8Array {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
99
|
+
const encoded = ctapCborEncoder.encode(canonicalizeCtapValue(value));
|
|
100
|
+
if (encoded instanceof Uint8Array) {
|
|
101
|
+
return new Uint8Array(encoded);
|
|
102
|
+
}
|
|
103
|
+
if (ArrayBuffer.isView(encoded)) {
|
|
104
|
+
const v = encoded as ArrayBufferView;
|
|
105
|
+
return new Uint8Array(v.buffer, v.byteOffset, v.byteLength);
|
|
106
|
+
}
|
|
107
|
+
return Uint8Array.from(encoded as ArrayLike<number>);
|
|
120
108
|
}
|
|
121
109
|
|
|
122
110
|
export function bytesView(v: unknown): Uint8Array | undefined {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
view.byteLength,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
return undefined;
|
|
111
|
+
if (v instanceof Uint8Array) {
|
|
112
|
+
return new Uint8Array(v);
|
|
113
|
+
}
|
|
114
|
+
if (ArrayBuffer.isView(v) && !(v instanceof DataView)) {
|
|
115
|
+
const view = v as ArrayBufferView;
|
|
116
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
135
119
|
}
|
|
136
120
|
|
|
137
121
|
export function cborMapGet(
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
map: Map<number, unknown> | object,
|
|
123
|
+
key: number,
|
|
140
124
|
): unknown {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
if (map instanceof Map) {
|
|
126
|
+
return map.get(key);
|
|
127
|
+
}
|
|
128
|
+
if (map !== null && typeof map === "object") {
|
|
129
|
+
const o = map as Record<number | string, unknown>;
|
|
130
|
+
if (key in o) {
|
|
131
|
+
return o[key];
|
|
132
|
+
}
|
|
133
|
+
const s = String(key);
|
|
134
|
+
if (s in o) {
|
|
135
|
+
return o[s];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
155
139
|
}
|