From e3302d6b3bd1e195bf0c7a5a9a77918fdf97d69b Mon Sep 17 00:00:00 2001 From: Ayham Kteash Date: Wed, 10 Jan 2024 17:36:57 +0100 Subject: [PATCH] fix: add type definitions for s2kNames --- openpgp.d.ts | 10 ++++++ src/type/s2k/argon2.ts | 72 ++++++++++++++++++++++------------------- src/type/s2k/generic.ts | 44 +++++++++++++++++-------- src/type/s2k/gnu.ts | 7 ++-- src/type/s2k/index.ts | 15 ++++++--- 5 files changed, 93 insertions(+), 55 deletions(-) diff --git a/openpgp.d.ts b/openpgp.d.ts index cf20f911..4daa8d21 100644 --- a/openpgp.d.ts +++ b/openpgp.d.ts @@ -733,6 +733,16 @@ interface VerifyMessageResult = MaybeStream> { signatures: VerificationResult[]; } +export type S2KType = enums.s2k.argon2 | enums.s2k.gnu | enums.s2k.iterated | enums.s2k.salted | enums.s2k.simple + +type S2KNames = 'argon2' | 'gnu' | 'iterated' | 'salted' | 'simple' | string +export interface S2K { + type: S2KNames + read(bytes: Uint8Array):number + write(): Uint8Array + produceKey(passphrase: string, keySize: number): Promise +} + /** * Armor an OpenPGP binary packet block diff --git a/src/type/s2k/argon2.ts b/src/type/s2k/argon2.ts index 4de0b6e6..508fe229 100644 --- a/src/type/s2k/argon2.ts +++ b/src/type/s2k/argon2.ts @@ -2,7 +2,7 @@ import enums from '../../enums'; import util from '../../util'; import crypto from '../../crypto'; import type { default as loadArgonWasmModuleType } from 'argon2id'; -// import defaultConfig from '../../config'; + import { Config } from '../../../openpgp'; const ARGON2_TYPE = 0x02; // id const ARGON2_VERSION = 0x13; @@ -27,16 +27,15 @@ let argon2Promise: ReturnType; const ARGON2_WASM_MEMORY_THRESHOLD_RELOAD = 2 << 19; class Argon2S2K { - type: 'argon2'; - private salt: Uint8Array; - private t: number; - private p: number; - private encodedM: number; + type: 'argon2'; + private salt: Uint8Array; + private t: number; + private p: number; + private encodedM: number; /** - * @param {Object} [config] - Full configuration, defaults to openpgp.config - */ - constructor(config:Config) { - + * @param {Object} [config] - Full configuration, defaults to openpgp.config + */ + constructor(config: Config) { const { passes, parallelism, memoryExponent } = config.s2kArgon2Params; this.type = 'argon2'; @@ -55,10 +54,10 @@ class Argon2S2K { } /** - * Parsing function for argon2 string-to-key specifier. - * @param {Uint8Array} bytes - Payload of argon2 string-to-key specifier - * @returns {Integer} Actual length of the object. - */ + * Parsing function for argon2 string-to-key specifier. + * @param {Uint8Array} bytes - Payload of argon2 string-to-key specifier + * @returns {Integer} Actual length of the object. + */ read(bytes: Uint8Array) { let i = 0; @@ -73,27 +72,27 @@ class Argon2S2K { } /** - * Serializes s2k information - * @returns {Uint8Array} Binary representation of s2k. - */ + * Serializes s2k information + * @returns {Uint8Array} Binary representation of s2k. + */ write(): Uint8Array { const arr = [ new Uint8Array([enums.write(enums.s2k, this.type)]), this.salt, - new Uint8Array([this.t, this.p, this.encodedM]) + new Uint8Array([this.t, this.p, this.encodedM]), ]; return util.concatUint8Array(arr); } /** - * Produces a key using the specified passphrase and the defined - * hashAlgorithm - * @param {String} passphrase - Passphrase containing user input - * @returns {Promise} Produced key with a length corresponding to `keySize` - * @throws {Argon2OutOfMemoryError|Errors} - * @async - */ + * Produces a key using the specified passphrase and the defined + * hashAlgorithm + * @param {String} passphrase - Passphrase containing user input + * @returns {Promise} Produced key with a length corresponding to `keySize` + * @throws {Argon2OutOfMemoryError|Errors} + * @async + */ async produceKey(passphrase: string, keySize: number): Promise { const decodedM = 2 << (this.encodedM - 1); @@ -101,7 +100,8 @@ class Argon2S2K { // on first load, the argon2 lib is imported and the WASM module is initialized. // the two steps need to be atomic to avoid race conditions causing multiple wasm modules // being loaded when `argon2Promise` is not initialized. - loadArgonWasmModule = loadArgonWasmModule || (await import('argon2id')).default; + loadArgonWasmModule = + loadArgonWasmModule || (await import('argon2id')).default; argon2Promise = argon2Promise || loadArgonWasmModule(); // important to keep local ref to argon2 in case the module is reloaded by another instance @@ -117,7 +117,7 @@ class Argon2S2K { tagLength: keySize, memorySize: decodedM, parallelism: this.p, - passes: this.t + passes: this.t, }); // a lot of memory was used, reload to deallocate @@ -128,13 +128,17 @@ class Argon2S2K { } return hash; } catch (e) { - if (e instanceof Error && e.message && ( - e.message.includes('Unable to grow instance memory') || // Chrome - e.message.includes('failed to grow memory') || // Firefox - e.message.includes('WebAssembly.Memory.grow') || // Safari - e.message.includes('Out of memory') // Safari iOS - )) { - throw new Argon2OutOfMemoryError('Could not allocate required memory for Argon2'); + if ( + e instanceof Error && + e.message && + (e.message.includes('Unable to grow instance memory') || // Chrome + e.message.includes('failed to grow memory') || // Firefox + e.message.includes('WebAssembly.Memory.grow') || // Safari + e.message.includes('Out of memory')) // Safari iOS + ) { + throw new Argon2OutOfMemoryError( + 'Could not allocate required memory for Argon2' + ); } else { throw e; } diff --git a/src/type/s2k/generic.ts b/src/type/s2k/generic.ts index e3a4f54e..100ba99a 100644 --- a/src/type/s2k/generic.ts +++ b/src/type/s2k/generic.ts @@ -27,7 +27,12 @@ * @module type/s2k */ -import type { Config, enums as enumsType } from '../../../openpgp'; +import type { + Config, + S2KNames, + S2KType, + enums as enumsType, +} from '../../../openpgp'; import enums from '../../enums'; import crypto from '../../crypto'; @@ -36,16 +41,16 @@ import util from '../../util'; class GenericS2K { private algorithm: number; - type: string; + type: S2KNames; private c: number; private salt: Uint8Array | null; /** * @param {Object} [config] - Full configuration, defaults to openpgp.config */ - constructor(s2kType: enumsType.s2k.simple | enumsType.s2k.salted | enumsType.s2k.iterated, config:Config ) { + constructor(s2kType: S2KType, config: Config) { /** - * Hash function identifier, or 0 for gnu-dummy keys - * @type {module:enums.hash | 0} + * Hash function identifier + * @type {module:enums.hash} */ this.algorithm = enums.hash.sha256; /** @@ -81,7 +86,7 @@ class GenericS2K { * @param {Uint8Array} bytes - Payload of string-to-key specifier * @returns {Integer} Actual length of the object. */ - read(bytes: Uint8Array): Number { + read(bytes: Uint8Array): number { let i = 0; this.algorithm = bytes[i++]; @@ -103,10 +108,8 @@ class GenericS2K { break; default: - throw new UnsupportedError('Unknown s2k type.'); // unreachable } - return i; } @@ -115,17 +118,19 @@ class GenericS2K { * @returns {Uint8Array} Binary representation of s2k. */ write(): Uint8Array { - const arr = [new Uint8Array([enums.write(enums.s2k, this.type), this.algorithm])]; + const arr = [ + new Uint8Array([enums.write(enums.s2k, this.type), this.algorithm]), + ]; switch (this.type) { case 'simple': break; case 'salted': - if (!this.salt) { throw Error('Salt was not set') } + if (!this.salt) throw Error('Salt was not set'); arr.push(this.salt); break; case 'iterated': - this.salt &&arr.push(this.salt); + this.salt && arr.push(this.salt); arr.push(new Uint8Array([this.c])); break; default: @@ -154,10 +159,17 @@ class GenericS2K { let toHash; switch (this.type) { case 'simple': - toHash = util.concatUint8Array([new Uint8Array(prefixlen), encodedPassphrase]); + toHash = util.concatUint8Array([ + new Uint8Array(prefixlen), + encodedPassphrase, + ]); break; case 'salted': - toHash = util.concatUint8Array([new Uint8Array(prefixlen), this.salt, encodedPassphrase]); + toHash = util.concatUint8Array([ + new Uint8Array(prefixlen), + this.salt, + encodedPassphrase, + ]); break; case 'iterated': { const data = util.concatUint8Array([this.salt, encodedPassphrase]); @@ -165,7 +177,11 @@ class GenericS2K { const count = Math.max(this.getCount(), datalen); toHash = new Uint8Array(prefixlen + count); toHash.set(data, prefixlen); - for (let pos = prefixlen + datalen; pos < count; pos += datalen, datalen *= 2) { + for ( + let pos = prefixlen + datalen; + pos < count; + pos += datalen, datalen *= 2 + ) { toHash.copyWithin(pos, prefixlen, pos); } break; diff --git a/src/type/s2k/gnu.ts b/src/type/s2k/gnu.ts index 2fbceb10..7b61d9e1 100644 --- a/src/type/s2k/gnu.ts +++ b/src/type/s2k/gnu.ts @@ -15,21 +15,22 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import { S2KNames } from '../../../openpgp'; import enums from '../../enums'; import { UnsupportedError } from '../../packet/packet'; import util from '../../util'; class GnuS2K { - type: 'gnu' = 'gnu'; + type: S2KNames = 'gnu'; gnuType?: 'gnu-dummy' = undefined; - algorithm: number = enums.hash.sha256 + algorithm: number = enums.hash.sha256; /** * Parsing function for a string-to-key specifier ({@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC 4880 3.7}). * @param {Uint8Array} bytes - Payload of string-to-key specifier * @returns {Integer} Actual length of the object. */ - read(bytes: Uint8Array): Number { + read(bytes: Uint8Array): number { let i = 0; this.algorithm = bytes[i++]; if (util.uint8ArrayToString(bytes.subarray(i, i + 3)) === 'GNU') { diff --git a/src/type/s2k/index.ts b/src/type/s2k/index.ts index 0494f1cb..25836c44 100644 --- a/src/type/s2k/index.ts +++ b/src/type/s2k/index.ts @@ -5,8 +5,12 @@ import { UnsupportedError } from '../../packet/packet'; import GnuS2K from './gnu'; import defaultConfig from '../../config'; -import { Config } from '../../../openpgp'; -const allowedS2KTypesForEncryption = new Set([enums.s2k.argon2, enums.s2k.iterated]); +import { Config, S2K, S2KType } from '../../../openpgp'; + +const allowedS2KTypesForEncryption = new Set([ + enums.s2k.argon2, + enums.s2k.iterated, +]); /** * Instantiate a new S2K instance of the given type @@ -15,7 +19,10 @@ const allowedS2KTypesForEncryption = new Set([enums.s2k.argon2, enums.s2k.iterat * @returns {Object} New s2k object * @throws {Error} for unknown or unsupported types */ -export function newS2KFromType (type: number, config:Config = defaultConfig ): Argon2S2K | GenericS2K | GnuS2K { +export function newS2KFromType( + type: S2KType, + config: Config = defaultConfig +): S2K { switch (type) { case enums.s2k.gnu: return new GnuS2K(); @@ -36,7 +43,7 @@ export function newS2KFromType (type: number, config:Config = defaultConfig ): A * @returns {Object} New s2k object * @throws {Error} for unknown or unsupported types */ -export function newS2KFromConfig(config:Config = defaultConfig) { +export function newS2KFromConfig(config: Config = defaultConfig) { const { s2kType } = config; if (!allowedS2KTypesForEncryption.has(s2kType)) {