@pryv/encryption 3.11.0 → 3.12.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pryv/encryption",
3
- "version": "3.11.0",
3
+ "version": "3.12.0",
4
4
  "description": "Client-side encryption and decryption of Pryv.io events",
5
5
  "keywords": [
6
6
  "Pryv",
@@ -46,7 +46,7 @@ class EventsCipher {
46
46
  * `encrypt` is optional (a decrypt-only method supports reading existing
47
47
  * encrypted events but cannot produce new ones).
48
48
  * @param {string} name
49
- * @param {{ decrypt: Function, encrypt?: Function }} method
49
+ * @param {{ decrypt: Function, encrypt?: Function, encryptBytes?: Function, decryptBytes?: Function }} method
50
50
  * @returns {EventsCipher} this, for chaining.
51
51
  */
52
52
  registerMethod (name, method) {
@@ -72,7 +72,7 @@ class EventsCipher {
72
72
  * @param {*} [params.hint]
73
73
  * @returns {Promise<{ type: string, content: { payload: string, keyRef?: string, hint?: * } }>}
74
74
  */
75
- async encryptEventContent (material, params = {}) {
75
+ async encryptEventContent (material, params = /** @type {any} */ ({})) {
76
76
  const { method: methodName, keyRef, hint } = params;
77
77
  const method = this._methods[methodName];
78
78
  if (!method) {
@@ -103,7 +103,7 @@ class EventsCipher {
103
103
  * @param {*} [params.hint]
104
104
  * @returns {Promise<Object>} a new encrypted event (input is not mutated).
105
105
  */
106
- async encryptEvent (plainEvent, params = {}) {
106
+ async encryptEvent (plainEvent, params = /** @type {any} */ ({})) {
107
107
  const { type, content } = await this.encryptEventContent(plainEvent, params);
108
108
  const encrypted = Object.assign({}, plainEvent);
109
109
  encrypted.type = type;
@@ -180,7 +180,7 @@ class EventsCipher {
180
180
  * @param {*} [params.hint]
181
181
  * @returns {Promise<Uint8Array>} raw payload-layout bytes.
182
182
  */
183
- async encryptAttachmentData (bytes, params = {}) {
183
+ async encryptAttachmentData (bytes, params = /** @type {any} */ ({})) {
184
184
  const { method: methodName, keyRef, hint } = params;
185
185
  const method = this._methods[methodName];
186
186
  if (!method) {
@@ -43,7 +43,11 @@ async function importKey (key) {
43
43
  if (raw.length !== KEY_LENGTH) {
44
44
  throw new Error(`aes-256-gcm: key must be ${KEY_LENGTH} bytes, got ${raw.length}`);
45
45
  }
46
- return globalThis.crypto.subtle.importKey('raw', raw, ALGORITHM, false, ['encrypt', 'decrypt']);
46
+ // Cast at the Web Crypto boundary: since TypeScript 5.7 a bare
47
+ // `Uint8Array` is `Uint8Array<ArrayBufferLike>`, which admits
48
+ // SharedArrayBuffer and so is not a `BufferSource`. These bytes never
49
+ // come from shared memory, and Web Crypto rejects it regardless.
50
+ return globalThis.crypto.subtle.importKey('raw', /** @type {BufferSource} */ (raw), ALGORITHM, false, ['encrypt', 'decrypt']);
47
51
  }
48
52
 
49
53
  /**
@@ -58,7 +62,7 @@ async function importKey (key) {
58
62
  async function encryptBytes (bytes, key) {
59
63
  const cryptoKey = await importKey(key);
60
64
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH));
61
- const cipherBuffer = await globalThis.crypto.subtle.encrypt({ name: ALGORITHM, iv }, cryptoKey, bytes);
65
+ const cipherBuffer = await globalThis.crypto.subtle.encrypt({ name: ALGORITHM, iv }, cryptoKey, /** @type {BufferSource} */ (bytes));
62
66
  const cipherBytes = new Uint8Array(cipherBuffer);
63
67
 
64
68
  const out = new Uint8Array(iv.length + cipherBytes.length);
@@ -45,7 +45,7 @@ function evpBytesToKey (passphrase, salt) {
45
45
  input.set(block, 0);
46
46
  input.set(passphrase, block.length);
47
47
  input.set(salt, block.length + passphrase.length);
48
- block = md5(input);
48
+ block = /** @type {Uint8Array<ArrayBuffer>} */ (md5(input));
49
49
  const take = Math.min(block.length, needed - filled);
50
50
  derived.set(block.subarray(0, take), filled);
51
51
  filled += take;
@@ -85,8 +85,13 @@ async function decrypt (content, key) {
85
85
  const passphrase = new TextEncoder().encode(key);
86
86
  const { key: keyBytes, iv } = evpBytesToKey(passphrase, salt);
87
87
 
88
- const cryptoKey = await globalThis.crypto.subtle.importKey('raw', keyBytes, 'AES-CBC', false, ['decrypt']);
89
- const plainBuffer = await globalThis.crypto.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, ciphertext);
88
+ // See the boundary note in aes-256-gcm.js.
89
+ const cryptoKey = await globalThis.crypto.subtle.importKey('raw', /** @type {BufferSource} */ (keyBytes), 'AES-CBC', false, ['decrypt']);
90
+ const plainBuffer = await globalThis.crypto.subtle.decrypt(
91
+ { name: 'AES-CBC', iv: /** @type {BufferSource} */ (iv) },
92
+ cryptoKey,
93
+ /** @type {BufferSource} */ (ciphertext)
94
+ );
90
95
  return JSON.parse(new TextDecoder().decode(plainBuffer));
91
96
  }
92
97
 
@@ -106,29 +106,30 @@ async function normalizeKey (material, usage) {
106
106
  }
107
107
 
108
108
  if (wantPrivate) {
109
- return subtle.importKey('pkcs8', bytes, ALGORITHM, false, ['deriveBits']);
109
+ // See the boundary note in aes-256-gcm.js.
110
+ return subtle.importKey('pkcs8', /** @type {BufferSource} */ (bytes), ALGORITHM, false, ['deriveBits']);
110
111
  }
111
112
  if (bytes.length !== EPH_PUB_LENGTH || bytes[0] !== 0x04) {
112
113
  throw new Error('ecies-aes-256-gcm: public key must be a 65-byte SEC1 uncompressed point (0x04 || X || Y)');
113
114
  }
114
- return subtle.importKey('raw', bytes, ALGORITHM, false, []);
115
+ return subtle.importKey('raw', /** @type {BufferSource} */ (bytes), ALGORITHM, false, []);
115
116
  }
116
117
 
117
118
  /**
118
119
  * Derive the AES-256-GCM key from an ECDH shared secret via HKDF-SHA-256.
119
120
  * @param {Uint8Array} secret - the 32-byte ECDH shared secret.
120
- * @param {string[]} usages
121
+ * @param {KeyUsage[]} usages
121
122
  * @returns {Promise<CryptoKey>}
122
123
  */
123
124
  async function deriveAesKey (secret, usages) {
124
125
  const subtle = globalThis.crypto.subtle;
125
- const hkdfKey = await subtle.importKey('raw', secret, 'HKDF', false, ['deriveBits']);
126
+ const hkdfKey = await subtle.importKey('raw', /** @type {BufferSource} */ (secret), 'HKDF', false, ['deriveBits']);
126
127
  const aesBits = await subtle.deriveBits(
127
128
  { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: new TextEncoder().encode(INFO) },
128
129
  hkdfKey,
129
130
  256
130
131
  );
131
- return subtle.importKey('raw', new Uint8Array(aesBits), 'AES-GCM', false, usages);
132
+ return subtle.importKey('raw', /** @type {BufferSource} */ (new Uint8Array(aesBits)), 'AES-GCM', false, usages);
132
133
  }
133
134
 
134
135
  /**
@@ -150,7 +151,7 @@ async function encryptBytes (bytes, key) {
150
151
  const aesKey = await deriveAesKey(new Uint8Array(secretBits), ['encrypt']);
151
152
 
152
153
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_LENGTH));
153
- const cipherBuffer = await subtle.encrypt({ name: 'AES-GCM', iv }, aesKey, bytes);
154
+ const cipherBuffer = await subtle.encrypt({ name: 'AES-GCM', iv }, aesKey, /** @type {BufferSource} */ (bytes));
154
155
  const cipherBytes = new Uint8Array(cipherBuffer);
155
156
 
156
157
  const ephRaw = new Uint8Array(await subtle.exportKey('raw', ephemeral.publicKey));