@casual-simulation/crypto 2.0.12

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,91 @@
1
+ /**
2
+ * Defines an interface for objects that can sign and verify
3
+ * messages.
4
+ *
5
+ * Note that signing and verifying the integrity of messages has nothing to do with
6
+ * confidentiality. That is, anyone can read the data even after signing.
7
+ */
8
+ export interface SigningCryptoImpl {
9
+ /**
10
+ * Gets whether this crypto implementation is supported on this platform.
11
+ */
12
+ supported(): boolean;
13
+ /**
14
+ * Signs the given data using the given private key and returns a promise
15
+ * that contains the signed data.
16
+ *
17
+ * Returns a promise that resolves with a buffer that contains the signature needed to verify that the given
18
+ * data was signed with the given key.
19
+ *
20
+ * @param key The key to use to sign the data.
21
+ * @param data The data to sign.
22
+ */
23
+ sign(key: PrivateCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer>;
24
+ /**
25
+ * Verifies that the given signature was created by the private key
26
+ * corresponding to the given public key and that it was used for the given data.
27
+ *
28
+ * Returns a promise that resolves with true if the data is valid, and resolves false otherwise.
29
+ *
30
+ * When the result is true, then we know that the party that posseses the private key created the signature
31
+ * for the data. Additionally, we know that the data has not been tampered with. We do not, however, know that the
32
+ * data was kept confidential.
33
+ *
34
+ * When the result is false, then we know that either another party is attempting to impersonate the valid one or that
35
+ * the data got changed/corrupted/tampered with while in transit.
36
+ *
37
+ * @param key The key to use to verify the data.
38
+ * @param signature The signature to verify.
39
+ * @param data The data to verify.
40
+ */
41
+ verify(key: PublicCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean>;
42
+ /**
43
+ * Verifies that the given signatures were created by the private key corresponding to the given public key
44
+ * and that it was used for the given datas.
45
+ * @param key The key to use to verify the data.
46
+ * @param signatures The signatures to verify.
47
+ * @param datas The data to verify.
48
+ */
49
+ verifyBatch(key: PublicCryptoKey, signatures: ArrayBuffer[], datas: ArrayBuffer[]): Promise<boolean[]>;
50
+ /**
51
+ * Exports the private key in PKCS #8 format encoded as PEM.
52
+ * @param key The key to export.
53
+ */
54
+ exportKey(key: PrivateCryptoKey): Promise<string>;
55
+ /**
56
+ * Exports the public key in SPKI format encoded as PEM.
57
+ * @param key
58
+ */
59
+ exportKey(key: PublicCryptoKey): Promise<string>;
60
+ /**
61
+ * Imports the given public key.
62
+ * @param key The key to import.
63
+ */
64
+ importPublicKey(key: string): Promise<PublicCryptoKey>;
65
+ /**
66
+ * Imports the given private key.
67
+ * @param key The key to import.
68
+ */
69
+ importPrivateKey(key: string): Promise<PrivateCryptoKey>;
70
+ /**
71
+ * Generates a new public/private key pair.
72
+ */
73
+ generateKeyPair(): Promise<[PublicCryptoKey, PrivateCryptoKey]>;
74
+ }
75
+ /**
76
+ * Defines an interface for a private key.
77
+ */
78
+ export interface PrivateCryptoKey {
79
+ type: 'private';
80
+ }
81
+ /**
82
+ * Defines an interface for a public key.
83
+ */
84
+ export interface PublicCryptoKey {
85
+ type: 'public';
86
+ }
87
+ /**
88
+ * Defines a crypto key that is used for signing and verification.
89
+ */
90
+ export declare type SigningCryptoKey = PrivateCryptoKey | PublicCryptoKey;
91
+ //# sourceMappingURL=CryptoImpl.d.ts.map
package/CryptoImpl.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CryptoImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CryptoImpl.js","sourceRoot":"","sources":["CryptoImpl.ts"],"names":[],"mappings":""}
@@ -0,0 +1,166 @@
1
+ interface DerivedKey {
2
+ salt: Uint8Array;
3
+ hash: Uint8Array;
4
+ }
5
+ export declare function deriveKey(password: Uint8Array, salt: Uint8Array): DerivedKey;
6
+ /**
7
+ * Encrypts the given data with the given password and returns the resulting cyphertext.
8
+ *
9
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
10
+ *
11
+ * v1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
12
+ * The output string is formatted as following with periods between the components:
13
+ * 1. The version number (v1)
14
+ * 2. The base64 of the salt used to derive the key from the password. (pseudorandom)
15
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
16
+ * 4. The base64 of the encrypted data.
17
+ *
18
+ * @param password The password to use to encrypt.
19
+ * @param data The data to encrypt.
20
+ */
21
+ export declare function encrypt(password: string, data: Uint8Array): string;
22
+ /**
23
+ * Decrypts the given cyphertext with the given password and returns the original plaintext.
24
+ * Returns null if the data was unable to be decrypted.
25
+ * @param password The password to use to decrypt.
26
+ * @param cyphertext The data to decrypt.
27
+ */
28
+ export declare function decrypt(password: string, cyphertext: string): Uint8Array;
29
+ /**
30
+ * Encrypts the given data with the given password using version 1 of the encryption mechanisms in this file and returns the resulting
31
+ * cyphertext.
32
+ *
33
+ * version 1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
34
+ * The output string is formatted as following with periods between the components:
35
+ * 1. The version number (v1)
36
+ * 2. The base64 of the salt used to derive the key from the password. (pseudorandom)
37
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
38
+ * 4. The base64 of the encrypted data.
39
+ *
40
+ * @param password The password to use to encrypt the data.
41
+ * @param data The data to encrypt.
42
+ */
43
+ export declare function encryptV1(password: string, data: Uint8Array): string;
44
+ /**
45
+ * Decrypts the given data with the given password using version 1 of the encryption mechanisms in this file and returns the resulting
46
+ * plaintext.
47
+ *
48
+ * version 1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
49
+ *
50
+ * @param password The password to use to decrypt the data.
51
+ * @param cyphertext The cyphertext produced from encryptV1().
52
+ */
53
+ export declare function decryptV1(password: string, cyphertext: string): Uint8Array;
54
+ /**
55
+ * Determines whether the given keypair is a valid asymmetric keypair.
56
+ * Note that this function only determines if the keypair could have been generated by asymmetricKeypair(),
57
+ * not that it was or that it can be used to decrypt something.
58
+ * @param keypair The keypair to test.
59
+ */
60
+ export declare function isAsymmetricKeypair(keypair: string): boolean;
61
+ /**
62
+ * Creates a keypair that can be used for public key authenticated encryption.
63
+ *
64
+ * The returned keypair contains a version number at the beginning which determines the format of the following data.
65
+ *
66
+ * vEK1 keypairs use x25519 with XSalsa20 and Poly1305.
67
+ * The output string is formatting as following with periods between the components:
68
+ * 1. The version number (vEK1) - the EK is for "encryption keypair".
69
+ * 2. The base64 of the public key.
70
+ * 3. The base64 of the encrypted private key.
71
+ *
72
+ * @param password The password that should be used to encrypt the private key of the keypair.
73
+ */
74
+ export declare function asymmetricKeypair(password: string): string;
75
+ /**
76
+ * Creates a version 1 keypair that can be used for public key authenticated encryption.
77
+ *
78
+ * The returned keypair contains a version number at the beginning which determines the format of the following data.
79
+ *
80
+ * vEK1 keypairs use x25519 with XSalsa20 and Poly1305.
81
+ * The output string is formatting as following with periods between the components:
82
+ * 1. The version number (vEK1) - the EK is for "encryption keypair".
83
+ * 2. The base64 of the public key.
84
+ * 3. The base64 of the encrypted private key.
85
+ *
86
+ * @param password The password that should be used to encrypt the private key of the keypair.
87
+ */
88
+ export declare function asymmetricKeypairV1(password: string): string;
89
+ /**
90
+ * Encrypts the given data with the given keypair and returns the resulting cyphertext.
91
+ *
92
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
93
+ *
94
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
95
+ *
96
+ * vA1 encryptions technically use two keypairs for encryption/decryption. One for the local party and one for the remote party.
97
+ * These two parties are used to calculate a shared key that is then used to encrypt and authenticate the data.
98
+ * When encrypting, a new keypair is generated and used for the "local" party while the given keypair is used for the remote party.
99
+ * The secret key is then discarded while the public key is included in the output to make it possible for the other party to decrypt the data.
100
+ *
101
+ * The output string is formatted as following with periods between the components:
102
+ * 1. The version number (vA1). The "A" means "asymmetric".
103
+ * 2. The base64 of the public key of the keypair used to encrypt the data.
104
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
105
+ * 4. The base64 of the encrypted data.
106
+ *
107
+ * @param keypair The keypair to use to encrypt.
108
+ * @param data The data to encrypt.
109
+ */
110
+ export declare function asymmetricEncrypt(keypair: string, data: Uint8Array): string;
111
+ /**
112
+ * Encrypts the given data with the given keypair and returns the resulting cyphertext.
113
+ *
114
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
115
+ *
116
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
117
+ *
118
+ * vA1 encryptions technically use two keypairs for encryption/decryption. One for the local party and one for the remote party.
119
+ * These two parties are used to calculate a shared key that is then used to encrypt and authenticate the data.
120
+ * When encrypting, a new keypair is generated and used for the "local" party while the given keypair is used for the remote party.
121
+ * The secret key is then discarded while the public key is included in the output to make it possible for the other party to decrypt the data.
122
+ *
123
+ * The output string is formatted as following with periods between the components:
124
+ * 1. The version number (vA1). The "A" means "asymmetric".
125
+ * 2. The base64 of the public key of the keypair used to encrypt the data.
126
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
127
+ * 4. The base64 of the encrypted data.
128
+ *
129
+ * @param keypair The keypair to use to encrypt.
130
+ * @param data The data to encrypt.
131
+ */
132
+ export declare function asymmetricEncryptV1(keypair: string, data: Uint8Array): string;
133
+ /**
134
+ * Decrypts the given data with the given keypair and returns the resulting plaintext.
135
+ * Returns null if the data was unable to be decrypted.
136
+ *
137
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
138
+ *
139
+ * @param keypair The keypair to use to decrypt the data.
140
+ * @param password The password that should be used to decrypt the keypair's private key.
141
+ * @param cyphertext The data to decrypt.
142
+ */
143
+ export declare function asymmetricDecrypt(keypair: string, password: string, cyphertext: string): Uint8Array;
144
+ /**
145
+ * Decrypts the given data with the given keypair using version 1 of the asymmetric encryption mechanisms in this file and returns the resulting
146
+ * plaintext. Returns null if the data was unable to be decrypted.
147
+ *
148
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
149
+ *
150
+ * @param keypair The keypair to use to decrypt the data.
151
+ * @param password The password that should be used to decrypt the keypair's private key.
152
+ * @param cyphertext The data to decrypt.
153
+ */
154
+ export declare function asymmetricDecryptV1(keypair: string, password: string, cyphertext: string): Uint8Array;
155
+ /**
156
+ * Determines whether the given data appears to be encrypted using asymmetric encryption.
157
+ * @param cyphertext The cyphertext to test.
158
+ */
159
+ export declare function isAsymmetricEncrypted(cyphertext: string): boolean;
160
+ /**
161
+ * Determines whether the given data appears to be encrypted using symmetric encryption.
162
+ * @param cyphertext The cyphertext to test.
163
+ */
164
+ export declare function isEncrypted(cyphertext: string): boolean;
165
+ export {};
166
+ //# sourceMappingURL=Encryption.d.ts.map
package/Encryption.js ADDED
@@ -0,0 +1,355 @@
1
+ import { randomBytes, secretbox, box } from 'tweetnacl';
2
+ import { syncScrypt } from 'scrypt-js';
3
+ import { fromByteArray, toByteArray } from 'base64-js';
4
+ const ITERATIONS = 16384;
5
+ const BLOCK_SIZE = 8;
6
+ const PARALLELISM = 1;
7
+ const KEY_LENGTH = secretbox.keyLength;
8
+ export function deriveKey(password, salt) {
9
+ const result = syncScrypt(password, salt, ITERATIONS, BLOCK_SIZE, PARALLELISM, KEY_LENGTH);
10
+ return {
11
+ salt: salt,
12
+ hash: result,
13
+ };
14
+ }
15
+ /**
16
+ * Encrypts the given data with the given password and returns the resulting cyphertext.
17
+ *
18
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
19
+ *
20
+ * v1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
21
+ * The output string is formatted as following with periods between the components:
22
+ * 1. The version number (v1)
23
+ * 2. The base64 of the salt used to derive the key from the password. (pseudorandom)
24
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
25
+ * 4. The base64 of the encrypted data.
26
+ *
27
+ * @param password The password to use to encrypt.
28
+ * @param data The data to encrypt.
29
+ */
30
+ export function encrypt(password, data) {
31
+ return encryptV1(password, data);
32
+ }
33
+ /**
34
+ * Decrypts the given cyphertext with the given password and returns the original plaintext.
35
+ * Returns null if the data was unable to be decrypted.
36
+ * @param password The password to use to decrypt.
37
+ * @param cyphertext The data to decrypt.
38
+ */
39
+ export function decrypt(password, cyphertext) {
40
+ if (!password) {
41
+ throw new Error('Invalid password. Must not be null or undefined.');
42
+ }
43
+ if (!cyphertext) {
44
+ throw new Error('Invalid cyphertext. Must not be null or undefined.');
45
+ }
46
+ if (cyphertext.startsWith('v1.')) {
47
+ return decryptV1(password, cyphertext);
48
+ }
49
+ return null;
50
+ }
51
+ /**
52
+ * Encrypts the given data with the given password using version 1 of the encryption mechanisms in this file and returns the resulting
53
+ * cyphertext.
54
+ *
55
+ * version 1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
56
+ * The output string is formatted as following with periods between the components:
57
+ * 1. The version number (v1)
58
+ * 2. The base64 of the salt used to derive the key from the password. (pseudorandom)
59
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
60
+ * 4. The base64 of the encrypted data.
61
+ *
62
+ * @param password The password to use to encrypt the data.
63
+ * @param data The data to encrypt.
64
+ */
65
+ export function encryptV1(password, data) {
66
+ if (!password) {
67
+ throw new Error('Invalid password. Must not be null or undefined.');
68
+ }
69
+ if (!data) {
70
+ throw new Error('Invalid data. Must not be null or undefined.');
71
+ }
72
+ const nonce = randomBytes(secretbox.nonceLength);
73
+ const salt = randomBytes(secretbox.nonceLength);
74
+ const textEncoder = new TextEncoder();
75
+ const passwordBytes = textEncoder.encode(password);
76
+ const key = deriveKey(passwordBytes, salt);
77
+ const cypherBytes = secretbox(data, nonce, key.hash);
78
+ const cyphertext = `v1.${fromByteArray(salt)}.${fromByteArray(nonce)}.${fromByteArray(cypherBytes)}`;
79
+ return cyphertext;
80
+ }
81
+ /**
82
+ * Decrypts the given data with the given password using version 1 of the encryption mechanisms in this file and returns the resulting
83
+ * plaintext.
84
+ *
85
+ * version 1 encryptions use XSalsa20 as the cipher and Poly1305 for authentication in addition to scrypt for password-based key derivation.
86
+ *
87
+ * @param password The password to use to decrypt the data.
88
+ * @param cyphertext The cyphertext produced from encryptV1().
89
+ */
90
+ export function decryptV1(password, cyphertext) {
91
+ if (!password) {
92
+ throw new Error('Invalid password. Must not be null or undefined.');
93
+ }
94
+ if (!cyphertext) {
95
+ throw new Error('Invalid cyphertext. Must not be null or undefined.');
96
+ }
97
+ if (!cyphertext.startsWith('v1.')) {
98
+ throw new Error('Invalid cyphertext. Must start with "v1."');
99
+ }
100
+ const withoutVersion = cyphertext.slice('v1.'.length);
101
+ let nextPeriod = withoutVersion.indexOf('.');
102
+ if (nextPeriod < 0) {
103
+ return null;
104
+ }
105
+ const saltBase64 = withoutVersion.slice(0, nextPeriod);
106
+ const withoutSalt = withoutVersion.slice(nextPeriod + 1);
107
+ nextPeriod = withoutSalt.indexOf('.');
108
+ if (nextPeriod < 0) {
109
+ return null;
110
+ }
111
+ const nonceBase64 = withoutSalt.slice(0, nextPeriod);
112
+ const dataBase64 = withoutSalt.slice(nextPeriod + 1);
113
+ if (dataBase64.length <= 0) {
114
+ return null;
115
+ }
116
+ const salt = toByteArray(saltBase64);
117
+ const nonce = toByteArray(nonceBase64);
118
+ const data = toByteArray(dataBase64);
119
+ const textEncoder = new TextEncoder();
120
+ const passwordBytes = textEncoder.encode(password);
121
+ const key = deriveKey(passwordBytes, salt);
122
+ return secretbox.open(data, nonce, key.hash);
123
+ }
124
+ /**
125
+ * Determines whether the given keypair is a valid asymmetric keypair.
126
+ * Note that this function only determines if the keypair could have been generated by asymmetricKeypair(),
127
+ * not that it was or that it can be used to decrypt something.
128
+ * @param keypair The keypair to test.
129
+ */
130
+ export function isAsymmetricKeypair(keypair) {
131
+ try {
132
+ const [publicKey, privateKey] = decodeAsymmetricKeypairV1(keypair);
133
+ return !!publicKey && !!privateKey;
134
+ }
135
+ catch (ex) {
136
+ return false;
137
+ }
138
+ }
139
+ /**
140
+ * Creates a keypair that can be used for public key authenticated encryption.
141
+ *
142
+ * The returned keypair contains a version number at the beginning which determines the format of the following data.
143
+ *
144
+ * vEK1 keypairs use x25519 with XSalsa20 and Poly1305.
145
+ * The output string is formatting as following with periods between the components:
146
+ * 1. The version number (vEK1) - the EK is for "encryption keypair".
147
+ * 2. The base64 of the public key.
148
+ * 3. The base64 of the encrypted private key.
149
+ *
150
+ * @param password The password that should be used to encrypt the private key of the keypair.
151
+ */
152
+ export function asymmetricKeypair(password) {
153
+ return asymmetricKeypairV1(password);
154
+ }
155
+ /**
156
+ * Creates a version 1 keypair that can be used for public key authenticated encryption.
157
+ *
158
+ * The returned keypair contains a version number at the beginning which determines the format of the following data.
159
+ *
160
+ * vEK1 keypairs use x25519 with XSalsa20 and Poly1305.
161
+ * The output string is formatting as following with periods between the components:
162
+ * 1. The version number (vEK1) - the EK is for "encryption keypair".
163
+ * 2. The base64 of the public key.
164
+ * 3. The base64 of the encrypted private key.
165
+ *
166
+ * @param password The password that should be used to encrypt the private key of the keypair.
167
+ */
168
+ export function asymmetricKeypairV1(password) {
169
+ const pair = box.keyPair();
170
+ const encryptedPrivateKey = encrypt(password, pair.secretKey);
171
+ const encoder = new TextEncoder();
172
+ const privateKeyBytes = encoder.encode(encryptedPrivateKey);
173
+ return `vEK1.${fromByteArray(pair.publicKey)}.${fromByteArray(privateKeyBytes)}`;
174
+ }
175
+ function decodeAsymmetricKeypairV1(keypair) {
176
+ const withoutVersion = keypair.slice('vEK1.'.length);
177
+ let nextPeriod = withoutVersion.indexOf('.');
178
+ if (nextPeriod < 0) {
179
+ return [null, null];
180
+ }
181
+ const publicKeyBase64 = withoutVersion.slice(0, nextPeriod);
182
+ const withoutPublicKey = withoutVersion.slice(nextPeriod + 1);
183
+ const privateKeyBase64 = withoutPublicKey;
184
+ const publicKey = toByteArray(publicKeyBase64);
185
+ const privateKeyBytes = toByteArray(privateKeyBase64);
186
+ const decoder = new TextDecoder();
187
+ const privateKey = decoder.decode(privateKeyBytes);
188
+ return [publicKey, privateKey];
189
+ }
190
+ /**
191
+ * Encrypts the given data with the given keypair and returns the resulting cyphertext.
192
+ *
193
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
194
+ *
195
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
196
+ *
197
+ * vA1 encryptions technically use two keypairs for encryption/decryption. One for the local party and one for the remote party.
198
+ * These two parties are used to calculate a shared key that is then used to encrypt and authenticate the data.
199
+ * When encrypting, a new keypair is generated and used for the "local" party while the given keypair is used for the remote party.
200
+ * The secret key is then discarded while the public key is included in the output to make it possible for the other party to decrypt the data.
201
+ *
202
+ * The output string is formatted as following with periods between the components:
203
+ * 1. The version number (vA1). The "A" means "asymmetric".
204
+ * 2. The base64 of the public key of the keypair used to encrypt the data.
205
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
206
+ * 4. The base64 of the encrypted data.
207
+ *
208
+ * @param keypair The keypair to use to encrypt.
209
+ * @param data The data to encrypt.
210
+ */
211
+ export function asymmetricEncrypt(keypair, data) {
212
+ return asymmetricEncryptV1(keypair, data);
213
+ }
214
+ /**
215
+ * Encrypts the given data with the given keypair and returns the resulting cyphertext.
216
+ *
217
+ * The returned cyphertext contains a version number at the beginning which determines the format of the following data.
218
+ *
219
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
220
+ *
221
+ * vA1 encryptions technically use two keypairs for encryption/decryption. One for the local party and one for the remote party.
222
+ * These two parties are used to calculate a shared key that is then used to encrypt and authenticate the data.
223
+ * When encrypting, a new keypair is generated and used for the "local" party while the given keypair is used for the remote party.
224
+ * The secret key is then discarded while the public key is included in the output to make it possible for the other party to decrypt the data.
225
+ *
226
+ * The output string is formatted as following with periods between the components:
227
+ * 1. The version number (vA1). The "A" means "asymmetric".
228
+ * 2. The base64 of the public key of the keypair used to encrypt the data.
229
+ * 3. The base64 of the nonce used by the cipher. (pseudorandom)
230
+ * 4. The base64 of the encrypted data.
231
+ *
232
+ * @param keypair The keypair to use to encrypt.
233
+ * @param data The data to encrypt.
234
+ */
235
+ export function asymmetricEncryptV1(keypair, data) {
236
+ if (!keypair) {
237
+ throw new Error('Invalid keypair. Must not be null or undefined.');
238
+ }
239
+ if (!data) {
240
+ throw new Error('Invalid data. Must not be null or undefined.');
241
+ }
242
+ if (!keypair.startsWith('vEK1.')) {
243
+ throw new Error('Invalid keypair. Must start with "vEK1."');
244
+ }
245
+ const [theirPublicKey, theirPrivateKey] = decodeAsymmetricKeypairV1(keypair);
246
+ if (!theirPublicKey || !theirPrivateKey) {
247
+ throw new Error('Invalid keypair. Unable to be decoded.');
248
+ }
249
+ const localKeypair = box.keyPair();
250
+ const myPublicKey = localKeypair.publicKey;
251
+ const myPrivateKey = localKeypair.secretKey;
252
+ const nonce = randomBytes(box.nonceLength);
253
+ const cypherBytes = box(data, nonce, theirPublicKey, myPrivateKey);
254
+ const cyphertext = `vA1.${fromByteArray(myPublicKey)}.${fromByteArray(nonce)}.${fromByteArray(cypherBytes)}`;
255
+ return cyphertext;
256
+ }
257
+ /**
258
+ * Decrypts the given data with the given keypair and returns the resulting plaintext.
259
+ * Returns null if the data was unable to be decrypted.
260
+ *
261
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
262
+ *
263
+ * @param keypair The keypair to use to decrypt the data.
264
+ * @param password The password that should be used to decrypt the keypair's private key.
265
+ * @param cyphertext The data to decrypt.
266
+ */
267
+ export function asymmetricDecrypt(keypair, password, cyphertext) {
268
+ if (!keypair) {
269
+ throw new Error('Invalid keypair. Must not be null or undefined.');
270
+ }
271
+ if (!password) {
272
+ throw new Error('Invalid password. Must not be null or undefined.');
273
+ }
274
+ if (cyphertext.startsWith('vA1.')) {
275
+ return asymmetricDecryptV1(keypair, password, cyphertext);
276
+ }
277
+ return null;
278
+ }
279
+ /**
280
+ * Decrypts the given data with the given keypair using version 1 of the asymmetric encryption mechanisms in this file and returns the resulting
281
+ * plaintext. Returns null if the data was unable to be decrypted.
282
+ *
283
+ * vA1 encryptions use x25519 for key exchange, XSalsa20 as the cipher and Poly1305 for authentication.
284
+ *
285
+ * @param keypair The keypair to use to decrypt the data.
286
+ * @param password The password that should be used to decrypt the keypair's private key.
287
+ * @param cyphertext The data to decrypt.
288
+ */
289
+ export function asymmetricDecryptV1(keypair, password, cyphertext) {
290
+ if (!keypair) {
291
+ throw new Error('Invalid keypair. Must not be null or undefined.');
292
+ }
293
+ if (!password) {
294
+ throw new Error('Invalid password. Must not be null or undefined.');
295
+ }
296
+ if (!keypair.startsWith('vEK1.')) {
297
+ throw new Error('Invalid keypair. Must start with "vEK1."');
298
+ }
299
+ if (!cyphertext.startsWith('vA1.')) {
300
+ throw new Error('Invalid cyphertext. Must start with "vA1."');
301
+ }
302
+ const [myPublicKey, myEncryptedPrivateKey] = decodeAsymmetricKeypairV1(keypair);
303
+ if (!myPublicKey || !myEncryptedPrivateKey) {
304
+ throw new Error('Invalid keypair. Unable to be decoded.');
305
+ }
306
+ const myPrivateKey = decrypt(password, myEncryptedPrivateKey);
307
+ if (!myPrivateKey) {
308
+ throw new Error('Invalid keypair. Unable to decrypt the private key.');
309
+ }
310
+ const withoutVersion = cyphertext.slice('vA1.'.length);
311
+ let nextPeriod = withoutVersion.indexOf('.');
312
+ if (nextPeriod < 0) {
313
+ return null;
314
+ }
315
+ const theirPublicKeyBase64 = withoutVersion.slice(0, nextPeriod);
316
+ const withoutPublicKey = withoutVersion.slice(nextPeriod + 1);
317
+ nextPeriod = withoutPublicKey.indexOf('.');
318
+ if (nextPeriod < 0) {
319
+ return null;
320
+ }
321
+ const nonceBase64 = withoutPublicKey.slice(0, nextPeriod);
322
+ const dataBase64 = withoutPublicKey.slice(nextPeriod + 1);
323
+ if (dataBase64.length <= 0) {
324
+ return null;
325
+ }
326
+ const theirPublicKey = toByteArray(theirPublicKeyBase64);
327
+ const nonce = toByteArray(nonceBase64);
328
+ const data = toByteArray(dataBase64);
329
+ return box.open(data, nonce, theirPublicKey, myPrivateKey);
330
+ }
331
+ /**
332
+ * Determines whether the given data appears to be encrypted using asymmetric encryption.
333
+ * @param cyphertext The cyphertext to test.
334
+ */
335
+ export function isAsymmetricEncrypted(cyphertext) {
336
+ try {
337
+ return typeof cyphertext === 'string' && cyphertext.startsWith('vA1.');
338
+ }
339
+ catch (_a) {
340
+ return false;
341
+ }
342
+ }
343
+ /**
344
+ * Determines whether the given data appears to be encrypted using symmetric encryption.
345
+ * @param cyphertext The cyphertext to test.
346
+ */
347
+ export function isEncrypted(cyphertext) {
348
+ try {
349
+ return typeof cyphertext === 'string' && cyphertext.startsWith('v1.');
350
+ }
351
+ catch (_a) {
352
+ return false;
353
+ }
354
+ }
355
+ //# sourceMappingURL=Encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Encryption.js","sourceRoot":"","sources":["Encryption.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Calculates the SHA-256 hash of the given object.
4
+ * @param obj The object to calculate the hash of.
5
+ */
6
+ export declare function getHash(obj: any): string;
7
+ /**
8
+ * Calculates the SHA-256 hash of the given object and
9
+ * returns a byte buffer containing the hash.
10
+ * @param obj The object to hash.
11
+ */
12
+ export declare function getHashBuffer(obj: any): Buffer;
13
+ /**
14
+ * Hashes the given password using scrypt and returns the result.
15
+ * @param password The password that should be hashed.
16
+ */
17
+ export declare function hashPassword(password: string): string;
18
+ /**
19
+ * Verifies that the given password matches the given hash.
20
+ * @param password The password to check.
21
+ * @param hash The hash to check the password against.
22
+ */
23
+ export declare function verifyPassword(password: string, hash: string): boolean;
24
+ //# sourceMappingURL=HashHelpers.d.ts.map