@lakutata/security 3.0.0-beta.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.
Files changed (61) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +143 -0
  3. package/THIRD_PARTY_NOTICES.md +36 -0
  4. package/dist/cjs/exceptions/DecryptionFailedException.d.ts +13 -0
  5. package/dist/cjs/exceptions/DecryptionFailedException.js +20 -0
  6. package/dist/cjs/exceptions/InvalidSignatureException.d.ts +13 -0
  7. package/dist/cjs/exceptions/InvalidSignatureException.js +20 -0
  8. package/dist/cjs/exceptions/PasswordTooLongException.d.ts +12 -0
  9. package/dist/cjs/exceptions/PasswordTooLongException.js +19 -0
  10. package/dist/cjs/exports/Security.d.ts +19 -0
  11. package/dist/cjs/exports/Security.js +38 -0
  12. package/dist/cjs/functions/Encryption.d.ts +95 -0
  13. package/dist/cjs/functions/Encryption.js +208 -0
  14. package/dist/cjs/functions/GenerateSecret.d.ts +20 -0
  15. package/dist/cjs/functions/GenerateSecret.js +26 -0
  16. package/dist/cjs/functions/HMAC.d.ts +50 -0
  17. package/dist/cjs/functions/HMAC.js +58 -0
  18. package/dist/cjs/functions/TimingSafeEqual.d.ts +18 -0
  19. package/dist/cjs/functions/TimingSafeEqual.js +30 -0
  20. package/dist/cjs/lib/AssertKey.d.ts +5 -0
  21. package/dist/cjs/lib/AssertKey.js +11 -0
  22. package/dist/cjs/lib/bcrypt/constants.d.ts +13 -0
  23. package/dist/cjs/lib/bcrypt/constants.js +213 -0
  24. package/dist/cjs/lib/bcrypt/index.d.ts +84 -0
  25. package/dist/cjs/lib/bcrypt/index.js +658 -0
  26. package/dist/cjs/package.json +1 -0
  27. package/dist/cjs/providers/Crypt.d.ts +71 -0
  28. package/dist/cjs/providers/Crypt.js +88 -0
  29. package/dist/cjs/providers/Mac.d.ts +86 -0
  30. package/dist/cjs/providers/Mac.js +114 -0
  31. package/dist/cjs/providers/PasswordHash.d.ts +78 -0
  32. package/dist/cjs/providers/PasswordHash.js +146 -0
  33. package/dist/esm/exceptions/DecryptionFailedException.js +16 -0
  34. package/dist/esm/exceptions/InvalidSignatureException.js +16 -0
  35. package/dist/esm/exceptions/PasswordTooLongException.js +15 -0
  36. package/dist/esm/exports/Security.js +19 -0
  37. package/dist/esm/functions/Encryption.js +202 -0
  38. package/dist/esm/functions/GenerateSecret.js +23 -0
  39. package/dist/esm/functions/HMAC.js +54 -0
  40. package/dist/esm/functions/TimingSafeEqual.js +27 -0
  41. package/dist/esm/lib/AssertKey.js +8 -0
  42. package/dist/esm/lib/bcrypt/constants.js +202 -0
  43. package/dist/esm/lib/bcrypt/index.js +645 -0
  44. package/dist/esm/providers/Crypt.js +84 -0
  45. package/dist/esm/providers/Mac.js +110 -0
  46. package/dist/esm/providers/PasswordHash.js +109 -0
  47. package/dist/types/exceptions/DecryptionFailedException.d.ts +13 -0
  48. package/dist/types/exceptions/InvalidSignatureException.d.ts +13 -0
  49. package/dist/types/exceptions/PasswordTooLongException.d.ts +12 -0
  50. package/dist/types/exports/Security.d.ts +19 -0
  51. package/dist/types/functions/Encryption.d.ts +95 -0
  52. package/dist/types/functions/GenerateSecret.d.ts +20 -0
  53. package/dist/types/functions/HMAC.d.ts +50 -0
  54. package/dist/types/functions/TimingSafeEqual.d.ts +18 -0
  55. package/dist/types/lib/AssertKey.d.ts +5 -0
  56. package/dist/types/lib/bcrypt/constants.d.ts +13 -0
  57. package/dist/types/lib/bcrypt/index.d.ts +84 -0
  58. package/dist/types/providers/Crypt.d.ts +71 -0
  59. package/dist/types/providers/Mac.d.ts +86 -0
  60. package/dist/types/providers/PasswordHash.d.ts +78 -0
  61. package/package.json +45 -0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-present Lakutata
