@twin.org/crypto 0.0.3-next.2 → 0.0.3-next.21

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.
@@ -0,0 +1,67 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { Converter, Guards } from "@twin.org/core";
4
+ import { Sha256 } from "../hashes/sha256.js";
5
+ import { Sha512 } from "../hashes/sha512.js";
6
+ import { IntegrityAlgorithm } from "../models/integrityAlgorithm.js";
7
+ /**
8
+ * Helper class for creating integrity signatures.
9
+ * @see https://www.w3.org/TR/SRI/
10
+ */
11
+ export class IntegrityHelper {
12
+ /**
13
+ * Runtime name for the class.
14
+ */
15
+ static CLASS_NAME = "IntegrityHelper";
16
+ /**
17
+ * Generate an integrity signature for the given content using the specified hash algorithm.
18
+ * @param type The hash algorithm to use, either "sha256", "sha384" or "sha512".
19
+ * @param content The content to hash as a Uint8Array.
20
+ * @returns The integrity signature in the format "type-base64hash".
21
+ */
22
+ static generate(type, content) {
23
+ Guards.arrayOneOf(IntegrityHelper.CLASS_NAME, "type", type, Object.values(IntegrityAlgorithm));
24
+ Guards.uint8Array(IntegrityHelper.CLASS_NAME, "content", content);
25
+ return `${type}-${IntegrityHelper.generateHash(type, content)}`;
26
+ }
27
+ /**
28
+ * Verify an integrity signature for the given content.
29
+ * @param integrity The integrity signature in the format "type-base64hash".
30
+ * @param content The content to hash as a Uint8Array.
31
+ * @returns True if the integrity signature matches the content.
32
+ * @throws If the integrity signature is invalid.
33
+ */
34
+ static verify(integrity, content) {
35
+ Guards.stringValue(IntegrityHelper.CLASS_NAME, "integrity", integrity);
36
+ Guards.uint8Array(IntegrityHelper.CLASS_NAME, "content", content);
37
+ const parts = integrity.split("-");
38
+ if (parts.length !== 2) {
39
+ return false;
40
+ }
41
+ const [type, hash] = parts;
42
+ Guards.arrayOneOf(IntegrityHelper.CLASS_NAME, "type", type, Object.values(IntegrityAlgorithm));
43
+ Guards.stringValue(IntegrityHelper.CLASS_NAME, "hash", hash);
44
+ return IntegrityHelper.generateHash(type, content) === hash;
45
+ }
46
+ /**
47
+ * Generate a hash for the given content using the specified type.
48
+ * @param type The hash algorithm to use, either "sha256", "sha384" or "sha512".
49
+ * @param content The content to hash as a Uint8Array.
50
+ * @returns The integrity signature in the format "type-base64hash".
51
+ * @internal
52
+ */
53
+ static generateHash(type, content) {
54
+ let hash;
55
+ if (type === IntegrityAlgorithm.Sha384) {
56
+ hash = Sha512.sum384(content);
57
+ }
58
+ else if (type === IntegrityAlgorithm.Sha512) {
59
+ hash = Sha512.sum512(content);
60
+ }
61
+ else {
62
+ hash = Sha256.sum256(content);
63
+ }
64
+ return Converter.bytesToBase64(hash);
65
+ }
66
+ }
67
+ //# sourceMappingURL=integrityHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrityHelper.js","sourceRoot":"","sources":["../../../src/helpers/integrityHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE;;;GAGG;AACH,MAAM,OAAO,eAAe;IAC3B;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;;;OAKG;IACI,MAAM,CAAC,QAAQ,CAAC,IAAwB,EAAE,OAAmB;QACnE,MAAM,CAAC,UAAU,CAChB,eAAe,CAAC,UAAU,UAE1B,IAAI,EACJ,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACjC,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAExE,OAAO,GAAG,IAAI,IAAI,eAAe,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;IACjE,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,MAAM,CAAC,SAAiB,EAAE,OAAmB;QAC1D,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,eAAqB,SAAS,CAAC,CAAC;QAC7E,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAExE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAE3B,MAAM,CAAC,UAAU,CAChB,eAAe,CAAC,UAAU,UAE1B,IAA0B,EAC1B,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CACjC,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAEnE,OAAO,eAAe,CAAC,YAAY,CAAC,IAA0B,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnF,CAAC;IAED;;;;;;OAMG;IACK,MAAM,CAAC,YAAY,CAAC,IAAwB,EAAE,OAAmB;QACxE,IAAI,IAAI,CAAC;QACT,IAAI,IAAI,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;YACxC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,IAAI,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;YAC/C,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACP,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { Sha256 } from \"../hashes/sha256.js\";\nimport { Sha512 } from \"../hashes/sha512.js\";\nimport { IntegrityAlgorithm } from \"../models/integrityAlgorithm.js\";\n\n/**\n * Helper class for creating integrity signatures.\n * @see https://www.w3.org/TR/SRI/\n */\nexport class IntegrityHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<IntegrityHelper>();\n\n\t/**\n\t * Generate an integrity signature for the given content using the specified hash algorithm.\n\t * @param type The hash algorithm to use, either \"sha256\", \"sha384\" or \"sha512\".\n\t * @param content The content to hash as a Uint8Array.\n\t * @returns The integrity signature in the format \"type-base64hash\".\n\t */\n\tpublic static generate(type: IntegrityAlgorithm, content: Uint8Array): string {\n\t\tGuards.arrayOneOf(\n\t\t\tIntegrityHelper.CLASS_NAME,\n\t\t\tnameof(type),\n\t\t\ttype,\n\t\t\tObject.values(IntegrityAlgorithm)\n\t\t);\n\t\tGuards.uint8Array(IntegrityHelper.CLASS_NAME, nameof(content), content);\n\n\t\treturn `${type}-${IntegrityHelper.generateHash(type, content)}`;\n\t}\n\n\t/**\n\t * Verify an integrity signature for the given content.\n\t * @param integrity The integrity signature in the format \"type-base64hash\".\n\t * @param content The content to hash as a Uint8Array.\n\t * @returns True if the integrity signature matches the content.\n\t * @throws If the integrity signature is invalid.\n\t */\n\tpublic static verify(integrity: string, content: Uint8Array): boolean {\n\t\tGuards.stringValue(IntegrityHelper.CLASS_NAME, nameof(integrity), integrity);\n\t\tGuards.uint8Array(IntegrityHelper.CLASS_NAME, nameof(content), content);\n\n\t\tconst parts = integrity.split(\"-\");\n\t\tif (parts.length !== 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst [type, hash] = parts;\n\n\t\tGuards.arrayOneOf(\n\t\t\tIntegrityHelper.CLASS_NAME,\n\t\t\tnameof(type),\n\t\t\ttype as IntegrityAlgorithm,\n\t\t\tObject.values(IntegrityAlgorithm)\n\t\t);\n\t\tGuards.stringValue(IntegrityHelper.CLASS_NAME, nameof(hash), hash);\n\n\t\treturn IntegrityHelper.generateHash(type as IntegrityAlgorithm, content) === hash;\n\t}\n\n\t/**\n\t * Generate a hash for the given content using the specified type.\n\t * @param type The hash algorithm to use, either \"sha256\", \"sha384\" or \"sha512\".\n\t * @param content The content to hash as a Uint8Array.\n\t * @returns The integrity signature in the format \"type-base64hash\".\n\t * @internal\n\t */\n\tprivate static generateHash(type: IntegrityAlgorithm, content: Uint8Array): string {\n\t\tlet hash;\n\t\tif (type === IntegrityAlgorithm.Sha384) {\n\t\t\thash = Sha512.sum384(content);\n\t\t} else if (type === IntegrityAlgorithm.Sha512) {\n\t\t\thash = Sha512.sum512(content);\n\t\t} else {\n\t\t\thash = Sha256.sum256(content);\n\t\t}\n\t\treturn Converter.bytesToBase64(hash);\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -17,10 +17,12 @@ export * from "./hashes/sha1.js";
17
17
  export * from "./hashes/sha256.js";
18
18
  export * from "./hashes/sha3.js";
19
19
  export * from "./hashes/sha512.js";
20
+ export * from "./helpers/integrityHelper.js";
20
21
  export * from "./helpers/pemHelper.js";
21
22
  export * from "./keys/bip32Path.js";
22
23
  export * from "./keys/bip39.js";
23
24
  export * from "./keys/slip0010.js";
25
+ export * from "./models/integrityAlgorithm.js";
24
26
  export * from "./models/keyType.js";
25
27
  export * from "./otp/hotp.js";
