Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 28c034d

Browse filesBrowse files
panvadanielleadams
authored andcommitted
crypto: add CFRG curves to Web Crypto API
PR-URL: #42507 Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 7f8f61a commit 28c034d
Copy full SHA for 28c034d
Expand file treeCollapse file tree

24 files changed

+1898
-1334
lines changed
Open diff view settings
Collapse file

‎doc/api/webcrypto.md‎

Copy file name to clipboardExpand all lines: doc/api/webcrypto.md
+129-212Lines changed: 129 additions & 212 deletions
  • Display the source diff
  • Display the rich diff
Large diffs are not rendered by default.
Collapse file

‎lib/internal/crypto/cfrg.js‎

Copy file name to clipboard
+369Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
'use strict';
2+
3+
const {
4+
Promise,
5+
SafeSet,
6+
} = primordials;
7+
8+
const { Buffer } = require('buffer');
9+
10+
const {
11+
ECKeyExportJob,
12+
KeyObjectHandle,
13+
SignJob,
14+
kCryptoJobAsync,
15+
kKeyTypePrivate,
16+
kKeyTypePublic,
17+
kSignJobModeSign,
18+
kSignJobModeVerify,
19+
} = internalBinding('crypto');
20+
21+
const {
22+
codes: {
23+
ERR_INVALID_ARG_TYPE,
24+
},
25+
} = require('internal/errors');
26+
27+
const {
28+
getArrayBufferOrView,
29+
getUsagesUnion,
30+
hasAnyNotIn,
31+
jobPromise,
32+
validateKeyOps,
33+
kHandle,
34+
kKeyObject,
35+
} = require('internal/crypto/util');
36+
37+
const {
38+
emitExperimentalWarning,
39+
lazyDOMException,
40+
} = require('internal/util');
41+
42+
const {
43+
generateKeyPair,
44+
} = require('internal/crypto/keygen');
45+
46+
const {
47+
InternalCryptoKey,
48+
PrivateKeyObject,
49+
PublicKeyObject,
50+
createPrivateKey,
51+
createPublicKey,
52+
isKeyObject,
53+
} = require('internal/crypto/keys');
54+
55+
function verifyAcceptableCfrgKeyUse(name, type, usages) {
56+
let checkSet;
57+
switch (name) {
58+
case 'X25519':
59+
// Fall through
60+
case 'X448':
61+
checkSet = ['deriveKey', 'deriveBits'];
62+
break;
63+
case 'Ed25519':
64+
// Fall through
65+
case 'Ed448':
66+
switch (type) {
67+
case 'private':
68+
checkSet = ['sign'];
69+
break;
70+
case 'public':
71+
checkSet = ['verify'];
72+
break;
73+
}
74+
}
75+
if (hasAnyNotIn(usages, checkSet)) {
76+
throw lazyDOMException(
77+
`Unsupported key usage for a ${name} key`,
78+
'SyntaxError');
79+
}
80+
}
81+
82+
function createECPublicKeyRaw(name, keyData) {
83+
const handle = new KeyObjectHandle();
84+
keyData = getArrayBufferOrView(keyData, 'keyData');
85+
if (handle.initECRaw(name.toLowerCase(), keyData))
86+
return new PublicKeyObject(handle);
87+
}
88+
89+
function createCFRGRawKey(name, keyData, isPublic) {
90+
const handle = new KeyObjectHandle();
91+
keyData = getArrayBufferOrView(keyData, 'keyData');
92+
93+
switch (name) {
94+
case 'Ed25519':
95+
case 'X25519':
96+
if (keyData.byteLength !== 32) {
97+
throw lazyDOMException(
98+
`${name} raw keys must be exactly 32-bytes`);
99+
}
100+
break;
101+
case 'Ed448':
102+
if (keyData.byteLength !== 57) {
103+
throw lazyDOMException(
104+
`${name} raw keys must be exactly 57-bytes`);
105+
}
106+
break;
107+
case 'X448':
108+
if (keyData.byteLength !== 56) {
109+
throw lazyDOMException(
110+
`${name} raw keys must be exactly 56-bytes`);
111+
}
112+
break;
113+
}
114+
115+
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
116+
if (!handle.initEDRaw(name, keyData, keyType)) {
117+
throw lazyDOMException('Failure to generate key object');
118+
}
119+
120+
return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle);
121+
}
122+
123+
async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
124+
const { name } = algorithm;
125+
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
126+
127+
const usageSet = new SafeSet(keyUsages);
128+
switch (name) {
129+
case 'Ed25519':
130+
// Fall through
131+
case 'Ed448':
132+
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
133+
throw lazyDOMException(
134+
`Unsupported key usage for an ${name} key`,
135+
'SyntaxError');
136+
}
137+
break;
138+
case 'X25519':
139+
// Fall through
140+
case 'X448':
141+
if (hasAnyNotIn(usageSet, ['deriveKey', 'deriveBits'])) {
142+
throw lazyDOMException(
143+
`Unsupported key usage for an ${name} key`,
144+
'SyntaxError');
145+
}
146+
break;
147+
}
148+
return new Promise((resolve, reject) => {
149+
let genKeyType;
150+
switch (name) {
151+
case 'Ed25519':
152+
genKeyType = 'ed25519';
153+
break;
154+
case 'Ed448':
155+
genKeyType = 'ed448';
156+
break;
157+
case 'X25519':
158+
genKeyType = 'x25519';
159+
break;
160+
case 'X448':
161+
genKeyType = 'x448';
162+
break;
163+
}
164+
generateKeyPair(genKeyType, undefined, (err, pubKey, privKey) => {
165+
if (err) {
166+
return reject(lazyDOMException(
167+
'The operation failed for an operation-specific reason',
168+
'OperationError'));
169+
}
170+
171+
const algorithm = { name };
172+
173+
let publicUsages;
174+
let privateUsages;
175+
switch (name) {
176+
case 'Ed25519':
177+
// Fall through
178+
case 'Ed448':
179+
publicUsages = getUsagesUnion(usageSet, 'verify');
180+
privateUsages = getUsagesUnion(usageSet, 'sign');
181+
break;
182+
case 'X25519':
183+
// Fall through
184+
case 'X448':
185+
publicUsages = [];
186+
privateUsages = getUsagesUnion(usageSet, 'deriveKey', 'deriveBits');
187+
break;
188+
}
189+
190+
const publicKey =
191+
new InternalCryptoKey(
192+
pubKey,
193+
algorithm,
194+
publicUsages,
195+
true);
196+
197+
const privateKey =
198+
new InternalCryptoKey(
199+
privKey,
200+
algorithm,
201+
privateUsages,
202+
extractable);
203+
204+
resolve({ publicKey, privateKey });
205+
});
206+
});
207+
}
208+
209+
function cfrgExportKey(key, format) {
210+
emitExperimentalWarning(`The ${key.algorithm.name} Web Crypto API algorithm`);
211+
return jobPromise(new ECKeyExportJob(
212+
kCryptoJobAsync,
213+
format,
214+
key[kKeyObject][kHandle]));
215+
}
216+
217+
async function cfrgImportKey(
218+
format,
219+
keyData,
220+
algorithm,
221+
extractable,
222+
keyUsages) {
223+
224+
const { name } = algorithm;
225+
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
226+
let keyObject;
227+
const usagesSet = new SafeSet(keyUsages);
228+
switch (format) {
229+
case 'node.keyObject': {
230+
if (!isKeyObject(keyData))
231+
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
232+
if (keyData.type === 'secret')
233+
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
234+
verifyAcceptableCfrgKeyUse(name, keyData.type, usagesSet);
235+
keyObject = keyData;
236+
break;
237+
}
238+
case 'spki': {
239+
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
240+
keyObject = createPublicKey({
241+
key: keyData,
242+
format: 'der',
243+
type: 'spki'
244+
});
245+
break;
246+
}
247+
case 'pkcs8': {
248+
verifyAcceptableCfrgKeyUse(name, 'private', usagesSet);
249+
keyObject = createPrivateKey({
250+
key: keyData,
251+
format: 'der',
252+
type: 'pkcs8'
253+
});
254+
break;
255+
}
256+
case 'jwk': {
257+
if (keyData == null || typeof keyData !== 'object')
258+
throw lazyDOMException('Invalid JWK keyData', 'DataError');
259+
if (keyData.kty !== 'OKP')
260+
throw lazyDOMException('Invalid key type', 'DataError');
261+
const isPublic = keyData.d === undefined;
262+
263+
if (usagesSet.size > 0 && keyData.use !== undefined) {
264+
let checkUse;
265+
switch (name) {
266+
case 'Ed25519':
267+
// Fall through
268+
case 'Ed448':
269+
checkUse = 'sig';
270+
break;
271+
case 'X25519':
272+
// Fall through
273+
case 'X448':
274+
checkUse = 'enc';
275+
break;
276+
}
277+
if (keyData.use !== checkUse)
278+
throw lazyDOMException('Invalid use type', 'DataError');
279+
}
280+
281+
validateKeyOps(keyData.key_ops, usagesSet);
282+
283+
if (keyData.ext !== undefined &&
284+
keyData.ext === false &&
285+
extractable === true) {
286+
throw lazyDOMException('JWK is not extractable', 'DataError');
287+
}
288+
289+
if (keyData.alg !== undefined) {
290+
if (typeof keyData.alg !== 'string')
291+
throw lazyDOMException('Invalid alg', 'DataError');
292+
if (
293+
(name === 'Ed25519' || name === 'Ed448') &&
294+
keyData.alg !== 'EdDSA'
295+
) {
296+
throw lazyDOMException('Invalid alg', 'DataError');
297+
}
298+
}
299+
300+
verifyAcceptableCfrgKeyUse(
301+
name,
302+
isPublic ? 'public' : 'private',
303+
usagesSet);
304+
keyObject = createCFRGRawKey(
305+
name,
306+
Buffer.from(
307+
isPublic ? keyData.x : keyData.d,
308+
'base64'),
309+
isPublic);
310+
break;
311+
}
312+
case 'raw': {
313+
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
314+
keyObject = createECPublicKeyRaw(name, keyData);
315+
if (keyObject === undefined)
316+
throw lazyDOMException('Unable to import CFRG key', 'OperationError');
317+
break;
318+
}
319+
}
320+
321+
if (keyObject.asymmetricKeyType !== name.toLowerCase()) {
322+
throw lazyDOMException('Invalid key type', 'DataError');
323+
}
324+
325+
return new InternalCryptoKey(
326+
keyObject,
327+
{ name },
328+
keyUsages,
329+
extractable);
330+
}
331+
332+
function eddsaSignVerify(key, data, { name, context }, signature) {
333+
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
334+
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
335+
const type = mode === kSignJobModeSign ? 'private' : 'public';
336+
337+
if (key.type !== type)
338+
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
339+
340+
if (name === 'Ed448' && context !== undefined) {
341+
context =
342+
getArrayBufferOrView(context, 'algorithm.context');
343+
if (context.byteLength !== 0) {
344+
throw lazyDOMException(
345+
'Non zero-length context is not yet supported.', 'NotSupportedError');
346+
}
347+
}
348+
349+
return jobPromise(new SignJob(
350+
kCryptoJobAsync,
351+
mode,
352+
key[kKeyObject][kHandle],
353+
undefined,
354+
undefined,
355+
undefined,
356+
data,
357+
undefined,
358+
undefined,
359+
undefined,
360+
undefined,
361+
signature));
362+
}
363+
364+
module.exports = {
365+
cfrgExportKey,
366+
cfrgImportKey,
367+
cfrgGenerateKey,
368+
eddsaSignVerify,
369+
};

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.