4
+
5
+ All rights reserved
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @lakutata/security
2
+
3
+ The security toolkit of the lakutata framework, built on `node:crypto`: password hashing with bcrypt (`PasswordHash`),
4
+ message signatures with HMAC (`Mac`), authenticated encryption with AES-256-GCM (`Crypt`), with key rotation, and the
5
+ functions under them (random keys, constant-time comparison, encryption with a key or a password). The umbrella package
6
+ `lakutata` re-exports it as `lakutata/security`, and `PasswordHash` alone as `lakutata/provider/passwordHash`.
7
+
8
+ ## Installation
9
+
10
+ ```shell
11
+ npm install lakutata
12
+ ```
13
+
14
+ It has no optional dependencies: bcrypt is implemented in JavaScript, the rest uses `node:crypto`.
15
+
16
+ ## Usage
17
+
18
+ ### The providers in an application
19
+
20
+ Declare the providers in the `providers` of the application, with their keys read from the environment, then get them
21
+ with `getObject()` or `@Inject`. They are scoped, like every provider.
22
+
23
+ ```typescript
24
+ import {Application} from 'lakutata'
25
+ import {Crypt, generateSecret, Mac, PasswordHash} from 'lakutata/security'
26
+
27
+ Application
28
+ .run(() => ({
29
+ id: 'security.app',
30
+ name: 'Security',
31
+ providers: {
32
+ passwordHash: {class: PasswordHash, saltRounds: 12},
33
+ //The first key signs and encrypts, every key verifies and decrypts
34
+ mac: {class: Mac, keys: [process.env.MAC_KEY ?? generateSecret()], algorithm: 'sha256'},
35
+ crypt: {class: Crypt, keys: [process.env.CRYPT_KEY ?? generateSecret(), process.env.CRYPT_PREVIOUS_KEY ?? generateSecret()]}
36
+ }
37
+ }))
38
+ .onLaunched(async (app: Application): Promise<void> => {
39
+ const passwordHash: PasswordHash = await app.getObject('passwordHash')
40
+ const hash: string = await passwordHash.hash('correct horse battery staple')
41
+ console.log(await passwordHash.validate('correct horse battery staple', hash))
42
+
43
+ const mac: Mac = await app.getObject('mac')
44
+ const token: string = mac.signMessage('user:42')
45
+ console.log(mac.unsignMessage(token))
46
+
47
+ const crypt: Crypt = await app.getObject('crypt')
48
+ console.log(crypt.decryptString(crypt.encrypt('api-token-123')))
49
+ })
50
+ ```
51
+
52
+ ### Which tool for which secret
53
+
54
+ | Need | Use | Fit for passwords | Output |
55
+ |---|---|---|---|
56
+ | Store a user password, check it at login | `PasswordHash` (bcrypt) | yes: slow and salted | 60-character bcrypt hash (`$2b$10$...`) |
57
+ | Detect tampering (cookies, links, webhooks) | `Mac`, `hmac()` / `verifyHMAC()` | no: fast, needs a random key | base64url HMAC (43 characters with SHA-256) |
58
+ | Hide and protect data, read it back | `Crypt`, `encryptByKey()` / `decryptByKey()` | no: needs a random key | base64url: version, salt, IV, tag, ciphertext |
59
+ | Encrypt with a passphrase typed by a person | `encryptByPassword()` / `decryptByPassword()` | yes, for encryption (scrypt) | same format, another version |
60
+ | Generate a key or a token | `generateSecret()` | — | base64url random bytes (32 by default) |
61
+ | Compare a secret received with the expected one | `constantTimeEqual()` | no: use `PasswordHash.validate()` | boolean |
62
+
63
+ - **Passwords**: `PasswordHash.hash()` rejects passwords longer than 72 bytes in UTF-8 (bcrypt ignores the following
64
+ bytes) with `PasswordTooLongException`, and `validate()` gives `false` for them. Each hash has its own random salt and
65
+ its cost (`saltRounds`, 4 to 31, 10 by default: each extra round doubles the time); store the hash as is and check it
66
+ with `validate()`, never with `===`. `validate()` gives `false` for a value which is not a bcrypt hash.
67
+ - **Keys**: the keys of `Mac` and `Crypt` are strings of at least 16 characters, used as their UTF-8 bytes. Generate
68
+ them with `generateSecret()` (32 random bytes): the key derivations of `Mac`, `Crypt` and `encryptByKey()` are fast, so a
69
+ password used as a key is open to brute force. The functions refuse an empty key with a `RangeError`, but do not
70
+ check the key's length otherwise.
71
+ - **Key rotation**: put the new key first and keep the previous ones while the data they signed or encrypted is read;
72
+ the first key signs and encrypts, every key verifies and decrypts.
73
+ - **Formats**: the signatures and the encrypted data are base64url strings without padding, safe in URLs and cookies.
74
+ The encrypted data is authenticated: any change makes the decryption fail rather than return altered data.
75
+
76
+ ### The functions
77
+
78
+ ```typescript
79
+ import {
80
+ decryptByKey,
81
+ decryptByPassword,
82
+ encryptByKey,
83
+ encryptByPassword,
84
+ generateSecret,
85
+ hmac,
86
+ constantTimeEqual,
87
+ verifyHMAC
88
+ } from 'lakutata/security'
89
+
90
+ const key: string = generateSecret()
91
+
92
+ //Signatures: the raw body and its signature, sent in a header
93
+ const body: string = JSON.stringify({order: 42})
94
+ const signature: string = hmac(body, key, 'sha512')
95
+ console.log(verifyHMAC(body, signature, key, 'sha512'))
96
+
97
+ //Encryption with a random key, decrypted with the current or a previous key
98
+ const encrypted: string = encryptByKey('card ending 4242', key)
99
+ console.log(decryptByKey(encrypted, [key, generateSecret()]).toString('utf8'))
100
+
101
+ //Encryption with a passphrase (scrypt, asynchronous)
102
+ const backup: string = await encryptByPassword('backup contents', 'a long passphrase')
103
+ console.log((await decryptByPassword(backup, 'a long passphrase')).toString('utf8'))
104
+
105
+ //Constant-time comparison of a presented token
106
+ console.log(constantTimeEqual('presented-token', process.env.API_TOKEN ?? ''))
107
+ ```
108
+
109
+ ## API
110
+
111
+ | Export | What it is |
112
+ |---|---|
113
+ | `PasswordHash` | Provider hashing passwords with bcrypt: `hash()`, `validate()`; the option `saltRounds` (default 10) |
114
+ | `Mac` | Provider signing with HMAC and key rotation: `sign()`, `verify()`, `signMessage()`, `unsignMessage()`; the options `keys`, `algorithm` (default `'sha256'`) |
115
+ | `Crypt` | Provider encrypting with AES-256-GCM and key rotation: `encrypt()`, `decrypt()`, `decryptString()`; the option `keys` |
116
+ | `hmac`, `verifyHMAC` | Sign data with a key, check a signature in constant time |
117
+ | `HMACAlgorithm` | The hash functions of the signatures: `'sha256'`, `'sha384'`, `'sha512'` |
118
+ | `encryptByKey`, `decryptByKey` | Authenticated encryption with a random key (HKDF-SHA256 per message) |
119
+ | `encryptByPassword`, `decryptByPassword` | Authenticated encryption with a password (scrypt) |
120
+ | `generateSecret` | A random key or token, base64url |
121
+ | `constantTimeEqual` | Compare two secrets in constant time, whatever their lengths |
122
+ | `PasswordTooLongException`, `InvalidSignatureException`, `DecryptionFailedException` | The exceptions |
123
+
124
+ All are exported by `lakutata/security` and `@lakutata/security`. The typings document each declaration with examples.
125
+
126
+ ## Errors
127
+
128
+ | Exception | When |
129
+ |---|---|
130
+ | `PasswordTooLongException` (`E_PASSWORD_TOO_LONG`) | `PasswordHash.hash()` with a password longer than 72 bytes in UTF-8 |
131
+ | `InvalidSignatureException` (`E_INVALID_SIGNATURE`) | `Mac.unsignMessage()` with a message changed, unsigned or signed by an unknown key |
132
+ | `DecryptionFailedException` (`E_DECRYPTION_FAILED`) | `Crypt.decrypt()`, `decryptString()`, `decryptByKey()`, `decryptByPassword()`: a wrong key or password, changed or truncated data |
133
+ | `InvalidObjectOptionsException` (from `lakutata`) | The application starts with invalid provider options: no key, a key shorter than 16 characters, an unknown algorithm, `saltRounds` outside 4 to 31 (`InvalidValueException` when the provider is created outside the checked options, as in a bare `Container`) |
134
+ | `RangeError` | `generateSecret()` with a negative or too large number of bytes; `hmac()`, `verifyHMAC()`, `encryptByKey()`, `decryptByKey()` with an empty key |
135
+
136
+ The exceptions have the HTTP status 500 unless the application maps them; `verify()`, `verifyHMAC()` (given a key),
137
+ `validate()` and `constantTimeEqual()` return `false` for a wrong or malformed signature, hash or secret instead of
138
+ throwing.
139
+
140
+ ## See also
141
+
142
+ - `doc/en/Security.md` of the package `lakutata` (Chinese in `doc/zh`): the guide of the security tools.
143
+ - `@lakutata/core` (`lakutata`): the providers, `Application` and the dependency injection.
@@ -0,0 +1,36 @@
1
+ # Third-party notices
2
+
3
+ Parts of `@lakutata/security` are derived from the third-party projects listed below. They are rewritten in
4
+ TypeScript and modified; the original copyright notices and license terms are reproduced here as required.
5
+ The files derived from these projects refer to "the upstream" in their header; the projects, versions and directories
6
+ are listed in `src/lib/bcrypt/UPSTREAM.md`.
7
+
8
+ ## bcrypt.js (bcryptjs) 3.0.3
9
+
10
+ Used in `src/lib/bcrypt` (and its ported tests).
11
+
12
+ Copyright (c) 2012 Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
13
+ Copyright (c) 2012 Shane Girish <shaneGirish@gmail.com>
14
+ Copyright (c) 2025 Daniel Wirtz <dcode@dcode.io>
15
+
16
+ Redistribution and use in source and binary forms, with or without
17
+ modification, are permitted provided that the following conditions
18
+ are met:
19
+ 1. Redistributions of source code must retain the above copyright
20
+ notice, this list of conditions and the following disclaimer.
21
+ 2. Redistributions in binary form must reproduce the above copyright
22
+ notice, this list of conditions and the following disclaimer in the
23
+ documentation and/or other materials provided with the distribution.
24
+ 3. The name of the author may not be used to endorse or promote products
25
+ derived from this software without specific prior written permission.
26
+
27
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,13 @@
1
+ import { Exception } from '@lakutata/core';
2
+ /**
3
+ * Thrown by {@link Crypt.decrypt}, {@link Crypt.decryptString}, {@link decryptByKey} and {@link decryptByPassword}
4
+ * when the data cannot be decrypted: a wrong key or password, data changed or truncated, or data not produced by the
5
+ * matching encryption function. The message does not tell these cases apart, on purpose. Its `errno` is
6
+ * `'E_DECRYPTION_FAILED'`; its HTTP status is 500 unless the application handles it.
7
+ */
8
+ export declare class DecryptionFailedException extends Exception {
9
+ /**
10
+ * `'E_DECRYPTION_FAILED'`.
11
+ */
12
+ errno: string | number;
13
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DecryptionFailedException = void 0;
4
+ const core_1 = require("@lakutata/core");
5
+ /**
6
+ * Thrown by {@link Crypt.decrypt}, {@link Crypt.decryptString}, {@link decryptByKey} and {@link decryptByPassword}
7
+ * when the data cannot be decrypted: a wrong key or password, data changed or truncated, or data not produced by the
8
+ * matching encryption function. The message does not tell these cases apart, on purpose. Its `errno` is
9
+ * `'E_DECRYPTION_FAILED'`; its HTTP status is 500 unless the application handles it.
10
+ */
11
+ class DecryptionFailedException extends core_1.Exception {
12
+ constructor() {
13
+ super(...arguments);
14
+ /**
15
+ * `'E_DECRYPTION_FAILED'`.
16
+ */
17
+ this.errno = 'E_DECRYPTION_FAILED';
18
+ }
19
+ }
20
+ exports.DecryptionFailedException = DecryptionFailedException;
@@ -0,0 +1,13 @@
1
+ import { Exception } from '@lakutata/core';
2
+ /**
3
+ * Thrown by {@link Mac.unsignMessage} when the signed message has no signature or its signature is not valid (the
4
+ * message or the signature was changed, or none of the configured keys signed it); applications may throw it too
5
+ * after {@link verifyHMAC} fails. Its `errno` is `'E_INVALID_SIGNATURE'`; its HTTP status is 500 unless the application
6
+ * handles it.
7
+ */
8
+ export declare class InvalidSignatureException extends Exception {
9
+ /**
10
+ * `'E_INVALID_SIGNATURE'`.
11
+ */
12
+ errno: string | number;
13
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidSignatureException = void 0;
4
+ const core_1 = require("@lakutata/core");
5
+ /**
6
+ * Thrown by {@link Mac.unsignMessage} when the signed message has no signature or its signature is not valid (the
7
+ * message or the signature was changed, or none of the configured keys signed it); applications may throw it too
8
+ * after {@link verifyHMAC} fails. Its `errno` is `'E_INVALID_SIGNATURE'`; its HTTP status is 500 unless the application
9
+ * handles it.
10
+ */
11
+ class InvalidSignatureException extends core_1.Exception {
12
+ constructor() {
13
+ super(...arguments);
14
+ /**
15
+ * `'E_INVALID_SIGNATURE'`.
16
+ */
17
+ this.errno = 'E_INVALID_SIGNATURE';
18
+ }
19
+ }
20
+ exports.InvalidSignatureException = InvalidSignatureException;
@@ -0,0 +1,12 @@
1
+ import { Exception } from '@lakutata/core';
2
+ /**
3
+ * Thrown by {@link PasswordHash.hash} when the password is longer than 72 bytes once encoded in UTF-8, the most bcrypt
4
+ * reads: hashing only its prefix would let any password sharing those 72 bytes match. Ask the user for a shorter
5
+ * password. Its `errno` is `'E_PASSWORD_TOO_LONG'`; its HTTP status is 500 unless the application handles it.
6
+ */
7
+ export declare class PasswordTooLongException extends Exception {
8
+ /**
9
+ * `'E_PASSWORD_TOO_LONG'`.
10
+ */
11
+ errno: string | number;
12
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PasswordTooLongException = void 0;
4
+ const core_1 = require("@lakutata/core");
5
+ /**
6
+ * Thrown by {@link PasswordHash.hash} when the password is longer than 72 bytes once encoded in UTF-8, the most bcrypt
7
+ * reads: hashing only its prefix would let any password sharing those 72 bytes match. Ask the user for a shorter
8
+ * password. Its `errno` is `'E_PASSWORD_TOO_LONG'`; its HTTP status is 500 unless the application handles it.
9
+ */
10
+ class PasswordTooLongException extends core_1.Exception {
11
+ constructor() {
12
+ super(...arguments);
13
+ /**
14
+ * `'E_PASSWORD_TOO_LONG'`.
15
+ */
16
+ this.errno = 'E_PASSWORD_TOO_LONG';
17
+ }
18
+ }
19
+ exports.PasswordTooLongException = PasswordTooLongException;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Providers
3
+ */
4
+ export * from '../providers/PasswordHash.js';
5
+ export * from '../providers/Mac.js';
6
+ export * from '../providers/Crypt.js';
7
+ /**
8
+ * Functions
9
+ */
10
+ export * from '../functions/TimingSafeEqual.js';
11
+ export * from '../functions/GenerateSecret.js';
12
+ export * from '../functions/HMAC.js';
13
+ export * from '../functions/Encryption.js';
14
+ /**
15
+ * Exceptions
16
+ */
17
+ export * from '../exceptions/PasswordTooLongException.js';
18
+ export * from '../exceptions/InvalidSignatureException.js';
19
+ export * from '../exceptions/DecryptionFailedException.js';
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Providers
19
+ */
20
+ __exportStar(require("../providers/PasswordHash.js"), exports);
21
+ __exportStar(require("../providers/Mac.js"), exports);
22
+ __exportStar(require("../providers/Crypt.js"), exports);
23
+ /**
24
+ * Functions
25
+ */
26
+ __exportStar(require("../functions/TimingSafeEqual.js"), exports);
27
+ __exportStar(require("../functions/GenerateSecret.js"), exports);
28
+ __exportStar(require("../functions/HMAC.js"), exports);
29
+ __exportStar(require("../functions/Encryption.js"), exports);
30
+ /**
31
+ * Exceptions
32
+ */
33
+ __exportStar(require("../exceptions/PasswordTooLongException.js"), exports);
34
+ __exportStar(require("../exceptions/InvalidSignatureException.js"), exports);
35
+ __exportStar(require("../exceptions/DecryptionFailedException.js"), exports);
36
+
37
+ //The default of the entry: the entry itself (scripts/package-build/cjs-package.mjs)
38
+ if (!Object.prototype.hasOwnProperty.call(exports, 'default')) Object.defineProperty(exports, 'default', {value: exports})
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Encrypt data with a secret key, using AES-256-GCM (authenticated encryption): the output is confidential and
3
+ * tamper-proof. Each call derives a fresh key from the key and a random 16-byte salt (HKDF-sha256) and uses a random
4
+ * 12-byte IV, so encrypting the same data twice gives different outputs. The {@link Crypt} provider does the same with
5
+ * configured keys and key rotation.
6
+ *
7
+ * The key must be random (see {@link generateSecret}): its derivation is fast, so a password used as a key is open to
8
+ * brute force; use {@link encryptByPassword} for passwords. The function refuses an empty key, but does not check
9
+ * the key's length or strength otherwise.
10
+ * @param data The data to encrypt: a string (encrypted as its UTF-8 bytes) or bytes.
11
+ * @param key The secret key: a string (used as its UTF-8 bytes, not decoded from base64) or bytes, not empty; 32
12
+ * random bytes recommended.
13
+ * @returns The encrypted data, base64url encoded without padding: 1 version byte, the salt, the IV, a 16-byte
14
+ * authentication tag and the ciphertext (45 bytes more than the data, before base64url).
15
+ * @throws {RangeError} When the key is empty.
16
+ * @example
17
+ * ```typescript
18
+ * import {decryptByKey, encryptByKey, generateSecret} from 'lakutata/security'
19
+ *
20
+ * const key: string = process.env.CRYPT_KEY ?? generateSecret()
21
+ * const encrypted: string = encryptByKey('card ending 4242', key)
22
+ * console.log(decryptByKey(encrypted, key).toString('utf8'))
23
+ * ```
24
+ */
25
+ export declare function encryptByKey(data: string | Uint8Array, key: string | Uint8Array): string;
26
+ /**
27
+ * Decrypt data encrypted by {@link encryptByKey} (or {@link Crypt.encrypt}) with one of the keys, tried in order: list
28
+ * the current key and the previous ones during a key rotation. The authentication tag is checked before anything is
29
+ * returned, so changed data is never decrypted.
30
+ * @param encrypted The encrypted data, base64url as {@link encryptByKey} returns it.
31
+ * @param keys The key, or the keys to try: strings (used as their UTF-8 bytes) or bytes, none empty.
32
+ * @returns The decrypted bytes (call `.toString('utf8')` for a string).
33
+ * @throws {DecryptionFailedException} When no key decrypts the data, or it was changed, truncated or not produced by
34
+ * {@link encryptByKey} (data encrypted by {@link encryptByPassword} included).
35
+ * @throws {RangeError} When a key is empty.
36
+ * @example
37
+ * ```typescript
38
+ * import {decryptByKey, DecryptionFailedException} from 'lakutata/security'
39
+ *
40
+ * declare const encrypted: string
41
+ * declare const currentKey: string
42
+ * declare const previousKey: string
43
+ *
44
+ * try {
45
+ * //The new key first, the previous one while old data remains
46
+ * const secret: string = decryptByKey(encrypted, [currentKey, previousKey]).toString('utf8')
47
+ * console.log(secret)
48
+ * } catch (error) {
49
+ * if (error instanceof DecryptionFailedException) console.log('Changed, or encrypted with an unknown key')
50
+ * }
51
+ * ```
52
+ */
53
+ export declare function decryptByKey(encrypted: string, keys: string | Uint8Array | (string | Uint8Array)[]): Buffer;
54
+ /**
55
+ * Encrypt data with a password chosen by a person, using AES-256-GCM (authenticated encryption) with a key derived
56
+ * from the password and a random 16-byte salt by scrypt (N = 16384, r = 8, p = 1: about 16 MB of memory and a few
57
+ * tens of milliseconds per call, off the main thread). The slow derivation makes guessing the password costly; a
58
+ * strong password still matters. For a random key, {@link encryptByKey} is faster. To check a login password, use
59
+ * {@link PasswordHash} instead.
60
+ * @param data The data to encrypt: a string (encrypted as its UTF-8 bytes) or bytes.
61
+ * @param password The password: a string (used as its UTF-8 bytes) or bytes. Its length is not checked.
62
+ * @returns The encrypted data, base64url encoded without padding, in the format of {@link encryptByKey} with another
63
+ * version byte: the two are not interchangeable.
64
+ * @example
65
+ * ```typescript
66
+ * import {decryptByPassword, encryptByPassword} from 'lakutata/security'
67
+ *
68
+ * const encrypted: string = await encryptByPassword('backup contents', 'a long passphrase of the user')
69
+ * console.log((await decryptByPassword(encrypted, 'a long passphrase of the user')).toString('utf8'))
70
+ * ```
71
+ */
72
+ export declare function encryptByPassword(data: string | Uint8Array, password: string | Uint8Array): Promise<string>;
73
+ /**
74
+ * Decrypt data encrypted by {@link encryptByPassword}, deriving the key from the password and the salt stored in the
75
+ * data (the same scrypt cost).
76
+ * @param encrypted The encrypted data, base64url as {@link encryptByPassword} returns it.
77
+ * @param password The password it was encrypted with: a string (used as its UTF-8 bytes) or bytes.
78
+ * @returns The decrypted bytes (call `.toString('utf8')` for a string).
79
+ * @throws {DecryptionFailedException} When the password is wrong, or the data was changed, truncated or not produced
80
+ * by {@link encryptByPassword} (data encrypted by {@link encryptByKey} included).
81
+ * @example
82
+ * ```typescript
83
+ * import {decryptByPassword, DecryptionFailedException} from 'lakutata/security'
84
+ *
85
+ * declare const encrypted: string
86
+ * declare const passphrase: string
87
+ *
88
+ * try {
89
+ * console.log((await decryptByPassword(encrypted, passphrase)).toString('utf8'))
90
+ * } catch (error) {
91
+ * if (error instanceof DecryptionFailedException) console.log('Wrong passphrase, or changed data')
92
+ * }
93
+ * ```
94
+ */
95
+ export declare function decryptByPassword(encrypted: string, password: string | Uint8Array): Promise<Buffer>;