26
28
  export * from "./otp/totp.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./address/bech32.js\";\nexport * from \"./address/bip44.js\";\nexport * from \"./ciphers/chaCha20Poly1305.js\";\nexport * from \"./curves/ed25519.js\";\nexport * from \"./curves/secp256k1.js\";\nexport * from \"./curves/x25519.js\";\nexport * from \"./curves/zip215.js\";\nexport * from \"./hashes/blake2b.js\";\nexport * from \"./hashes/blake3.js\";\nexport * from \"./hashes/hmacSha1.js\";\nexport * from \"./hashes/hmacSha256.js\";\nexport * from \"./hashes/hmacSha512.js\";\nexport * from \"./hashes/pbkdf2.js\";\nexport * from \"./hashes/sha1.js\";\nexport * from \"./hashes/sha256.js\";\nexport * from \"./hashes/sha3.js\";\nexport * from \"./hashes/sha512.js\";\nexport * from \"./helpers/pemHelper.js\";\nexport * from \"./keys/bip32Path.js\";\nexport * from \"./keys/bip39.js\";\nexport * from \"./keys/slip0010.js\";\nexport * from \"./models/keyType.js\";\nexport * from \"./otp/hotp.js\";\nexport * from \"./otp/totp.js\";\nexport * from \"./passwords/passwordGenerator.js\";\nexport * from \"./passwords/passwordValidator.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./address/bech32.js\";\nexport * from \"./address/bip44.js\";\nexport * from \"./ciphers/chaCha20Poly1305.js\";\nexport * from \"./curves/ed25519.js\";\nexport * from \"./curves/secp256k1.js\";\nexport * from \"./curves/x25519.js\";\nexport * from \"./curves/zip215.js\";\nexport * from \"./hashes/blake2b.js\";\nexport * from \"./hashes/blake3.js\";\nexport * from \"./hashes/hmacSha1.js\";\nexport * from \"./hashes/hmacSha256.js\";\nexport * from \"./hashes/hmacSha512.js\";\nexport * from \"./hashes/pbkdf2.js\";\nexport * from \"./hashes/sha1.js\";\nexport * from \"./hashes/sha256.js\";\nexport * from \"./hashes/sha3.js\";\nexport * from \"./hashes/sha512.js\";\nexport * from \"./helpers/integrityHelper.js\";\nexport * from \"./helpers/pemHelper.js\";\nexport * from \"./keys/bip32Path.js\";\nexport * from \"./keys/bip39.js\";\nexport * from \"./keys/slip0010.js\";\nexport * from \"./models/integrityAlgorithm.js\";\nexport * from \"./models/keyType.js\";\nexport * from \"./otp/hotp.js\";\nexport * from \"./otp/totp.js\";\nexport * from \"./passwords/passwordGenerator.js\";\nexport * from \"./passwords/passwordValidator.js\";\n"]}
@@ -0,0 +1,21 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ /**
4
+ * The names of the integrity algorithms.
5
+ */
6
+ // eslint-disable-next-line @typescript-eslint/naming-convention
7
+ export const IntegrityAlgorithm = {
8
+ /**
9
+ * Sha256.
10
+ */
11
+ Sha256: "sha256",
12
+ /**
13
+ * Sha384.
14
+ */
15
+ Sha384: "sha384",
16
+ /**
17
+ * Sha512.
18
+ */
19
+ Sha512: "sha512"
20
+ };
21
+ //# sourceMappingURL=integrityAlgorithm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrityAlgorithm.js","sourceRoot":"","sources":["../../../src/models/integrityAlgorithm.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,MAAM,EAAE,QAAQ;IAEhB;;OAEG;IACH,MAAM,EAAE,QAAQ;IAEhB;;OAEG;IACH,MAAM,EAAE,QAAQ;CACP,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The names of the integrity algorithms.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const IntegrityAlgorithm = {\n\t/**\n\t * Sha256.\n\t */\n\tSha256: \"sha256\",\n\n\t/**\n\t * Sha384.\n\t */\n\tSha384: \"sha384\",\n\n\t/**\n\t * Sha512.\n\t */\n\tSha512: \"sha512\"\n} as const;\n\n/**\n * Integrity algorithms.\n */\nexport type IntegrityAlgorithm = (typeof IntegrityAlgorithm)[keyof typeof IntegrityAlgorithm];\n"]}
@@ -1,28 +1,86 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- import { RandomHelper } from "@twin.org/core";
3
+ import { Converter, Guards, RandomHelper } from "@twin.org/core";
4
+ import { Blake2b } from "../hashes/blake2b.js";
4
5
  /**
5
6
  * Generate random passwords.
6
7
  */
