-
Notifications
You must be signed in to change notification settings - Fork 120
Feat/with async crypto #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
518ef5e
76e99ba
385ceae
71e8635
0a45864
ed2c514
b4a45e0
a812e4b
977bdf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'use strict'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nodeCrypto = require('crypto'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { webcrypto } = require('crypto'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const subtle = webcrypto.subtle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assert = require('assert'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -14,43 +15,45 @@ function assertBuffer(value) { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function encrypt(key, data, iv) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function encrypt(key, data, iv) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(iv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.concat([cipher.update(data), cipher.final()]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cryptoKey = await subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const encrypted = await subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.from(encrypted); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function decrypt(key, data, iv) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function decrypt(key, data, iv) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(iv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.concat([decipher.update(data), decipher.final()]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cryptoKey = await subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['decrypt']); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const decrypted = await subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.from(decrypted); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function calculateMAC(key, data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function calculateMAC(key, data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hmac = nodeCrypto.createHmac('sha256', key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| hmac.update(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.from(hmac.digest()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cryptoKey = await subtle.importKey( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mac = await subtle.sign('HMAC', cryptoKey, data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.from(mac); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function hash(data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sha512 = nodeCrypto.createHash('sha512'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sha512.update(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return sha512.digest(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function hash(data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = await subtle.digest('SHA-512', data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Buffer.from(result); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Salts always end up being 32 bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| function deriveSecrets(input, salt, info, chunks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function deriveSecrets(input, salt, info, chunks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Specific implementation of RFC 5869 that only returns the first 3 32-byte chunks | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(input); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assertBuffer(salt); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -60,31 +63,37 @@ function deriveSecrets(input, salt, info, chunks) { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| chunks = chunks || 3; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert(chunks >= 1 && chunks <= 3); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PRK = calculateMAC(salt, input); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PRK = await calculateMAC(salt, input); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const infoArray = new Uint8Array(info.byteLength + 1 + 32); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray.set(info, 32); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray[infoArray.length - 1] = 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const signed = [calculateMAC(PRK, Buffer.from(infoArray.slice(32)))]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const signed = [await calculateMAC(PRK, Buffer.from(infoArray.slice(32)))]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (chunks > 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray.set(signed[signed.length - 1]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray[infoArray.length - 1] = 2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| signed.push(calculateMAC(PRK, Buffer.from(infoArray))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| signed.push(await calculateMAC(PRK, Buffer.from(infoArray))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (chunks > 2) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray.set(signed[signed.length - 1]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| infoArray[infoArray.length - 1] = 3; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| signed.push(calculateMAC(PRK, Buffer.from(infoArray))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| signed.push(await calculateMAC(PRK, Buffer.from(infoArray))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return signed; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function verifyMAC(data, key, mac, length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const calculatedMac = calculateMAC(key, data).slice(0, length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function verifyMAC(data, key, mac, length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const calculatedMac = (await calculateMAC(key, data)).subarray(0, length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mac.length !== length || calculatedMac.length !== length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Bad MAC length"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Bad MAC length Expected: " + length + | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| " Got: " + mac.length + " and " + calculatedMac.length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let diff = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let i = 0; i < length; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| diff |= mac[i] ^ calculatedMac[i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!mac.equals(calculatedMac)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Bad MAC"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (diff !== 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Bad MAC Expected: " + calculatedMac.toString('hex') + | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| " Got: " + mac.toString('hex')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not leak MAC material in error strings.
Safer error handling- throw new Error("Bad MAC length Expected: " + length +
- " Got: " + mac.length + " and " + calculatedMac.length);
+ throw new Error('Bad MAC length');
...
- throw new Error("Bad MAC Expected: " + calculatedMac.toString('hex') +
- " Got: " + mac.toString('hex'));
+ throw new Error('Bad MAC');📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,12 +2,15 @@ | |||||||||||||
| 'use strict'; | ||||||||||||||
|
|
||||||||||||||
| const curveJs = require('curve25519-js'); | ||||||||||||||
| const nodeCrypto = require('crypto'); | ||||||||||||||
| const { webcrypto } = require('crypto'); | ||||||||||||||
| const subtle = webcrypto.subtle; | ||||||||||||||
|
|
||||||||||||||
| // DER prefixes for X25519 keys (used for WebCrypto import/export) | ||||||||||||||
| // from: https://github.com/digitalbazaar/x25519-key-agreement-key-2019/blob/master/lib/crypto.js | ||||||||||||||
| const PUBLIC_KEY_DER_PREFIX = Buffer.from([ | ||||||||||||||
| 48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0 | ||||||||||||||
| ]); | ||||||||||||||
|
Comment on lines
10
to
12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused constant This constant is defined but never used in the file. It may be leftover from the migration to WebCrypto. Consider removing it to avoid confusion. 🧹 Proposed fix-// DER prefixes for X25519 keys (used for WebCrypto import/export)
+// DER prefix for X25519 private keys (used for WebCrypto import/export)
// from: https://github.com/digitalbazaar/x25519-key-agreement-key-2019/blob/master/lib/crypto.js
-const PUBLIC_KEY_DER_PREFIX = Buffer.from([
- 48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0
-]);
-
const PRIVATE_KEY_DER_PREFIX = Buffer.from([📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 10-10: 'PUBLIC_KEY_DER_PREFIX' is assigned a value but never used. (no-unused-vars) 🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| const PRIVATE_KEY_DER_PREFIX = Buffer.from([ | ||||||||||||||
| 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32 | ||||||||||||||
| ]); | ||||||||||||||
|
|
@@ -38,7 +41,7 @@ function scrubPubKeyFormat(pubKey) { | |||||||||||||
| throw new Error("Invalid public key"); | ||||||||||||||
| } | ||||||||||||||
| if (pubKey.byteLength == 33) { | ||||||||||||||
| return pubKey.slice(1); | ||||||||||||||
| return pubKey.subarray(1); | ||||||||||||||
| } else { | ||||||||||||||
| console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); | ||||||||||||||
| return pubKey; | ||||||||||||||
|
|
@@ -58,76 +61,65 @@ function unclampEd25519PrivateKey(clampedSk) { | |||||||||||||
| return unclampedSk; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| exports.getPublicFromPrivateKey = function(privKey) { | ||||||||||||||
| exports.getPublicFromPrivateKey = async function(privKey) { | ||||||||||||||
| const unclampedPK = unclampEd25519PrivateKey(privKey); | ||||||||||||||
| const keyPair = curveJs.generateKeyPair(unclampedPK); | ||||||||||||||
| return prefixKeyInPublicKey(Buffer.from(keyPair.public)); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| exports.generateKeyPair = function() { | ||||||||||||||
| try { | ||||||||||||||
| const {publicKey: publicDerBytes, privateKey: privateDerBytes} = nodeCrypto.generateKeyPairSync( | ||||||||||||||
| 'x25519', | ||||||||||||||
| { | ||||||||||||||
| publicKeyEncoding: { format: 'der', type: 'spki' }, | ||||||||||||||
| privateKeyEncoding: { format: 'der', type: 'pkcs8' } | ||||||||||||||
| } | ||||||||||||||
| ); | ||||||||||||||
| const pubKey = publicDerBytes.slice(PUBLIC_KEY_DER_PREFIX.length, PUBLIC_KEY_DER_PREFIX.length + 32); | ||||||||||||||
|
|
||||||||||||||
| const privKey = privateDerBytes.slice(PRIVATE_KEY_DER_PREFIX.length, PRIVATE_KEY_DER_PREFIX.length + 32); | ||||||||||||||
|
|
||||||||||||||
| return { | ||||||||||||||
| pubKey: prefixKeyInPublicKey(pubKey), | ||||||||||||||
| privKey | ||||||||||||||
| }; | ||||||||||||||
| } catch(e) { | ||||||||||||||
| const keyPair = curveJs.generateKeyPair(nodeCrypto.randomBytes(32)); | ||||||||||||||
| return { | ||||||||||||||
| privKey: Buffer.from(keyPair.private), | ||||||||||||||
| pubKey: prefixKeyInPublicKey(Buffer.from(keyPair.public)), | ||||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
| exports.generateKeyPair = async function() { | ||||||||||||||
| const keyPair = await subtle.generateKey({ name: 'X25519' }, true, ['deriveBits']); | ||||||||||||||
|
|
||||||||||||||
| const publicKeyRaw = await subtle.exportKey('raw', keyPair.publicKey); | ||||||||||||||
| const privateKeyDer = await subtle.exportKey('pkcs8', keyPair.privateKey); | ||||||||||||||
|
|
||||||||||||||
| const pubKey = Buffer.from(publicKeyRaw); | ||||||||||||||
| const privKey = Buffer.from(new Uint8Array(privateKeyDer).subarray(PRIVATE_KEY_DER_PREFIX.length)); | ||||||||||||||
|
|
||||||||||||||
| return { | ||||||||||||||
| pubKey: prefixKeyInPublicKey(pubKey), | ||||||||||||||
| privKey | ||||||||||||||
| }; | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| exports.calculateAgreement = function(pubKey, privKey) { | ||||||||||||||
| exports.calculateAgreement = async function(pubKey, privKey) { | ||||||||||||||
| pubKey = scrubPubKeyFormat(pubKey); | ||||||||||||||
| validatePrivKey(privKey); | ||||||||||||||
| if (!pubKey || pubKey.byteLength != 32) { | ||||||||||||||
| throw new Error("Invalid public key"); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if(typeof nodeCrypto.diffieHellman === 'function') { | ||||||||||||||
| const nodePrivateKey = nodeCrypto.createPrivateKey({ | ||||||||||||||
| key: Buffer.concat([PRIVATE_KEY_DER_PREFIX, privKey]), | ||||||||||||||
| format: 'der', | ||||||||||||||
| type: 'pkcs8' | ||||||||||||||
| }); | ||||||||||||||
| const nodePublicKey = nodeCrypto.createPublicKey({ | ||||||||||||||
| key: Buffer.concat([PUBLIC_KEY_DER_PREFIX, pubKey]), | ||||||||||||||
| format: 'der', | ||||||||||||||
| type: 'spki' | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| return nodeCrypto.diffieHellman({ | ||||||||||||||
| privateKey: nodePrivateKey, | ||||||||||||||
| publicKey: nodePublicKey, | ||||||||||||||
| }); | ||||||||||||||
| } else { | ||||||||||||||
| const secret = curveJs.sharedKey(privKey, pubKey); | ||||||||||||||
| return Buffer.from(secret); | ||||||||||||||
| } | ||||||||||||||
| const privateKeyObj = await subtle.importKey( | ||||||||||||||
| 'pkcs8', | ||||||||||||||
| Buffer.concat([PRIVATE_KEY_DER_PREFIX, privKey]), | ||||||||||||||
| { name: 'X25519' }, | ||||||||||||||
| false, | ||||||||||||||
| ['deriveBits'] | ||||||||||||||
| ); | ||||||||||||||
| const publicKeyObj = await subtle.importKey( | ||||||||||||||
| 'raw', | ||||||||||||||
| pubKey, | ||||||||||||||
| { name: 'X25519' }, | ||||||||||||||
| false, | ||||||||||||||
| [] | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| const shared = await subtle.deriveBits({ name: 'X25519', public: publicKeyObj }, privateKeyObj, 256); | ||||||||||||||
| return Buffer.from(shared); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| exports.calculateSignature = function(privKey, message) { | ||||||||||||||
| // XEdDSA signatures use Curve25519 keys converted to Ed25519-style — not supported by | ||||||||||||||
| // WebCrypto, so we keep using curve25519-js here but expose an async interface for | ||||||||||||||
| // consistency with the rest of the module. | ||||||||||||||
| exports.calculateSignature = async function(privKey, message) { | ||||||||||||||
| validatePrivKey(privKey); | ||||||||||||||
| if (!message) { | ||||||||||||||
| throw new Error("Invalid message"); | ||||||||||||||
| } | ||||||||||||||
| return Buffer.from(curveJs.sign(privKey, message)); | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| exports.verifySignature = function(pubKey, msg, sig, isInit) { | ||||||||||||||
| exports.verifySignature = async function(pubKey, msg, sig, isInit) { | ||||||||||||||
| pubKey = scrubPubKeyFormat(pubKey); | ||||||||||||||
| if (!pubKey || pubKey.byteLength != 32) { | ||||||||||||||
| throw new Error("Invalid public key"); | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| // vim: ts=4:sw=4:expandtab | ||
|
|
||
| const curve = require('./curve'); | ||
| const nodeCrypto = require('crypto'); | ||
| const { webcrypto } = require('crypto'); | ||
|
|
||
| function isNonNegativeInteger(n) { | ||
| return (typeof n === 'number' && (n % 1) === 0 && n >= 0); | ||
|
|
@@ -10,11 +10,11 @@ function isNonNegativeInteger(n) { | |
| exports.generateIdentityKeyPair = curve.generateKeyPair; | ||
|
|
||
| exports.generateRegistrationId = function() { | ||
| var registrationId = Uint16Array.from(nodeCrypto.randomBytes(2))[0]; | ||
| var registrationId = webcrypto.getRandomValues(new Uint16Array(1))[0]; | ||
| return registrationId & 0x3fff; | ||
| }; | ||
|
|
||
| exports.generateSignedPreKey = function(identityKeyPair, signedKeyId) { | ||
| exports.generateSignedPreKey = async function(identityKeyPair, signedKeyId) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a breaking public API change.
Also applies to: 36-36 🤖 Prompt for AI Agents |
||
| if (!(identityKeyPair.privKey instanceof Buffer) || | ||
| identityKeyPair.privKey.byteLength != 32 || | ||
| !(identityKeyPair.pubKey instanceof Buffer) || | ||
|
|
@@ -24,20 +24,20 @@ exports.generateSignedPreKey = function(identityKeyPair, signedKeyId) { | |
| if (!isNonNegativeInteger(signedKeyId)) { | ||
| throw new TypeError('Invalid argument for signedKeyId: ' + signedKeyId); | ||
| } | ||
| const keyPair = curve.generateKeyPair(); | ||
| const sig = curve.calculateSignature(identityKeyPair.privKey, keyPair.pubKey); | ||
| const keyPair = await curve.generateKeyPair(); | ||
| const sig = await curve.calculateSignature(identityKeyPair.privKey, keyPair.pubKey); | ||
| return { | ||
| keyId: signedKeyId, | ||
| keyPair: keyPair, | ||
| signature: sig | ||
| }; | ||
| }; | ||
|
|
||
| exports.generatePreKey = function(keyId) { | ||
| exports.generatePreKey = async function(keyId) { | ||
| if (!isNonNegativeInteger(keyId)) { | ||
| throw new TypeError('Invalid argument for keyId: ' + keyId); | ||
| } | ||
| const keyPair = curve.generateKeyPair(); | ||
| const keyPair = await curve.generateKeyPair(); | ||
| return { | ||
| keyId, | ||
| keyPair | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,14 +10,41 @@ | |||||
| const _queueAsyncBuckets = new Map(); | ||||||
| const _gcLimit = 10000; | ||||||
|
|
||||||
|
|
||||||
|
|
||||||
| /* | ||||||
| * This is a wrapper around the async function that will reject if it | ||||||
| * takes longer than the specified timeout. This is useful for | ||||||
| * preventing a job from hanging indefinitely. The default timeout | ||||||
| * is 30 seconds. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Documentation says "The default timeout is 30 seconds" but the actual default is 15 seconds ( Prompt for AI agents
Suggested change
|
||||||
| */ | ||||||
| function withTimeout(fn, ms = 15000) { | ||||||
| if (typeof fn !== 'function') { | ||||||
| throw new TypeError('fn must be a function to wrap received ' + typeof fn); | ||||||
| } | ||||||
| if (typeof ms !== 'number') { | ||||||
| throw new TypeError('ms must be a number to wrap received ' + typeof ms); | ||||||
| } | ||||||
| return new Promise((resolve, reject) => { | ||||||
| const timer = setTimeout(() => reject(new Error('Job timed out')), ms); | ||||||
| fn().then((res) => { | ||||||
| clearTimeout(timer); | ||||||
| resolve(res); | ||||||
| }).catch((err) => { | ||||||
| clearTimeout(timer); | ||||||
| reject(err); | ||||||
| }); | ||||||
| }); | ||||||
|
Comment on lines
+21
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve queue ordering even after a timeout. Once Minimal direction- job.resolve(await withTimeout(job.awaitable)); // if the job takes longer than 15 seconds, it will be rejected
+ const work = Promise.resolve().then(job.awaitable);
+ try {
+ job.resolve(await withTimeout(() => work));
+ } catch (e) {
+ job.reject(e);
+ } finally {
+ try { await work; } catch (_) {}
+ }Also applies to: 47-47 🤖 Prompt for AI Agents |
||||||
| } | ||||||
|
|
||||||
| async function _asyncQueueExecutor(queue, cleanup) { | ||||||
| let offt = 0; | ||||||
| while (true) { | ||||||
| let limit = Math.min(queue.length, _gcLimit); // Break up thundering hurds for GC duty. | ||||||
| for (let i = offt; i < limit; i++) { | ||||||
| const job = queue[i]; | ||||||
| try { | ||||||
| job.resolve(await job.awaitable()); | ||||||
| job.resolve(await withTimeout(job.awaitable)); // if the job takes longer than 15 seconds, it will be rejected | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: When Prompt for AI agents |
||||||
| } catch(e) { | ||||||
| job.reject(e); | ||||||
| } | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Similarly, the MAC length error now includes the expected and actual lengths. Keep MAC verification errors generic to avoid leaking any information about the verification process.
Prompt for AI agents