7
8
  export class PasswordGenerator {
9
+ /**
10
+ * Runtime name for the class.
11
+ */
12
+ static CLASS_NAME = "PasswordGenerator";
13
+ /**
14
+ * The minimum password length, 15 to match owasp rules.
15
+ * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
16
+ * @internal
17
+ */
18
+ static _DEFAULT_MIN_PASSWORD_LENGTH = 15;
8
19
  /**
9
20
  * Generate a password of given length.
10
- * @param length The length of the password to generate.
21
+ * @param length The length of the password to generate, default to 15.
11
22
  * @returns The random password.
12
23
  */
13
- static generate(length) {
14
- const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
15
- const alphabet2 = `${alphabet}0123456789!#$£%^&*+=@~?}`;
24
+ static generate(length = PasswordGenerator._DEFAULT_MIN_PASSWORD_LENGTH) {
25
+ const lower = "abcdefghijklmnopqrstuvwxyz";
26
+ const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
27
+ const digits = "0123456789";
28
+ const specials = "!#$£%^&*+=@~?}";
29
+ const alphabet = `${lower}${upper}`;
30
+ const allChars = `${alphabet}${digits}${specials}`;
31
+ const targetLength = Math.max(length, PasswordGenerator._DEFAULT_MIN_PASSWORD_LENGTH);
16
32
  const chars = [];
17
- while (chars.length < length) {
18
- const charSet = chars.length === 0 ? alphabet : alphabet2;
19
- let b = 0;
20
- do {
21
- b = RandomHelper.generate(1)[0];
22
- } while (b >= charSet.length);
23
- chars.push(charSet[b]);
33
+ // Ensure required character classes are present.
34
+ PasswordGenerator.pushChar(chars, lower);
35
+ PasswordGenerator.pushChar(chars, upper);
36
+ PasswordGenerator.pushChar(chars, digits);
37
+ PasswordGenerator.pushChar(chars, specials);
38
+ while (chars.length < targetLength) {
39
+ const charSet = chars.length === 0 ? alphabet : allChars;
40
+ PasswordGenerator.pushChar(chars, charSet);
24
41
  }
25
42
  return chars.join("");
26
43
  }
44
+ /**
45
+ * Hash the password for the user.
46
+ * @param passwordBytes The password bytes.
47
+ * @param saltBytes The salt bytes.
48
+ * @returns The hashed password.
49
+ */
50
+ static async hashPassword(passwordBytes, saltBytes) {
51
+ Guards.uint8Array(PasswordGenerator.CLASS_NAME, "passwordBytes", passwordBytes);
52
+ Guards.uint8Array(PasswordGenerator.CLASS_NAME, "saltBytes", saltBytes);
53
+ const combined = new Uint8Array(saltBytes.length + passwordBytes.length);
54
+ combined.set(saltBytes);
55
+ combined.set(passwordBytes, saltBytes.length);
56
+ const hashedPassword = Blake2b.sum256(combined);
57
+ return Converter.bytesToBase64(hashedPassword);
58
+ }
59
+ /**
60
+ * Get a random character from the given character set.
61
+ * @param charSet The character set to get a random character from.
62
+ * @returns A random character from the given character set.
63
+ * @internal
64
+ */
65
+ static getRandomChar(charSet) {
66
+ let b = 0;
67
+ do {
68
+ b = RandomHelper.generate(1)[0];
69
+ } while (b >= charSet.length);
70
+ return charSet[b];
71
+ }
72
+ /**
73
+ * Push a random character from the given character set to the chars array, ensuring no three repeated characters in a row.
74
+ * @param chars The array to push the character to.
75
+ * @param charSet The character set to get a random character from.
76
+ * @internal
77
+ */
78
+ static pushChar(chars, charSet) {
79
+ let next = PasswordGenerator.getRandomChar(charSet);
80
+ while (chars.length >= 2 && next === chars.at(-1) && next === chars.at(-2)) {
81
+ next = PasswordGenerator.getRandomChar(charSet);
82
+ }
83
+ chars.push(next);
84
+ }
27
85
  }
28
86
  //# sourceMappingURL=passwordGenerator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"passwordGenerator.js","sourceRoot":"","sources":["../../../src/passwords/passwordGenerator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;GAEG;AACH,MAAM,OAAO,iBAAiB;IAC7B;;;;OAIG;IACI,MAAM,CAAC,QAAQ,CAAC,MAAc;QACpC,MAAM,QAAQ,GAAG,sDAAsD,CAAC;QACxE,MAAM,SAAS,GAAG,GAAG,QAAQ,0BAA0B,CAAC;QAExD,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,OAAO,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,GAAG,CAAC;gBACH,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE;YAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvB,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { RandomHelper } from \"@twin.org/core\";\n\n/**\n * Generate random passwords.\n */\nexport class PasswordGenerator {\n\t/**\n\t * Generate a password of given length.\n\t * @param length The length of the password to generate.\n\t * @returns The random password.\n\t */\n\tpublic static generate(length: number): string {\n\t\tconst alphabet = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\t\tconst alphabet2 = `${alphabet}0123456789!#$£%^&*+=@~?}`;\n\n\t\tconst chars: string[] = [];\n\n\t\twhile (chars.length < length) {\n\t\t\tconst charSet = chars.length === 0 ? alphabet : alphabet2;\n\t\t\tlet b = 0;\n\t\t\tdo {\n\t\t\t\tb = RandomHelper.generate(1)[0];\n\t\t\t} while (b >= charSet.length);\n\t\t\tchars.push(charSet[b]);\n\t\t}\n\n\t\treturn chars.join(\"\");\n\t}\n}\n"]}
1
+ {"version":3,"file":"passwordGenerator.js","sourceRoot":"","sources":["../../../src/passwords/passwordGenerator.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, Guards, RandomHelper } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { Blake2b } from \"../hashes/blake2b.js\";\n\n/**\n * Generate random passwords.\n */\nexport class PasswordGenerator {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<PasswordGenerator>();\n\n\t/**\n\t * The minimum password length, 15 to match owasp rules.\n\t * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_MIN_PASSWORD_LENGTH: number = 15;\n\n\t/**\n\t * Generate a password of given length.\n\t * @param length The length of the password to generate, default to 15.\n\t * @returns The random password.\n\t */\n\tpublic static generate(length: number = PasswordGenerator._DEFAULT_MIN_PASSWORD_LENGTH): string {\n\t\tconst lower = \"abcdefghijklmnopqrstuvwxyz\";\n\t\tconst upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n\t\tconst digits = \"0123456789\";\n\t\tconst specials = \"!#$£%^&*+=@~?}\";\n\t\tconst alphabet = `${lower}${upper}`;\n\t\tconst allChars = `${alphabet}${digits}${specials}`;\n\n\t\tconst targetLength = Math.max(length, PasswordGenerator._DEFAULT_MIN_PASSWORD_LENGTH);\n\t\tconst chars: string[] = [];\n\n\t\t// Ensure required character classes are present.\n\t\tPasswordGenerator.pushChar(chars, lower);\n\t\tPasswordGenerator.pushChar(chars, upper);\n\t\tPasswordGenerator.pushChar(chars, digits);\n\t\tPasswordGenerator.pushChar(chars, specials);\n\n\t\twhile (chars.length < targetLength) {\n\t\t\tconst charSet = chars.length === 0 ? alphabet : allChars;\n\t\t\tPasswordGenerator.pushChar(chars, charSet);\n\t\t}\n\n\t\treturn chars.join(\"\");\n\t}\n\n\t/**\n\t * Hash the password for the user.\n\t * @param passwordBytes The password bytes.\n\t * @param saltBytes The salt bytes.\n\t * @returns The hashed password.\n\t */\n\tpublic static async hashPassword(\n\t\tpasswordBytes: Uint8Array,\n\t\tsaltBytes: Uint8Array\n\t): Promise<string> {\n\t\tGuards.uint8Array(PasswordGenerator.CLASS_NAME, nameof(passwordBytes), passwordBytes);\n\t\tGuards.uint8Array(PasswordGenerator.CLASS_NAME, nameof(saltBytes), saltBytes);\n\n\t\tconst combined = new Uint8Array(saltBytes.length + passwordBytes.length);\n\t\tcombined.set(saltBytes);\n\t\tcombined.set(passwordBytes, saltBytes.length);\n\n\t\tconst hashedPassword = Blake2b.sum256(combined);\n\n\t\treturn Converter.bytesToBase64(hashedPassword);\n\t}\n\n\t/**\n\t * Get a random character from the given character set.\n\t * @param charSet The character set to get a random character from.\n\t * @returns A random character from the given character set.\n\t * @internal\n\t */\n\tprivate static getRandomChar(charSet: string): string {\n\t\tlet b = 0;\n\t\tdo {\n\t\t\tb = RandomHelper.generate(1)[0];\n\t\t} while (b >= charSet.length);\n\t\treturn charSet[b];\n\t}\n\n\t/**\n\t * Push a random character from the given character set to the chars array, ensuring no three repeated characters in a row.\n\t * @param chars The array to push the character to.\n\t * @param charSet The character set to get a random character from.\n\t * @internal\n\t */\n\tprivate static pushChar(chars: string[], charSet: string): void {\n\t\tlet next = PasswordGenerator.getRandomChar(charSet);\n\t\twhile (chars.length >= 2 && next === chars.at(-1) && next === chars.at(-2)) {\n\t\t\tnext = PasswordGenerator.getRandomChar(charSet);\n\t\t}\n\t\tchars.push(next);\n\t}\n}\n"]}
@@ -1,25 +1,35 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- import { Validation } from "@twin.org/core";
3
+ import { Converter, Guards, Validation } from "@twin.org/core";
4
4
  /**
5
5
  * Test password strength.
6
- * Ref https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
6
+ * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
7
7
  */
8
8
  export class PasswordValidator {
9
+ /**
10
+ * Runtime name for the class.
11
+ */
12
+ static CLASS_NAME = "PasswordValidator";
13
+ /**
14
+ * The minimum password length, 15 to match owasp rules.
15
+ * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
16
+ * @internal
17
+ */
18
+ static _DEFAULT_MIN_PASSWORD_LENGTH = 15;
9
19
  /**
10
20
  * Test the strength of the password.
11
21
  * @param property The name of the property.
12
22
  * @param password The password to test.
13
23
  * @param failures The list of failures to add to.
14
24
  * @param options Options to configure the testing.
15
- * @param options.minLength The minimum length of the password, defaults to 8.
25
+ * @param options.minLength The minimum length of the password, defaults to 15, can be 8 if MFA is enabled.
16
26
  * @param options.maxLength The minimum length of the password, defaults to 128.
17
27
  * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.
18
28
  */
19
29
  static validate(property, password, failures, options) {
20
30
  const isString = Validation.stringValue(property, password, failures);
21
31
  if (isString) {
22
- const minLength = options?.minLength ?? 8;
32
+ const minLength = options?.minLength ?? PasswordValidator._DEFAULT_MIN_PASSWORD_LENGTH;
23
33
  if (password.length < minLength) {
24
34
  failures.push({
25
35
  property,
@@ -77,5 +87,59 @@ export class PasswordValidator {
77
87
  }
78
88
  }
79
89
  }
90
+ /**
91
+ * Validate the password against security policy.
92
+ * @param password The password to validate.
93
+ * @param options Options to configure the testing.
94
+ * @param options.minLength The minimum length of the password, defaults to 15.
95
+ * @param options.maxLength The minimum length of the password, defaults to 128.
96
+ * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.
97
+ * @throws Error if the password does not meet the requirements.
98
+ */
99
+ static validatePassword(password, options) {
100
+ Guards.stringValue(PasswordValidator.CLASS_NAME, "password", password);
101
+ const failures = [];
102
+ PasswordValidator.validate("password", password, failures, options);
103
+ Validation.asValidationError(PasswordValidator.CLASS_NAME, "password", failures);
104
+ }
105
+ /**
106
+ * Compare two password byte arrays in constant time to prevent timing attacks.
107
+ * @param hashedPasswordBytes The computed password bytes to compare.
108
+ * @param storedPasswordBytes The stored password bytes to compare against.
109
+ * @returns True if the bytes match, false otherwise.
110
+ */
111
+ static comparePasswordBytes(hashedPasswordBytes, storedPasswordBytes) {
112
+ Guards.uint8Array(PasswordValidator.CLASS_NAME, "hashedPasswordBytes", hashedPasswordBytes);
113
+ Guards.uint8Array(PasswordValidator.CLASS_NAME, "storedPasswordBytes", storedPasswordBytes);
114
+ // Return immediately if lengths differ
115
+ if (hashedPasswordBytes.length !== storedPasswordBytes.length) {
116
+ return false;
117
+ }
118
+ // Compare bytes in constant time
119
+ let result = 0;
120
+ for (let i = 0; i < hashedPasswordBytes.length; i++) {
121
+ // eslint-disable-next-line no-bitwise
122
+ result |= hashedPasswordBytes[i] ^ storedPasswordBytes[i];
123
+ }
124
+ return result === 0;
125
+ }
126
+ /**
127
+ * Compare two hashed passwords in constant time to prevent timing attacks.
128
+ * @param hashedPassword The computed hash to compare.
129
+ * @param storedPassword The stored hash to compare against.
130
+ * @returns True if the hashes match, false otherwise.
131
+ */
132
+ static comparePasswordHashes(hashedPassword, storedPassword) {
133
+ Guards.stringValue(PasswordValidator.CLASS_NAME, "hashedPassword", hashedPassword);
134
+ Guards.stringValue(PasswordValidator.CLASS_NAME, "storedPassword", storedPassword);
135
+ // Return immediately if lengths differ
136
+ if (hashedPassword.length !== storedPassword.length) {
137
+ return false;
138
+ }
139
+ // Decode base64 strings to bytes
140
+ const hashedBytes = Converter.base64ToBytes(hashedPassword);
141
+ const storedBytes = Converter.base64ToBytes(storedPassword);
142
+ return PasswordValidator.comparePasswordBytes(hashedBytes, storedBytes);
143
+ }
80
144
  }
81
145
  //# sourceMappingURL=passwordValidator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"passwordValidator.js","sourceRoot":"","sources":["../../../src/passwords/passwordValidator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAA2B,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAErE;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAC7B;;;;;;;;;OASG;IACI,MAAM,CAAC,QAAQ,CACrB,QAAgB,EAChB,QAAgB,EAChB,QAA8B,EAC9B,OAIC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACd,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC;YAC1C,IAAI,QAAQ,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACb,QAAQ;oBACR,MAAM,EAAE,8BAA8B;oBACtC,UAAU,EAAE;wBACX,SAAS;wBACT,YAAY,EAAE,QAAQ,CAAC,MAAM;qBAC7B;iBACD,CAAC,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,GAAG,CAAC;YAC5C,IAAI,QAAQ,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACb,QAAQ;oBACR,MAAM,EAAE,8BAA8B;oBACtC,UAAU,EAAE;wBACX,SAAS;wBACT,YAAY,EAAE,QAAQ,CAAC,MAAM;qBAC7B;iBACD,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC;oBACb,QAAQ;oBACR,MAAM,EAAE,+BAA+B;iBACvC,CAAC,CAAC;YACJ,CAAC;YAED,0DAA0D;YAC1D,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,EAAE,CAAC;YAEvD,IAAI,QAAQ,CAAC,MAAM,GAAG,eAAe,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC;wBACb,QAAQ;wBACR,MAAM,EAAE,gCAAgC;qBACxC,CAAC,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC;wBACb,QAAQ;wBACR,MAAM,EAAE,gCAAgC;qBACxC,CAAC,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC1B,QAAQ,CAAC,IAAI,CAAC;wBACb,QAAQ;wBACR,MAAM,EAAE,6BAA6B;qBACrC,CAAC,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACnC,QAAQ,CAAC,IAAI,CAAC;wBACb,QAAQ;wBACR,MAAM,EAAE,kCAAkC;qBAC1C,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { type IValidationFailure, Validation } from \"@twin.org/core\";\n\n/**\n * Test password strength.\n * Ref https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .\n */\nexport class PasswordValidator {\n\t/**\n\t * Test the strength of the password.\n\t * @param property The name of the property.\n\t * @param password The password to test.\n\t * @param failures The list of failures to add to.\n\t * @param options Options to configure the testing.\n\t * @param options.minLength The minimum length of the password, defaults to 8.\n\t * @param options.maxLength The minimum length of the password, defaults to 128.\n\t * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.\n\t */\n\tpublic static validate(\n\t\tproperty: string,\n\t\tpassword: string,\n\t\tfailures: IValidationFailure[],\n\t\toptions?: {\n\t\t\tminLength?: number;\n\t\t\tmaxLength?: number;\n\t\t\tminPhraseLength?: number;\n\t\t}\n\t): void {\n\t\tconst isString = Validation.stringValue(property, password, failures);\n\n\t\tif (isString) {\n\t\t\tconst minLength = options?.minLength ?? 8;\n\t\t\tif (password.length < minLength) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.minLengthRequired\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tminLength,\n\t\t\t\t\t\tactualLength: password.length\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst maxLength = options?.maxLength ?? 128;\n\t\t\tif (password.length > maxLength) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.maxLengthRequired\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tmaxLength,\n\t\t\t\t\t\tactualLength: password.length\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (/(.)\\1{2,}/.test(password)) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.repeatedCharacters\"\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// If this looks like a phrase then apply additional rules\n\t\t\tconst minPhraseLength = options?.minPhraseLength ?? 20;\n\n\t\t\tif (password.length < minPhraseLength || !password.includes(\" \")) {\n\t\t\t\tif (!/[a-z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneLowerCase\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/[A-Z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneUpperCase\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/\\d/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneNumber\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/[^\\dA-Za-z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneSpecialChar\"\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"passwordValidator.js","sourceRoot":"","sources":["../../../src/passwords/passwordValidator.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, Guards, type IValidationFailure, Validation } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Test password strength.\n * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .\n */\nexport class PasswordValidator {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<PasswordValidator>();\n\n\t/**\n\t * The minimum password length, 15 to match owasp rules.\n\t * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_MIN_PASSWORD_LENGTH: number = 15;\n\n\t/**\n\t * Test the strength of the password.\n\t * @param property The name of the property.\n\t * @param password The password to test.\n\t * @param failures The list of failures to add to.\n\t * @param options Options to configure the testing.\n\t * @param options.minLength The minimum length of the password, defaults to 15, can be 8 if MFA is enabled.\n\t * @param options.maxLength The minimum length of the password, defaults to 128.\n\t * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.\n\t */\n\tpublic static validate(\n\t\tproperty: string,\n\t\tpassword: string,\n\t\tfailures: IValidationFailure[],\n\t\toptions?: {\n\t\t\tminLength?: number;\n\t\t\tmaxLength?: number;\n\t\t\tminPhraseLength?: number;\n\t\t}\n\t): void {\n\t\tconst isString = Validation.stringValue(property, password, failures);\n\n\t\tif (isString) {\n\t\t\tconst minLength = options?.minLength ?? PasswordValidator._DEFAULT_MIN_PASSWORD_LENGTH;\n\t\t\tif (password.length < minLength) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.minLengthRequired\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tminLength,\n\t\t\t\t\t\tactualLength: password.length\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst maxLength = options?.maxLength ?? 128;\n\t\t\tif (password.length > maxLength) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.maxLengthRequired\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tmaxLength,\n\t\t\t\t\t\tactualLength: password.length\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (/(.)\\1{2,}/.test(password)) {\n\t\t\t\tfailures.push({\n\t\t\t\t\tproperty,\n\t\t\t\t\treason: \"validation.repeatedCharacters\"\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// If this looks like a phrase then apply additional rules\n\t\t\tconst minPhraseLength = options?.minPhraseLength ?? 20;\n\n\t\t\tif (password.length < minPhraseLength || !password.includes(\" \")) {\n\t\t\t\tif (!/[a-z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneLowerCase\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/[A-Z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneUpperCase\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/\\d/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneNumber\"\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (!/[^\\dA-Za-z]/.test(password)) {\n\t\t\t\t\tfailures.push({\n\t\t\t\t\t\tproperty,\n\t\t\t\t\t\treason: \"validation.atLeastOneSpecialChar\"\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate the password against security policy.\n\t * @param password The password to validate.\n\t * @param options Options to configure the testing.\n\t * @param options.minLength The minimum length of the password, defaults to 15.\n\t * @param options.maxLength The minimum length of the password, defaults to 128.\n\t * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.\n\t * @throws Error if the password does not meet the requirements.\n\t */\n\tpublic static validatePassword(\n\t\tpassword: string,\n\t\toptions?: {\n\t\t\tminLength?: number;\n\t\t\tmaxLength?: number;\n\t\t\tminPhraseLength?: number;\n\t\t}\n\t): void {\n\t\tGuards.stringValue(PasswordValidator.CLASS_NAME, nameof(password), password);\n\n\t\tconst failures: IValidationFailure[] = [];\n\n\t\tPasswordValidator.validate(nameof(password), password, failures, options);\n\n\t\tValidation.asValidationError(PasswordValidator.CLASS_NAME, nameof(password), failures);\n\t}\n\n\t/**\n\t * Compare two password byte arrays in constant time to prevent timing attacks.\n\t * @param hashedPasswordBytes The computed password bytes to compare.\n\t * @param storedPasswordBytes The stored password bytes to compare against.\n\t * @returns True if the bytes match, false otherwise.\n\t */\n\tpublic static comparePasswordBytes(\n\t\thashedPasswordBytes: Uint8Array,\n\t\tstoredPasswordBytes: Uint8Array\n\t): boolean {\n\t\tGuards.uint8Array(\n\t\t\tPasswordValidator.CLASS_NAME,\n\t\t\tnameof(hashedPasswordBytes),\n\t\t\thashedPasswordBytes\n\t\t);\n\t\tGuards.uint8Array(\n\t\t\tPasswordValidator.CLASS_NAME,\n\t\t\tnameof(storedPasswordBytes),\n\t\t\tstoredPasswordBytes\n\t\t);\n\n\t\t// Return immediately if lengths differ\n\t\tif (hashedPasswordBytes.length !== storedPasswordBytes.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Compare bytes in constant time\n\t\tlet result = 0;\n\t\tfor (let i = 0; i < hashedPasswordBytes.length; i++) {\n\t\t\t// eslint-disable-next-line no-bitwise\n\t\t\tresult |= hashedPasswordBytes[i] ^ storedPasswordBytes[i];\n\t\t}\n\n\t\treturn result === 0;\n\t}\n\n\t/**\n\t * Compare two hashed passwords in constant time to prevent timing attacks.\n\t * @param hashedPassword The computed hash to compare.\n\t * @param storedPassword The stored hash to compare against.\n\t * @returns True if the hashes match, false otherwise.\n\t */\n\tpublic static comparePasswordHashes(hashedPassword: string, storedPassword: string): boolean {\n\t\tGuards.stringValue(PasswordValidator.CLASS_NAME, nameof(hashedPassword), hashedPassword);\n\t\tGuards.stringValue(PasswordValidator.CLASS_NAME, nameof(storedPassword), storedPassword);\n\n\t\t// Return immediately if lengths differ\n\t\tif (hashedPassword.length !== storedPassword.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Decode base64 strings to bytes\n\t\tconst hashedBytes = Converter.base64ToBytes(hashedPassword);\n\t\tconst storedBytes = Converter.base64ToBytes(storedPassword);\n\n\t\treturn PasswordValidator.comparePasswordBytes(hashedBytes, storedBytes);\n\t}\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { IntegrityAlgorithm } from "../models/integrityAlgorithm.js";
2
+ /**
3
+ * Helper class for creating integrity signatures.
4
+ * @see https://www.w3.org/TR/SRI/
5
+ */
6
+ export declare class IntegrityHelper {
7
+ /**
8
+ * Runtime name for the class.
9
+ */
10
+ static readonly CLASS_NAME: string;
11
+ /**
12
+ * Generate an integrity signature for the given content using the specified hash algorithm.
13
+ * @param type The hash algorithm to use, either "sha256", "sha384" or "sha512".
14
+ * @param content The content to hash as a Uint8Array.
15
+ * @returns The integrity signature in the format "type-base64hash".
16
+ */
17
+ static generate(type: IntegrityAlgorithm, content: Uint8Array): string;
18
+ /**
19
+ * Verify an integrity signature for the given content.
20
+ * @param integrity The integrity signature in the format "type-base64hash".
21
+ * @param content The content to hash as a Uint8Array.
22
+ * @returns True if the integrity signature matches the content.
23
+ * @throws If the integrity signature is invalid.
24
+ */
25
+ static verify(integrity: string, content: Uint8Array): boolean;
26
+ }
@@ -15,10 +15,12 @@ export * from "./hashes/sha1.js";
15
15
  export * from "./hashes/sha256.js";
16
16
  export * from "./hashes/sha3.js";
17
17
  export * from "./hashes/sha512.js";
18
+ export * from "./helpers/integrityHelper.js";
18
19
  export * from "./helpers/pemHelper.js";
19
20
  export * from "./keys/bip32Path.js";
20
21
  export * from "./keys/bip39.js";
21
22
  export * from "./keys/slip0010.js";
23
+ export * from "./models/integrityAlgorithm.js";
22
24
  export * from "./models/keyType.js";
23
25
  export * from "./otp/hotp.js";
24
26
  export * from "./otp/totp.js";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The names of the integrity algorithms.
3
+ */
4
+ export declare const IntegrityAlgorithm: {
5
+ /**
6
+ * Sha256.
7
+ */
8
+ readonly Sha256: "sha256";
9
+ /**
10
+ * Sha384.
11
+ */
12
+ readonly Sha384: "sha384";
13
+ /**
14
+ * Sha512.
15
+ */
16
+ readonly Sha512: "sha512";
17
+ };
18
+ /**
19
+ * Integrity algorithms.
20
+ */
21
+ export type IntegrityAlgorithm = (typeof IntegrityAlgorithm)[keyof typeof IntegrityAlgorithm];
@@ -2,10 +2,21 @@
2
2
  * Generate random passwords.
3
3
  */
4
4
  export declare class PasswordGenerator {
5
+ /**
6
+ * Runtime name for the class.
7
+ */
8
+ static readonly CLASS_NAME: string;
5
9
  /**
6
10
  * Generate a password of given length.
7
- * @param length The length of the password to generate.
11
+ * @param length The length of the password to generate, default to 15.
8
12
  * @returns The random password.
9
13
  */
10
- static generate(length: number): string;
14
+ static generate(length?: number): string;
15
+ /**
16
+ * Hash the password for the user.
17
+ * @param passwordBytes The password bytes.
18
+ * @param saltBytes The salt bytes.
19
+ * @returns The hashed password.
20
+ */
21
+ static hashPassword(passwordBytes: Uint8Array, saltBytes: Uint8Array): Promise<string>;
11
22
  }
@@ -1,16 +1,20 @@
1
1
  import { type IValidationFailure } from "@twin.org/core";
2
2
  /**
3
3
  * Test password strength.
4
- * Ref https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
4
+ * @see https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
5
5
  */
6
6
  export declare class PasswordValidator {
7
+ /**
8
+ * Runtime name for the class.
9
+ */
10
+ static readonly CLASS_NAME: string;
7
11
  /**
8
12
  * Test the strength of the password.
9
13
  * @param property The name of the property.
10
14
  * @param password The password to test.
11
15
  * @param failures The list of failures to add to.
12
16
  * @param options Options to configure the testing.
13
- * @param options.minLength The minimum length of the password, defaults to 8.
17
+ * @param options.minLength The minimum length of the password, defaults to 15, can be 8 if MFA is enabled.
14
18
  * @param options.maxLength The minimum length of the password, defaults to 128.
15
19
  * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.
16
20
  */
@@ -19,4 +23,32 @@ export declare class PasswordValidator {
19
23
  maxLength?: number;
20
24
  minPhraseLength?: number;
21
25
  }): void;
26
+ /**
27
+ * Validate the password against security policy.
28
+ * @param password The password to validate.
29
+ * @param options Options to configure the testing.
30
+ * @param options.minLength The minimum length of the password, defaults to 15.
31
+ * @param options.maxLength The minimum length of the password, defaults to 128.
32
+ * @param options.minPhraseLength The minimum length of the password for it to be considered a pass phrase.
33
+ * @throws Error if the password does not meet the requirements.
34
+ */
35
+ static validatePassword(password: string, options?: {
36
+ minLength?: number;
37
+ maxLength?: number;
38
+ minPhraseLength?: number;
39
+ }): void;
40
+ /**
41
+ * Compare two password byte arrays in constant time to prevent timing attacks.
42
+ * @param hashedPasswordBytes The computed password bytes to compare.
43
+ * @param storedPasswordBytes The stored password bytes to compare against.
44
+ * @returns True if the bytes match, false otherwise.
45
+ */
46
+ static comparePasswordBytes(hashedPasswordBytes: Uint8Array, storedPasswordBytes: Uint8Array): boolean;
47
+ /**
48
+ * Compare two hashed passwords in constant time to prevent timing attacks.
49
+ * @param hashedPassword The computed hash to compare.
50
+ * @param storedPassword The stored hash to compare against.
51
+ * @returns True if the hashes match, false otherwise.
52
+ */
53
+ static comparePasswordHashes(hashedPassword: string, storedPassword: string): boolean;
22
54
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,404 @@
1
1
  # @twin.org/crypto - Changelog
2
2
 
3
+ ## [0.0.3-next.21](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.20...crypto-v0.0.3-next.21) (2026-02-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * add context id features ([#206](https://github.com/twinfoundation/framework/issues/206)) ([ef0d4ee](https://github.com/twinfoundation/framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
9
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
10
+ * add mnemonic validation ([4b43491](https://github.com/twinfoundation/framework/commit/4b43491cf04bb626c27faea66e5c74b3971b111d))
11
+ * add rsa cipher support ([7af6cc6](https://github.com/twinfoundation/framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
12
+ * add RSA support for public key components ([7126259](https://github.com/twinfoundation/framework/commit/7126259103b758c291e52a8a03818eb822d1aad1))
13
+ * additional RSA methods and async ([1fceee2](https://github.com/twinfoundation/framework/commit/1fceee2d1248a24a7620846025fcf906495c07f4))
14
+ * change method accessibility ([c1b77fc](https://github.com/twinfoundation/framework/commit/c1b77fcfb61c092a01c97aebb2fe2dc2bbaa221b))
15
+ * eslint migration to flat config ([74427d7](https://github.com/twinfoundation/framework/commit/74427d78d342167f7850e49ab87269326355befe))
16
+ * factory create and integrity ([#235](https://github.com/twinfoundation/framework/issues/235)) ([9f98b99](https://github.com/twinfoundation/framework/commit/9f98b99daf46eb365346fae49cc4ffba63e74cb3))
17
+ * improved password generation and validation ([#232](https://github.com/twinfoundation/framework/issues/232)) ([ca4e18f](https://github.com/twinfoundation/framework/commit/ca4e18f388b1882cdfb774fc0d0921b8530fac33))
18
+ * locales validation ([#197](https://github.com/twinfoundation/framework/issues/197)) ([55fdadb](https://github.com/twinfoundation/framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
19
+ * relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
20
+ * update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
21
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/twinfoundation/framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
22
+ * use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * docs ([9df46e0](https://github.com/twinfoundation/framework/commit/9df46e0a3940a4d1f479373f58830519262f9590))
28
+ * docs ([67c8887](https://github.com/twinfoundation/framework/commit/67c888739448e753106ea067a8703d058e0ddf12))
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * The following workspace dependencies were updated
34
+ * dependencies
35
+ * @twin.org/core bumped from 0.0.3-next.20 to 0.0.3-next.21
36
+ * @twin.org/nameof bumped from 0.0.3-next.20 to 0.0.3-next.21
37
+ * devDependencies
38
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.20 to 0.0.3-next.21
39
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.20 to 0.0.3-next.21
40
+ * @twin.org/validate-locales bumped from 0.0.3-next.20 to 0.0.3-next.21
41
+
42
+ ## [0.0.3-next.20](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.19...crypto-v0.0.3-next.20) (2026-02-26)
43
+
44
+
45
+ ### Miscellaneous Chores
46
+
47
+ * **crypto:** Synchronize repo versions
48
+
49
+
50
+ ### Dependencies
51
+
52
+ * The following workspace dependencies were updated
53
+ * dependencies
54
+ * @twin.org/core bumped from 0.0.3-next.19 to 0.0.3-next.20
55
+ * @twin.org/nameof bumped from 0.0.3-next.19 to 0.0.3-next.20
56
+ * devDependencies
57
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.19 to 0.0.3-next.20
58
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.19 to 0.0.3-next.20
59
+ * @twin.org/validate-locales bumped from 0.0.3-next.19 to 0.0.3-next.20
60
+
61
+ ## [0.0.3-next.19](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.18...crypto-v0.0.3-next.19) (2026-02-26)
62
+
63
+
64
+ ### Miscellaneous Chores
65
+
66
+ * **crypto:** Synchronize repo versions
67
+
68
+
69
+ ### Dependencies
70
+
71
+ * The following workspace dependencies were updated
72
+ * dependencies
73
+ * @twin.org/core bumped from 0.0.3-next.18 to 0.0.3-next.19
74
+ * @twin.org/nameof bumped from 0.0.3-next.18 to 0.0.3-next.19
75
+ * devDependencies
76
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.18 to 0.0.3-next.19
77
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.18 to 0.0.3-next.19
78
+ * @twin.org/validate-locales bumped from 0.0.3-next.18 to 0.0.3-next.19
79
+
80
+ ## [0.0.3-next.18](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.17...crypto-v0.0.3-next.18) (2026-02-23)
81
+
82
+
83
+ ### Miscellaneous Chores
84
+
85
+ * **crypto:** Synchronize repo versions
86
+
87
+
88
+ ### Dependencies
89
+
90
+ * The following workspace dependencies were updated
91
+ * dependencies
92
+ * @twin.org/core bumped from 0.0.3-next.17 to 0.0.3-next.18
93
+ * @twin.org/nameof bumped from 0.0.3-next.17 to 0.0.3-next.18
94
+ * devDependencies
95
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.17 to 0.0.3-next.18
96
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.17 to 0.0.3-next.18
97
+ * @twin.org/validate-locales bumped from 0.0.3-next.17 to 0.0.3-next.18
98
+
99
+ ## [0.0.3-next.17](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.16...crypto-v0.0.3-next.17) (2026-02-09)
100
+
101
+
102
+ ### Features
103
+
104
+ * factory create and integrity ([#235](https://github.com/twinfoundation/framework/issues/235)) ([9f98b99](https://github.com/twinfoundation/framework/commit/9f98b99daf46eb365346fae49cc4ffba63e74cb3))
105
+
106
+
107
+ ### Bug Fixes
108
+
109
+ * docs ([9df46e0](https://github.com/twinfoundation/framework/commit/9df46e0a3940a4d1f479373f58830519262f9590))
110
+ * docs ([67c8887](https://github.com/twinfoundation/framework/commit/67c888739448e753106ea067a8703d058e0ddf12))
111
+
112
+
113
+ ### Dependencies
114
+
115
+ * The following workspace dependencies were updated
116
+ * dependencies
117
+ * @twin.org/core bumped from 0.0.3-next.16 to 0.0.3-next.17
118
+ * @twin.org/nameof bumped from 0.0.3-next.16 to 0.0.3-next.17
119
+ * devDependencies
120
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.16 to 0.0.3-next.17
121
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.16 to 0.0.3-next.17
122
+ * @twin.org/validate-locales bumped from 0.0.3-next.16 to 0.0.3-next.17
123
+
124
+ ## [0.0.3-next.16](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.15...crypto-v0.0.3-next.16) (2026-02-06)
125
+
126
+
127
+ ### Features
128
+
129
+ * improved password generation and validation ([#232](https://github.com/twinfoundation/framework/issues/232)) ([ca4e18f](https://github.com/twinfoundation/framework/commit/ca4e18f388b1882cdfb774fc0d0921b8530fac33))
130
+
131
+
132
+ ### Dependencies
133
+
134
+ * The following workspace dependencies were updated
135
+ * dependencies
136
+ * @twin.org/core bumped from 0.0.3-next.15 to 0.0.3-next.16
137
+ * @twin.org/nameof bumped from 0.0.3-next.15 to 0.0.3-next.16
138
+ * devDependencies
139
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.15 to 0.0.3-next.16
140
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.15 to 0.0.3-next.16
141
+ * @twin.org/validate-locales bumped from 0.0.3-next.15 to 0.0.3-next.16
142
+
143
+ ## [0.0.3-next.15](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.14...crypto-v0.0.3-next.15) (2026-01-29)
144
+
145
+
146
+ ### Miscellaneous Chores
147
+
148
+ * **crypto:** Synchronize repo versions
149
+
150
+
151
+ ### Dependencies
152
+
153
+ * The following workspace dependencies were updated
154
+ * dependencies
155
+ * @twin.org/core bumped from 0.0.3-next.14 to 0.0.3-next.15
156
+ * @twin.org/nameof bumped from 0.0.3-next.14 to 0.0.3-next.15
157
+ * devDependencies
158
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.14 to 0.0.3-next.15
159
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.14 to 0.0.3-next.15
160
+ * @twin.org/validate-locales bumped from 0.0.3-next.14 to 0.0.3-next.15
161
+
162
+ ## [0.0.3-next.14](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.13...crypto-v0.0.3-next.14) (2026-01-22)
163
+
164
+
165
+ ### Miscellaneous Chores
166
+
167
+ * **crypto:** Synchronize repo versions
168
+
169
+
170
+ ### Dependencies
171
+
172
+ * The following workspace dependencies were updated
173
+ * dependencies
174
+ * @twin.org/core bumped from 0.0.3-next.13 to 0.0.3-next.14
175
+ * @twin.org/nameof bumped from 0.0.3-next.13 to 0.0.3-next.14
176
+ * devDependencies
177
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.13 to 0.0.3-next.14
178
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.13 to 0.0.3-next.14
179
+ * @twin.org/validate-locales bumped from 0.0.3-next.13 to 0.0.3-next.14
180
+
181
+ ## [0.0.3-next.13](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.12...crypto-v0.0.3-next.13) (2026-01-08)
182
+
183
+
184
+ ### Miscellaneous Chores
185
+
186
+ * **crypto:** Synchronize repo versions
187
+
188
+
189
+ ### Dependencies
190
+
191
+ * The following workspace dependencies were updated
192
+ * dependencies
193
+ * @twin.org/core bumped from 0.0.3-next.12 to 0.0.3-next.13
194
+ * @twin.org/nameof bumped from 0.0.3-next.12 to 0.0.3-next.13
195
+ * devDependencies
196
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.12 to 0.0.3-next.13
197
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.12 to 0.0.3-next.13
198
+ * @twin.org/validate-locales bumped from 0.0.3-next.12 to 0.0.3-next.13
199
+
200
+ ## [0.0.3-next.12](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.11...crypto-v0.0.3-next.12) (2026-01-08)
201
+
202
+
203
+ ### Miscellaneous Chores
204
+
205
+ * **crypto:** Synchronize repo versions
206
+
207
+
208
+ ### Dependencies
209
+
210
+ * The following workspace dependencies were updated
211
+ * dependencies
212
+ * @twin.org/core bumped from 0.0.3-next.11 to 0.0.3-next.12
213
+ * @twin.org/nameof bumped from 0.0.3-next.11 to 0.0.3-next.12
214
+ * devDependencies
215
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.11 to 0.0.3-next.12
216
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.11 to 0.0.3-next.12
217
+ * @twin.org/validate-locales bumped from 0.0.3-next.11 to 0.0.3-next.12
218
+
219
+ ## [0.0.3-next.11](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.10...crypto-v0.0.3-next.11) (2026-01-07)
220
+
221
+
222
+ ### Miscellaneous Chores
223
+
224
+ * **crypto:** Synchronize repo versions
225
+
226
+
227
+ ### Dependencies
228
+
229
+ * The following workspace dependencies were updated
230
+ * dependencies
231
+ * @twin.org/core bumped from 0.0.3-next.10 to 0.0.3-next.11
232
+ * @twin.org/nameof bumped from 0.0.3-next.10 to 0.0.3-next.11
233
+ * devDependencies
234
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.10 to 0.0.3-next.11
235
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.10 to 0.0.3-next.11
236
+ * @twin.org/validate-locales bumped from 0.0.3-next.10 to 0.0.3-next.11
237
+
238
+ ## [0.0.3-next.10](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.9...crypto-v0.0.3-next.10) (2026-01-07)
239
+
240
+
241
+ ### Miscellaneous Chores
242
+
243
+ * **crypto:** Synchronize repo versions
244
+
245
+
246
+ ### Dependencies
247
+
248
+ * The following workspace dependencies were updated
249
+ * dependencies
250
+ * @twin.org/core bumped from 0.0.3-next.9 to 0.0.3-next.10
251
+ * @twin.org/nameof bumped from 0.0.3-next.9 to 0.0.3-next.10
252
+ * devDependencies
253
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.9 to 0.0.3-next.10
254
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.9 to 0.0.3-next.10
255
+ * @twin.org/validate-locales bumped from 0.0.3-next.9 to 0.0.3-next.10
256
+
257
+ ## [0.0.3-next.9](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.8...crypto-v0.0.3-next.9) (2026-01-05)
258
+
259
+
260
+ ### Miscellaneous Chores
261
+
262
+ * **crypto:** Synchronize repo versions
263
+
264
+
265
+ ### Dependencies
266
+
267
+ * The following workspace dependencies were updated
268
+ * dependencies
269
+ * @twin.org/core bumped from 0.0.3-next.8 to 0.0.3-next.9
270
+ * @twin.org/nameof bumped from 0.0.3-next.8 to 0.0.3-next.9
271
+ * devDependencies
272
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.8 to 0.0.3-next.9
273
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.8 to 0.0.3-next.9
274
+ * @twin.org/validate-locales bumped from 0.0.3-next.8 to 0.0.3-next.9
275
+
276
+ ## [0.0.3-next.8](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.7...crypto-v0.0.3-next.8) (2025-11-26)
277
+
278
+
279
+ ### Miscellaneous Chores
280
+
281
+ * **crypto:** Synchronize repo versions
282
+
283
+
284
+ ### Dependencies
285
+
286
+ * The following workspace dependencies were updated
287
+ * dependencies
288
+ * @twin.org/core bumped from 0.0.3-next.7 to 0.0.3-next.8
289
+ * @twin.org/nameof bumped from 0.0.3-next.7 to 0.0.3-next.8
290
+ * devDependencies
291
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.7 to 0.0.3-next.8
292
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.7 to 0.0.3-next.8
293
+ * @twin.org/validate-locales bumped from 0.0.3-next.7 to 0.0.3-next.8
294
+
295
+ ## [0.0.3-next.7](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.6...crypto-v0.0.3-next.7) (2025-11-25)
296
+
297
+
298
+ ### Features
299
+
300
+ * add context id features ([#206](https://github.com/twinfoundation/framework/issues/206)) ([ef0d4ee](https://github.com/twinfoundation/framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
301
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
302
+ * add mnemonic validation ([4b43491](https://github.com/twinfoundation/framework/commit/4b43491cf04bb626c27faea66e5c74b3971b111d))
303
+ * add rsa cipher support ([7af6cc6](https://github.com/twinfoundation/framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
304
+ * add RSA support for public key components ([7126259](https://github.com/twinfoundation/framework/commit/7126259103b758c291e52a8a03818eb822d1aad1))
305
+ * additional RSA methods and async ([1fceee2](https://github.com/twinfoundation/framework/commit/1fceee2d1248a24a7620846025fcf906495c07f4))
306
+ * change method accessibility ([c1b77fc](https://github.com/twinfoundation/framework/commit/c1b77fcfb61c092a01c97aebb2fe2dc2bbaa221b))
307
+ * eslint migration to flat config ([74427d7](https://github.com/twinfoundation/framework/commit/74427d78d342167f7850e49ab87269326355befe))
308
+ * locales validation ([#197](https://github.com/twinfoundation/framework/issues/197)) ([55fdadb](https://github.com/twinfoundation/framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
309
+ * relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
310
+ * update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
311
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/twinfoundation/framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
312
+ * use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
313
+
314
+
315
+ ### Dependencies
316
+
317
+ * The following workspace dependencies were updated
318
+ * dependencies
319
+ * @twin.org/core bumped from 0.0.3-next.6 to 0.0.3-next.7
320
+ * @twin.org/nameof bumped from 0.0.3-next.6 to 0.0.3-next.7
321
+ * devDependencies
322
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.6 to 0.0.3-next.7
323
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.6 to 0.0.3-next.7
324
+ * @twin.org/validate-locales bumped from 0.0.3-next.6 to 0.0.3-next.7
325
+
326
+ ## [0.0.3-next.6](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.5...crypto-v0.0.3-next.6) (2025-11-25)
327
+
328
+
329
+ ### Miscellaneous Chores
330
+
331
+ * **crypto:** Synchronize repo versions
332
+
333
+
334
+ ### Dependencies
335
+
336
+ * The following workspace dependencies were updated
337
+ * dependencies
338
+ * @twin.org/core bumped from 0.0.3-next.5 to 0.0.3-next.6
339
+ * @twin.org/nameof bumped from 0.0.3-next.5 to 0.0.3-next.6
340
+ * devDependencies
341
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.5 to 0.0.3-next.6
342
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.5 to 0.0.3-next.6
343
+ * @twin.org/validate-locales bumped from 0.0.3-next.5 to 0.0.3-next.6
344
+
345
+ ## [0.0.3-next.5](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.4...crypto-v0.0.3-next.5) (2025-11-20)
346
+
347
+
348
+ ### Miscellaneous Chores
349
+
350
+ * **crypto:** Synchronize repo versions
351
+
352
+
353
+ ### Dependencies
354
+
355
+ * The following workspace dependencies were updated
356
+ * dependencies
357
+ * @twin.org/core bumped from 0.0.3-next.4 to 0.0.3-next.5
358
+ * @twin.org/nameof bumped from 0.0.3-next.4 to 0.0.3-next.5
359
+ * devDependencies
360
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.4 to 0.0.3-next.5
361
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.4 to 0.0.3-next.5
362
+ * @twin.org/validate-locales bumped from 0.0.3-next.4 to 0.0.3-next.5
363
+
364
+ ## [0.0.3-next.4](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.3...crypto-v0.0.3-next.4) (2025-11-13)
365
+
366
+
367
+ ### Miscellaneous Chores
368
+
369
+ * **crypto:** Synchronize repo versions
370
+
371
+
372
+ ### Dependencies
373
+
374
+ * The following workspace dependencies were updated
375
+ * dependencies
376
+ * @twin.org/core bumped from 0.0.3-next.3 to 0.0.3-next.4
377
+ * @twin.org/nameof bumped from 0.0.3-next.3 to 0.0.3-next.4
378
+ * devDependencies
379
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.3 to 0.0.3-next.4
380
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.3 to 0.0.3-next.4
381
+ * @twin.org/validate-locales bumped from 0.0.3-next.3 to 0.0.3-next.4
382
+
383
+ ## [0.0.3-next.3](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.2...crypto-v0.0.3-next.3) (2025-11-12)
384
+
385
+
386
+ ### Miscellaneous Chores
387
+
388
+ * **crypto:** Synchronize repo versions
389
+
390
+
391
+ ### Dependencies
392
+
393
+ * The following workspace dependencies were updated
394
+ * dependencies
395
+ * @twin.org/core bumped from 0.0.3-next.2 to 0.0.3-next.3
396
+ * @twin.org/nameof bumped from 0.0.3-next.2 to 0.0.3-next.3
397
+ * devDependencies
398
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.2 to 0.0.3-next.3
399
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.2 to 0.0.3-next.3
400
+ * @twin.org/validate-locales bumped from 0.0.3-next.2 to 0.0.3-next.3
401
+
3
402
  ## [0.0.3-next.2](https://github.com/twinfoundation/framework/compare/crypto-v0.0.3-next.1...crypto-v0.0.3-next.2) (2025-11-12)
4
403
 
5
404
 
@@ -0,0 +1,85 @@
1
+ # Class: IntegrityHelper
2
+
3
+ Helper class for creating integrity signatures.
4
+
5
+ ## See
6
+
7
+ https://www.w3.org/TR/SRI/
8
+
9
+ ## Constructors
10
+
11
+ ### Constructor
12
+
13
+ > **new IntegrityHelper**(): `IntegrityHelper`
14
+
15
+ #### Returns
16
+
17
+ `IntegrityHelper`
18
+
19
+ ## Properties
20
+
21
+ ### CLASS\_NAME
22
+
23
+ > `readonly` `static` **CLASS\_NAME**: `string`
24
+
25
+ Runtime name for the class.
26
+
27
+ ## Methods
28
+
29
+ ### generate()
30
+
31
+ > `static` **generate**(`type`, `content`): `string`
32
+
33
+ Generate an integrity signature for the given content using the specified hash algorithm.
34
+
35
+ #### Parameters
36
+
37
+ ##### type
38
+
39
+ [`IntegrityAlgorithm`](../type-aliases/IntegrityAlgorithm.md)
40
+
41
+ The hash algorithm to use, either "sha256", "sha384" or "sha512".
42
+
43
+ ##### content
44
+
45
+ `Uint8Array`
46
+
47
+ The content to hash as a Uint8Array.
48
+
49
+ #### Returns
50
+
51
+ `string`
52
+
53
+ The integrity signature in the format "type-base64hash".
54
+
55
+ ***
56
+
57
+ ### verify()
58
+
59
+ > `static` **verify**(`integrity`, `content`): `boolean`
60
+
61
+ Verify an integrity signature for the given content.
62
+
63
+ #### Parameters
64
+
65
+ ##### integrity
66
+
67
+ `string`
68
+
69
+ The integrity signature in the format "type-base64hash".
70
+
71
+ ##### content
72
+
73
+ `Uint8Array`
74
+
75
+ The content to hash as a Uint8Array.
76
+
77
+ #### Returns
78
+
79
+ `boolean`
80
+
81
+ True if the integrity signature matches the content.
82
+
83
+ #### Throws
84
+
85
+ If the integrity signature is invalid.
@@ -12,6 +12,14 @@ Generate random passwords.
12
12
 
13
13
  `PasswordGenerator`
14
14
 
15
+ ## Properties
16
+
17
+ ### CLASS\_NAME
18
+
19
+ > `readonly` `static` **CLASS\_NAME**: `string`
20
+
21
+ Runtime name for the class.
22
+
15
23
  ## Methods
16
24
 
17
25
  ### generate()
@@ -24,12 +32,40 @@ Generate a password of given length.
24
32
 
25
33
  ##### length
26
34
 
27
- `number`
35
+ `number` = `PasswordGenerator._DEFAULT_MIN_PASSWORD_LENGTH`
28
36
 
29
- The length of the password to generate.
37
+ The length of the password to generate, default to 15.
30
38
 
31
39
  #### Returns
32
40
 
33
41
  `string`
34
42
 
35
43
  The random password.
44
+
45
+ ***
46
+
47
+ ### hashPassword()
48
+
49
+ > `static` **hashPassword**(`passwordBytes`, `saltBytes`): `Promise`\<`string`\>
50
+
51
+ Hash the password for the user.
52
+
53
+ #### Parameters
54
+
55
+ ##### passwordBytes
56
+
57
+ `Uint8Array`
58
+
59
+ The password bytes.
60
+
61
+ ##### saltBytes
62
+
63
+ `Uint8Array`
64
+
65
+ The salt bytes.
66
+
67
+ #### Returns
68
+
69
+ `Promise`\<`string`\>
70
+
71
+ The hashed password.
@@ -1,7 +1,10 @@
1
1
  # Class: PasswordValidator
2
2
 
3
3
  Test password strength.
4
- Ref https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
4
+
5
+ ## See
6
+
7
+ https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls .
5
8
 
6
9
  ## Constructors
7
10
 
@@ -13,6 +16,14 @@ Ref https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_
13
16
 
14
17
  `PasswordValidator`
15
18
 
19
+ ## Properties
20
+
21
+ ### CLASS\_NAME
22
+
23
+ > `readonly` `static` **CLASS\_NAME**: `string`
24
+
25
+ Runtime name for the class.
26
+
16
27
  ## Methods
17
28
 
18
29
  ### validate()
@@ -49,7 +60,49 @@ Options to configure the testing.
49
60
 
50
61
  `number`
51
62
 
52
- The minimum length of the password, defaults to 8.
63
+ The minimum length of the password, defaults to 15, can be 8 if MFA is enabled.
64
+
65
+ ###### maxLength?
66
+
67
+ `number`
68
+
69
+ The minimum length of the password, defaults to 128.
70
+
71
+ ###### minPhraseLength?
72
+
73
+ `number`
74
+
75
+ The minimum length of the password for it to be considered a pass phrase.
76
+
77
+ #### Returns
78
+
79
+ `void`
80
+
81
+ ***
82
+
83
+ ### validatePassword()
84
+
85
+ > `static` **validatePassword**(`password`, `options?`): `void`
86
+
87
+ Validate the password against security policy.
88
+
89
+ #### Parameters
90
+
91
+ ##### password
92
+
93
+ `string`
94
+
95
+ The password to validate.
96
+
97
+ ##### options?
98
+
99
+ Options to configure the testing.
100
+
101
+ ###### minLength?
102
+
103
+ `number`
104
+
105
+ The minimum length of the password, defaults to 15.
53
106
 
54
107
  ###### maxLength?
55
108
 
@@ -66,3 +119,63 @@ The minimum length of the password for it to be considered a pass phrase.
66
119
  #### Returns
67
120
 
68
121
  `void`
122
+
123
+ #### Throws
124
+
125
+ Error if the password does not meet the requirements.
126
+
127
+ ***
128
+
129
+ ### comparePasswordBytes()
130
+
131
+ > `static` **comparePasswordBytes**(`hashedPasswordBytes`, `storedPasswordBytes`): `boolean`
132
+
133
+ Compare two password byte arrays in constant time to prevent timing attacks.
134
+
135
+ #### Parameters
136
+
137
+ ##### hashedPasswordBytes
138
+
139
+ `Uint8Array`
140
+
141
+ The computed password bytes to compare.
142
+
143
+ ##### storedPasswordBytes
144
+
145
+ `Uint8Array`
146
+
147
+ The stored password bytes to compare against.
148
+
149
+ #### Returns
150
+
151
+ `boolean`
152
+
153
+ True if the bytes match, false otherwise.
154
+
155
+ ***
156
+
157
+ ### comparePasswordHashes()
158
+
159
+ > `static` **comparePasswordHashes**(`hashedPassword`, `storedPassword`): `boolean`
160
+
161
+ Compare two hashed passwords in constant time to prevent timing attacks.
162
+
163
+ #### Parameters
164
+
165
+ ##### hashedPassword
166
+
167
+ `string`
168
+
169
+ The computed hash to compare.
170
+
171
+ ##### storedPassword
172
+
173
+ `string`
174
+
175
+ The stored hash to compare against.
176
+
177
+ #### Returns
178
+
179
+ `boolean`
180
+
181
+ True if the hashes match, false otherwise.
@@ -19,6 +19,7 @@
19
19
  - [Sha256](classes/Sha256.md)
20
20
  - [Sha3](classes/Sha3.md)
21
21
  - [Sha512](classes/Sha512.md)
22
+ - [IntegrityHelper](classes/IntegrityHelper.md)
22
23
  - [PemHelper](classes/PemHelper.md)
23
24
  - [Bip32Path](classes/Bip32Path.md)
24
25
  - [Bip39](classes/Bip39.md)
@@ -30,8 +31,10 @@
30
31
 
31
32
  ## Type Aliases
32
33
 
34
+ - [IntegrityAlgorithm](type-aliases/IntegrityAlgorithm.md)
33
35
  - [KeyType](type-aliases/KeyType.md)
34
36
 
35
37
  ## Variables
36
38
 
39
+ - [IntegrityAlgorithm](variables/IntegrityAlgorithm.md)
37
40
  - [KeyType](variables/KeyType.md)
@@ -0,0 +1,5 @@
1
+ # Type Alias: IntegrityAlgorithm
2
+
3
+ > **IntegrityAlgorithm** = *typeof* [`IntegrityAlgorithm`](../variables/IntegrityAlgorithm.md)\[keyof *typeof* [`IntegrityAlgorithm`](../variables/IntegrityAlgorithm.md)\]
4
+
5
+ Integrity algorithms.
@@ -0,0 +1,25 @@
1
+ # Variable: IntegrityAlgorithm
2
+
3
+ > `const` **IntegrityAlgorithm**: `object`
4
+
5
+ The names of the integrity algorithms.
6
+
7
+ ## Type Declaration
8
+
9
+ ### Sha256
10
+
11
+ > `readonly` **Sha256**: `"sha256"` = `"sha256"`
12
+
13
+ Sha256.
14
+
15
+ ### Sha384
16
+
17
+ > `readonly` **Sha384**: `"sha384"` = `"sha384"`
18
+
19
+ Sha384.
20
+
21
+ ### Sha512
22
+
23
+ > `readonly` **Sha512**: `"sha512"` = `"sha512"`
24
+
25
+ Sha512.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/crypto",
3
- "version": "0.0.3-next.2",
3
+ "version": "0.0.3-next.21",
4
4
  "description": "Contains helper methods and classes which implement cryptographic functions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,8 +20,8 @@
20
20
  "@scure/base": "2.0.0",
21
21
  "@scure/bip32": "2.0.1",
22
22
  "@scure/bip39": "2.0.1",
23
- "@twin.org/core": "0.0.3-next.2",
24
- "@twin.org/nameof": "0.0.3-next.2",
23
+ "@twin.org/core": "0.0.3-next.21",
24
+ "@twin.org/nameof": "0.0.3-next.21",
25
25
  "crypto-browserify": "3.12.1",
26
26
  "micro-key-producer": "0.8.2"
27
27
  },