@digitaldefiance/ecies-lib 1.1.23 → 1.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/package.json +13 -33
- package/src/constants.ts +474 -0
- package/src/email-string.ts +83 -0
- package/src/enumerations/ecies-encryption-type.ts +102 -0
- package/src/enumerations/ecies-error-type.ts +31 -0
- package/src/enumerations/ecies-string-key.ts +108 -0
- package/src/enumerations/guid-brand-type.ts +26 -0
- package/src/enumerations/guid-error-type.ts +6 -0
- package/{dist/enumerations/index.d.ts → src/enumerations/index.ts} +0 -1
- package/src/enumerations/invalid-email-type.ts +5 -0
- package/src/enumerations/length-encoding-type.ts +6 -0
- package/src/enumerations/length-error-type.ts +5 -0
- package/src/enumerations/member-error-type.ts +106 -0
- package/{dist/enumerations/member-type.d.ts → src/enumerations/member-type.ts} +7 -6
- package/src/enumerations/password-login-error-type.ts +4 -0
- package/src/enumerations/pbkdf2-error-type.ts +5 -0
- package/src/enumerations/pbkdf2-profile.ts +5 -0
- package/src/enumerations/secure-storage-error-type.ts +5 -0
- package/src/errors/disposed.ts +15 -0
- package/src/errors/ecies.ts +34 -0
- package/src/errors/guid.ts +34 -0
- package/{dist/errors/index.d.ts → src/errors/index.ts} +0 -1
- package/src/errors/invalid-email.ts +11 -0
- package/src/errors/length.ts +11 -0
- package/src/errors/member.ts +12 -0
- package/src/errors/pbkdf2.ts +12 -0
- package/src/errors/secure-storage.ts +13 -0
- package/src/errors/simple-ecies.ts +18 -0
- package/src/errors/simple-test-error.ts +6 -0
- package/src/guid.ts +800 -0
- package/src/i18n-setup.ts +1312 -0
- package/{dist/index.d.ts → src/index.ts} +0 -1
- package/src/interfaces/checksum-config.ts +4 -0
- package/src/interfaces/checksum-consts.ts +13 -0
- package/src/interfaces/constants.ts +48 -0
- package/src/interfaces/ecies-config.ts +8 -0
- package/src/interfaces/ecies-consts.ts +70 -0
- package/src/interfaces/ecies-file-service.ts +6 -0
- package/src/interfaces/guid.ts +53 -0
- package/{dist/interfaces/index.d.ts → src/interfaces/index.ts} +0 -1
- package/src/interfaces/library-error.ts +23 -0
- package/src/interfaces/member-operational.ts +54 -0
- package/{dist/interfaces/member-storage.d.ts → src/interfaces/member-storage.ts} +11 -10
- package/{dist/interfaces/member-with-mnemonic.d.ts → src/interfaces/member-with-mnemonic.ts} +3 -3
- package/src/interfaces/pbkdf2-config.ts +6 -0
- package/src/interfaces/pbkdf2-consts.ts +10 -0
- package/src/interfaces/pbkdf2-result.ts +5 -0
- package/src/member.ts +429 -0
- package/{dist/pbkdf2-profiles.d.ts → src/pbkdf2-profiles.ts} +2 -2
- package/src/phone-number.ts +18 -0
- package/src/regexes.ts +10 -0
- package/src/secure-buffer.ts +183 -0
- package/src/secure-string.ts +229 -0
- package/src/services/aes-gcm.ts +177 -0
- package/src/services/ecies/README.md +147 -0
- package/src/services/ecies/crypto-core.ts +180 -0
- package/src/services/ecies/example.ts +185 -0
- package/src/services/ecies/file.ts +167 -0
- package/{dist/services/ecies/index.d.ts → src/services/ecies/index.ts} +3 -1
- package/src/services/ecies/integration.ts +241 -0
- package/src/services/ecies/interfaces.ts +59 -0
- package/src/services/ecies/manual-test.ts +219 -0
- package/src/services/ecies/multi-recipient.ts +394 -0
- package/src/services/ecies/service.ts +317 -0
- package/src/services/ecies/signature.ts +93 -0
- package/src/services/ecies/single-recipient.ts +340 -0
- package/{dist/services/index.d.ts → src/services/index.ts} +0 -1
- package/src/services/password-login.ts +228 -0
- package/src/services/pbkdf2.ts +172 -0
- package/src/services/xor.ts +65 -0
- package/src/types/deep-partial.ts +11 -0
- package/{dist/types.d.ts → src/types.ts} +10 -4
- package/src/utils.ts +331 -0
- package/dist/constants.d.ts +0 -46
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -358
- package/dist/constants.js.map +0 -1
- package/dist/email-string.d.ts +0 -42
- package/dist/email-string.d.ts.map +0 -1
- package/dist/email-string.js +0 -75
- package/dist/email-string.js.map +0 -1
- package/dist/enumerations/ecies-encryption-type.d.ts +0 -15
- package/dist/enumerations/ecies-encryption-type.d.ts.map +0 -1
- package/dist/enumerations/ecies-encryption-type.js +0 -71
- package/dist/enumerations/ecies-encryption-type.js.map +0 -1
- package/dist/enumerations/ecies-error-type.d.ts +0 -32
- package/dist/enumerations/ecies-error-type.d.ts.map +0 -1
- package/dist/enumerations/ecies-error-type.js +0 -36
- package/dist/enumerations/ecies-error-type.js.map +0 -1
- package/dist/enumerations/ecies-string-key.d.ts +0 -96
- package/dist/enumerations/ecies-string-key.d.ts.map +0 -1
- package/dist/enumerations/ecies-string-key.js +0 -105
- package/dist/enumerations/ecies-string-key.js.map +0 -1
- package/dist/enumerations/guid-brand-type.d.ts +0 -27
- package/dist/enumerations/guid-brand-type.d.ts.map +0 -1
- package/dist/enumerations/guid-brand-type.js +0 -31
- package/dist/enumerations/guid-brand-type.js.map +0 -1
- package/dist/enumerations/guid-error-type.d.ts +0 -7
- package/dist/enumerations/guid-error-type.d.ts.map +0 -1
- package/dist/enumerations/guid-error-type.js +0 -11
- package/dist/enumerations/guid-error-type.js.map +0 -1
- package/dist/enumerations/index.d.ts.map +0 -1
- package/dist/enumerations/index.js +0 -31
- package/dist/enumerations/index.js.map +0 -1
- package/dist/enumerations/invalid-email-type.d.ts +0 -6
- package/dist/enumerations/invalid-email-type.d.ts.map +0 -1
- package/dist/enumerations/invalid-email-type.js +0 -10
- package/dist/enumerations/invalid-email-type.js.map +0 -1
- package/dist/enumerations/length-encoding-type.d.ts +0 -7
- package/dist/enumerations/length-encoding-type.d.ts.map +0 -1
- package/dist/enumerations/length-encoding-type.js +0 -11
- package/dist/enumerations/length-encoding-type.js.map +0 -1
- package/dist/enumerations/length-error-type.d.ts +0 -6
- package/dist/enumerations/length-error-type.d.ts.map +0 -1
- package/dist/enumerations/length-error-type.js +0 -10
- package/dist/enumerations/length-error-type.js.map +0 -1
- package/dist/enumerations/member-error-type.d.ts +0 -87
- package/dist/enumerations/member-error-type.d.ts.map +0 -1
- package/dist/enumerations/member-error-type.js +0 -91
- package/dist/enumerations/member-error-type.js.map +0 -1
- package/dist/enumerations/member-type.d.ts.map +0 -1
- package/dist/enumerations/member-type.js +0 -19
- package/dist/enumerations/member-type.js.map +0 -1
- package/dist/enumerations/password-login-error-type.d.ts +0 -5
- package/dist/enumerations/password-login-error-type.d.ts.map +0 -1
- package/dist/enumerations/password-login-error-type.js +0 -9
- package/dist/enumerations/password-login-error-type.js.map +0 -1
- package/dist/enumerations/pbkdf2-error-type.d.ts +0 -6
- package/dist/enumerations/pbkdf2-error-type.d.ts.map +0 -1
- package/dist/enumerations/pbkdf2-error-type.js +0 -10
- package/dist/enumerations/pbkdf2-error-type.js.map +0 -1
- package/dist/enumerations/pbkdf2-profile.d.ts +0 -6
- package/dist/enumerations/pbkdf2-profile.d.ts.map +0 -1
- package/dist/enumerations/pbkdf2-profile.js +0 -10
- package/dist/enumerations/pbkdf2-profile.js.map +0 -1
- package/dist/enumerations/secure-storage-error-type.d.ts +0 -6
- package/dist/enumerations/secure-storage-error-type.d.ts.map +0 -1
- package/dist/enumerations/secure-storage-error-type.js +0 -10
- package/dist/enumerations/secure-storage-error-type.js.map +0 -1
- package/dist/errors/disposed.d.ts +0 -4
- package/dist/errors/disposed.d.ts.map +0 -1
- package/dist/errors/disposed.js +0 -20
- package/dist/errors/disposed.js.map +0 -1
- package/dist/errors/ecies.d.ts +0 -7
- package/dist/errors/ecies.d.ts.map +0 -1
- package/dist/errors/ecies.js +0 -15
- package/dist/errors/ecies.js.map +0 -1
- package/dist/errors/guid.d.ts +0 -15
- package/dist/errors/guid.d.ts.map +0 -1
- package/dist/errors/guid.js +0 -26
- package/dist/errors/guid.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js +0 -25
- package/dist/errors/index.js.map +0 -1
- package/dist/errors/invalid-email.d.ts +0 -7
- package/dist/errors/invalid-email.d.ts.map +0 -1
- package/dist/errors/invalid-email.js +0 -14
- package/dist/errors/invalid-email.js.map +0 -1
- package/dist/errors/length.d.ts +0 -7
- package/dist/errors/length.d.ts.map +0 -1
- package/dist/errors/length.js +0 -14
- package/dist/errors/length.js.map +0 -1
- package/dist/errors/member.d.ts +0 -7
- package/dist/errors/member.d.ts.map +0 -1
- package/dist/errors/member.js +0 -14
- package/dist/errors/member.js.map +0 -1
- package/dist/errors/pbkdf2.d.ts +0 -7
- package/dist/errors/pbkdf2.d.ts.map +0 -1
- package/dist/errors/pbkdf2.js +0 -14
- package/dist/errors/pbkdf2.js.map +0 -1
- package/dist/errors/secure-storage.d.ts +0 -7
- package/dist/errors/secure-storage.d.ts.map +0 -1
- package/dist/errors/secure-storage.js +0 -15
- package/dist/errors/secure-storage.js.map +0 -1
- package/dist/errors/simple-ecies.d.ts +0 -6
- package/dist/errors/simple-ecies.d.ts.map +0 -1
- package/dist/errors/simple-ecies.js +0 -15
- package/dist/errors/simple-ecies.js.map +0 -1
- package/dist/errors/simple-test-error.d.ts +0 -4
- package/dist/errors/simple-test-error.d.ts.map +0 -1
- package/dist/errors/simple-test-error.js +0 -11
- package/dist/errors/simple-test-error.js.map +0 -1
- package/dist/guid.d.ts +0 -153
- package/dist/guid.d.ts.map +0 -1
- package/dist/guid.js +0 -647
- package/dist/guid.js.map +0 -1
- package/dist/i18n-setup.d.ts +0 -24
- package/dist/i18n-setup.d.ts.map +0 -1
- package/dist/i18n-setup.js +0 -837
- package/dist/i18n-setup.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -32
- package/dist/index.js.map +0 -1
- package/dist/interfaces/checksum-config.d.ts +0 -5
- package/dist/interfaces/checksum-config.d.ts.map +0 -1
- package/dist/interfaces/checksum-config.js +0 -3
- package/dist/interfaces/checksum-config.js.map +0 -1
- package/dist/interfaces/checksum-consts.d.ts +0 -11
- package/dist/interfaces/checksum-consts.d.ts.map +0 -1
- package/dist/interfaces/checksum-consts.js +0 -3
- package/dist/interfaces/checksum-consts.js.map +0 -1
- package/dist/interfaces/constants.d.ts +0 -45
- package/dist/interfaces/constants.d.ts.map +0 -1
- package/dist/interfaces/constants.js +0 -3
- package/dist/interfaces/constants.js.map +0 -1
- package/dist/interfaces/ecies-config.d.ts +0 -9
- package/dist/interfaces/ecies-config.d.ts.map +0 -1
- package/dist/interfaces/ecies-config.js +0 -3
- package/dist/interfaces/ecies-config.js.map +0 -1
- package/dist/interfaces/ecies-consts.d.ts +0 -58
- package/dist/interfaces/ecies-consts.d.ts.map +0 -1
- package/dist/interfaces/ecies-consts.js +0 -3
- package/dist/interfaces/ecies-consts.js.map +0 -1
- package/dist/interfaces/ecies-file-service.d.ts +0 -7
- package/dist/interfaces/ecies-file-service.d.ts.map +0 -1
- package/dist/interfaces/ecies-file-service.js +0 -3
- package/dist/interfaces/ecies-file-service.js.map +0 -1
- package/dist/interfaces/guid.d.ts +0 -45
- package/dist/interfaces/guid.d.ts.map +0 -1
- package/dist/interfaces/guid.js +0 -3
- package/dist/interfaces/guid.js.map +0 -1
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js +0 -30
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/library-error.d.ts +0 -23
- package/dist/interfaces/library-error.d.ts.map +0 -1
- package/dist/interfaces/library-error.js +0 -3
- package/dist/interfaces/library-error.js.map +0 -1
- package/dist/interfaces/member-operational.d.ts +0 -40
- package/dist/interfaces/member-operational.d.ts.map +0 -1
- package/dist/interfaces/member-operational.js +0 -3
- package/dist/interfaces/member-operational.js.map +0 -1
- package/dist/interfaces/member-storage.d.ts.map +0 -1
- package/dist/interfaces/member-storage.js +0 -3
- package/dist/interfaces/member-storage.js.map +0 -1
- package/dist/interfaces/member-with-mnemonic.d.ts.map +0 -1
- package/dist/interfaces/member-with-mnemonic.js +0 -3
- package/dist/interfaces/member-with-mnemonic.js.map +0 -1
- package/dist/interfaces/pbkdf2-config.d.ts +0 -7
- package/dist/interfaces/pbkdf2-config.d.ts.map +0 -1
- package/dist/interfaces/pbkdf2-config.js +0 -3
- package/dist/interfaces/pbkdf2-config.js.map +0 -1
- package/dist/interfaces/pbkdf2-consts.d.ts +0 -9
- package/dist/interfaces/pbkdf2-consts.d.ts.map +0 -1
- package/dist/interfaces/pbkdf2-consts.js +0 -3
- package/dist/interfaces/pbkdf2-consts.js.map +0 -1
- package/dist/interfaces/pbkdf2-result.d.ts +0 -6
- package/dist/interfaces/pbkdf2-result.d.ts.map +0 -1
- package/dist/interfaces/pbkdf2-result.js +0 -3
- package/dist/interfaces/pbkdf2-result.js.map +0 -1
- package/dist/member.d.ts +0 -66
- package/dist/member.d.ts.map +0 -1
- package/dist/member.js +0 -271
- package/dist/member.js.map +0 -1
- package/dist/pbkdf2-profiles.d.ts.map +0 -1
- package/dist/pbkdf2-profiles.js +0 -3
- package/dist/pbkdf2-profiles.js.map +0 -1
- package/dist/phone-number.d.ts +0 -6
- package/dist/phone-number.d.ts.map +0 -1
- package/dist/phone-number.js +0 -22
- package/dist/phone-number.js.map +0 -1
- package/dist/regexes.d.ts +0 -7
- package/dist/regexes.d.ts.map +0 -1
- package/dist/regexes.js +0 -10
- package/dist/regexes.js.map +0 -1
- package/dist/secure-buffer.d.ts +0 -38
- package/dist/secure-buffer.d.ts.map +0 -1
- package/dist/secure-buffer.js +0 -168
- package/dist/secure-buffer.js.map +0 -1
- package/dist/secure-string.d.ts +0 -39
- package/dist/secure-string.d.ts.map +0 -1
- package/dist/secure-string.js +0 -195
- package/dist/secure-string.js.map +0 -1
- package/dist/services/aes-gcm.d.ts +0 -57
- package/dist/services/aes-gcm.d.ts.map +0 -1
- package/dist/services/aes-gcm.js +0 -111
- package/dist/services/aes-gcm.js.map +0 -1
- package/dist/services/ecies/crypto-core.d.ts +0 -51
- package/dist/services/ecies/crypto-core.d.ts.map +0 -1
- package/dist/services/ecies/crypto-core.js +0 -139
- package/dist/services/ecies/crypto-core.js.map +0 -1
- package/dist/services/ecies/example.d.ts +0 -25
- package/dist/services/ecies/example.d.ts.map +0 -1
- package/dist/services/ecies/example.js +0 -128
- package/dist/services/ecies/example.js.map +0 -1
- package/dist/services/ecies/file.d.ts +0 -18
- package/dist/services/ecies/file.d.ts.map +0 -1
- package/dist/services/ecies/file.js +0 -110
- package/dist/services/ecies/file.js.map +0 -1
- package/dist/services/ecies/index.d.ts.map +0 -1
- package/dist/services/ecies/index.js +0 -57
- package/dist/services/ecies/index.js.map +0 -1
- package/dist/services/ecies/integration.d.ts +0 -59
- package/dist/services/ecies/integration.d.ts.map +0 -1
- package/dist/services/ecies/integration.js +0 -172
- package/dist/services/ecies/integration.js.map +0 -1
- package/dist/services/ecies/interfaces.d.ts +0 -51
- package/dist/services/ecies/interfaces.d.ts.map +0 -1
- package/dist/services/ecies/interfaces.js +0 -6
- package/dist/services/ecies/interfaces.js.map +0 -1
- package/dist/services/ecies/manual-test.d.ts +0 -29
- package/dist/services/ecies/manual-test.d.ts.map +0 -1
- package/dist/services/ecies/manual-test.js +0 -171
- package/dist/services/ecies/manual-test.js.map +0 -1
- package/dist/services/ecies/multi-recipient.d.ts +0 -52
- package/dist/services/ecies/multi-recipient.d.ts.map +0 -1
- package/dist/services/ecies/multi-recipient.js +0 -243
- package/dist/services/ecies/multi-recipient.js.map +0 -1
- package/dist/services/ecies/service.d.ts +0 -104
- package/dist/services/ecies/service.d.ts.map +0 -1
- package/dist/services/ecies/service.js +0 -192
- package/dist/services/ecies/service.js.map +0 -1
- package/dist/services/ecies/signature.d.ts +0 -27
- package/dist/services/ecies/signature.d.ts.map +0 -1
- package/dist/services/ecies/signature.js +0 -76
- package/dist/services/ecies/signature.js.map +0 -1
- package/dist/services/ecies/single-recipient.d.ts +0 -46
- package/dist/services/ecies/single-recipient.d.ts.map +0 -1
- package/dist/services/ecies/single-recipient.js +0 -212
- package/dist/services/ecies/single-recipient.js.map +0 -1
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/index.js +0 -22
- package/dist/services/index.js.map +0 -1
- package/dist/services/password-login.d.ts +0 -49
- package/dist/services/password-login.d.ts.map +0 -1
- package/dist/services/password-login.js +0 -121
- package/dist/services/password-login.js.map +0 -1
- package/dist/services/pbkdf2.d.ts +0 -56
- package/dist/services/pbkdf2.d.ts.map +0 -1
- package/dist/services/pbkdf2.js +0 -114
- package/dist/services/pbkdf2.js.map +0 -1
- package/dist/services/xor.d.ts +0 -37
- package/dist/services/xor.d.ts.map +0 -1
- package/dist/services/xor.js +0 -67
- package/dist/services/xor.js.map +0 -1
- package/dist/types/deep-partial.d.ts +0 -4
- package/dist/types/deep-partial.d.ts.map +0 -1
- package/dist/types/deep-partial.js +0 -3
- package/dist/types/deep-partial.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -68
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -288
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
|
+
import { SignatureString, SignatureUint8Array } from '../../types';
|
|
4
|
+
import { uint8ArrayToHex } from '../../utils';
|
|
5
|
+
import { EciesCryptoCore } from './crypto-core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Browser-compatible ECDSA signature operations
|
|
9
|
+
*/
|
|
10
|
+
export class EciesSignature {
|
|
11
|
+
private readonly cryptoCore: EciesCryptoCore;
|
|
12
|
+
|
|
13
|
+
constructor(cryptoCore: EciesCryptoCore) {
|
|
14
|
+
this.cryptoCore = cryptoCore;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Sign arbitrary binary data with a secp256k1 private key.
|
|
19
|
+
* Returns 64 bytes: [r(32) | s(32)]
|
|
20
|
+
*/
|
|
21
|
+
public signMessage(
|
|
22
|
+
privateKey: Uint8Array,
|
|
23
|
+
data: Uint8Array,
|
|
24
|
+
): SignatureUint8Array {
|
|
25
|
+
const hash = sha256(data);
|
|
26
|
+
// Use deterministic signatures (RFC 6979) for consistency
|
|
27
|
+
const signature = secp256k1.sign(hash, privateKey, {
|
|
28
|
+
format: 'compact',
|
|
29
|
+
extraEntropy: false,
|
|
30
|
+
});
|
|
31
|
+
return signature as SignatureUint8Array;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verify signature (64 bytes: [r|s]) over arbitrary binary data against a public key.
|
|
36
|
+
*/
|
|
37
|
+
public verifyMessage(
|
|
38
|
+
publicKey: Uint8Array,
|
|
39
|
+
data: Uint8Array,
|
|
40
|
+
signature: SignatureUint8Array,
|
|
41
|
+
): boolean {
|
|
42
|
+
try {
|
|
43
|
+
if (!signature || signature.length !== 64) return false;
|
|
44
|
+
const hash = sha256(data);
|
|
45
|
+
const normalizedPublicKey = this.cryptoCore.normalizePublicKey(publicKey);
|
|
46
|
+
|
|
47
|
+
// Try direct verification first
|
|
48
|
+
try {
|
|
49
|
+
const directResult = secp256k1.verify(
|
|
50
|
+
signature,
|
|
51
|
+
hash,
|
|
52
|
+
normalizedPublicKey,
|
|
53
|
+
);
|
|
54
|
+
if (directResult) return true;
|
|
55
|
+
} catch {
|
|
56
|
+
// Continue to alternative verification methods
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If direct verification fails, the signature might be from a different library
|
|
60
|
+
// that uses different nonce generation. Since we can't make @noble/curves
|
|
61
|
+
// verify signatures from ethereumjs-util directly, we'll return false here.
|
|
62
|
+
// The calling code should handle cross-platform verification at a higher level.
|
|
63
|
+
|
|
64
|
+
return false;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error('Signature verification failed:', err);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convert signature string to signature Uint8Array
|
|
73
|
+
*/
|
|
74
|
+
public signatureStringToSignatureUint8Array(
|
|
75
|
+
signatureString: SignatureString,
|
|
76
|
+
): SignatureUint8Array {
|
|
77
|
+
const cleanHex = signatureString.replace(/^0x/, '');
|
|
78
|
+
const result = new Uint8Array(cleanHex.length / 2);
|
|
79
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
80
|
+
result[i / 2] = parseInt(cleanHex.substring(i, i + 2), 16);
|
|
81
|
+
}
|
|
82
|
+
return result as SignatureUint8Array;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert signature buffer to signature string
|
|
87
|
+
*/
|
|
88
|
+
public signatureUint8ArrayToSignatureString(
|
|
89
|
+
signatureArray: SignatureUint8Array,
|
|
90
|
+
): SignatureString {
|
|
91
|
+
return uint8ArrayToHex(signatureArray) as SignatureString;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { IECIESConstants } from '../../interfaces/ecies-consts';
|
|
2
|
+
import { Constants } from '../../constants';
|
|
3
|
+
import {
|
|
4
|
+
EciesEncryptionType,
|
|
5
|
+
EciesEncryptionTypeEnum,
|
|
6
|
+
} from '../../enumerations/ecies-encryption-type';
|
|
7
|
+
import { IECIESConfig } from '../../interfaces/ecies-config';
|
|
8
|
+
import { AESGCMService } from '../aes-gcm';
|
|
9
|
+
|
|
10
|
+
import { EciesCryptoCore } from './crypto-core';
|
|
11
|
+
import { IDecryptionResult, ISingleEncryptedParsedHeader } from './interfaces';
|
|
12
|
+
import { EciesComponentId, getEciesI18nEngine } from '../../i18n-setup';
|
|
13
|
+
import { EciesStringKey } from '../../enumerations';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Browser-compatible single recipient ECIES encryption/decryption
|
|
17
|
+
*/
|
|
18
|
+
export class EciesSingleRecipient {
|
|
19
|
+
protected readonly cryptoCore: EciesCryptoCore;
|
|
20
|
+
protected readonly config: IECIESConfig;
|
|
21
|
+
protected readonly eciesConsts: IECIESConstants;
|
|
22
|
+
|
|
23
|
+
constructor(config: IECIESConfig, eciesParams: IECIESConstants = Constants.ECIES) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.eciesConsts = eciesParams;
|
|
26
|
+
this.cryptoCore = new EciesCryptoCore(config, this.eciesConsts);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Encrypt a message for a single recipient
|
|
31
|
+
*/
|
|
32
|
+
public async encrypt(
|
|
33
|
+
encryptSimple: boolean,
|
|
34
|
+
receiverPublicKey: Uint8Array,
|
|
35
|
+
message: Uint8Array,
|
|
36
|
+
preamble: Uint8Array = new Uint8Array(0),
|
|
37
|
+
): Promise<Uint8Array> {
|
|
38
|
+
const encryptionType: EciesEncryptionType = encryptSimple
|
|
39
|
+
? 'simple'
|
|
40
|
+
: 'single';
|
|
41
|
+
const encryptionTypeArray = new Uint8Array([
|
|
42
|
+
encryptionType === 'simple'
|
|
43
|
+
? this.eciesConsts.ENCRYPTION_TYPE.SIMPLE
|
|
44
|
+
: this.eciesConsts.ENCRYPTION_TYPE.SINGLE,
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
if (message.length > this.eciesConsts.MAX_RAW_DATA_SIZE) {
|
|
48
|
+
const engine = getEciesI18nEngine();
|
|
49
|
+
throw new Error(engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_MessageLengthExceedsMaximumAllowedSizeTemplate, {messageLength: message.length }));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Generate ephemeral key pair
|
|
53
|
+
const ephemeralPrivateKey = this.cryptoCore.generatePrivateKey();
|
|
54
|
+
const ephemeralPublicKey =
|
|
55
|
+
this.cryptoCore.getPublicKey(ephemeralPrivateKey);
|
|
56
|
+
|
|
57
|
+
// Compute shared secret
|
|
58
|
+
const normalizedReceiverPublicKey =
|
|
59
|
+
this.cryptoCore.normalizePublicKey(receiverPublicKey);
|
|
60
|
+
const sharedSecret = this.cryptoCore.computeSharedSecret(
|
|
61
|
+
ephemeralPrivateKey,
|
|
62
|
+
normalizedReceiverPublicKey,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Use first 32 bytes as symmetric key
|
|
66
|
+
const symKey = sharedSecret.slice(0, this.eciesConsts.SYMMETRIC.KEY_SIZE);
|
|
67
|
+
|
|
68
|
+
// Encrypt using AES-GCM
|
|
69
|
+
const encryptResult = await AESGCMService.encrypt(message, symKey, true, this.eciesConsts);
|
|
70
|
+
const { encrypted, iv } = encryptResult;
|
|
71
|
+
const authTag = encryptResult.tag;
|
|
72
|
+
|
|
73
|
+
if (!authTag) {
|
|
74
|
+
const engine = getEciesI18nEngine();
|
|
75
|
+
throw new Error(engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_AuthenticationTagIsRequiredForECIESEncryption));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Add length prefix for single mode
|
|
79
|
+
const lengthArray =
|
|
80
|
+
encryptionType === 'simple' ? new Uint8Array(0) : new Uint8Array(8);
|
|
81
|
+
|
|
82
|
+
if (encryptionType === 'single') {
|
|
83
|
+
const view = new DataView(lengthArray.buffer);
|
|
84
|
+
view.setBigUint64(0, BigInt(message.length), false); // big-endian
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Format: [preamble] | type (1) | ephemeralPublicKey (65) | iv (16) | authTag (16) | length (8) | encryptedData
|
|
88
|
+
const result = new Uint8Array(
|
|
89
|
+
preamble.length +
|
|
90
|
+
encryptionTypeArray.length +
|
|
91
|
+
ephemeralPublicKey.length +
|
|
92
|
+
iv.length +
|
|
93
|
+
authTag.length +
|
|
94
|
+
lengthArray.length +
|
|
95
|
+
encrypted.length,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
let offset = 0;
|
|
99
|
+
result.set(preamble, offset);
|
|
100
|
+
offset += preamble.length;
|
|
101
|
+
result.set(encryptionTypeArray, offset);
|
|
102
|
+
offset += encryptionTypeArray.length;
|
|
103
|
+
result.set(ephemeralPublicKey, offset);
|
|
104
|
+
offset += ephemeralPublicKey.length;
|
|
105
|
+
result.set(iv, offset);
|
|
106
|
+
offset += iv.length;
|
|
107
|
+
result.set(authTag, offset);
|
|
108
|
+
offset += authTag.length;
|
|
109
|
+
result.set(lengthArray, offset);
|
|
110
|
+
offset += lengthArray.length;
|
|
111
|
+
result.set(encrypted, offset);
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Parse encrypted message header
|
|
118
|
+
*/
|
|
119
|
+
public parseEncryptedMessage(
|
|
120
|
+
encryptionType: EciesEncryptionTypeEnum | undefined,
|
|
121
|
+
data: Uint8Array,
|
|
122
|
+
preambleSize: number = 0,
|
|
123
|
+
options?: { dataLength?: number },
|
|
124
|
+
): {
|
|
125
|
+
header: ISingleEncryptedParsedHeader;
|
|
126
|
+
data: Uint8Array;
|
|
127
|
+
remainder: Uint8Array;
|
|
128
|
+
} {
|
|
129
|
+
// Read encryption type from first byte after preamble
|
|
130
|
+
const actualEncryptionTypeByte = data[preambleSize];
|
|
131
|
+
let actualEncryptionType: EciesEncryptionTypeEnum;
|
|
132
|
+
const engine = getEciesI18nEngine();
|
|
133
|
+
|
|
134
|
+
switch (actualEncryptionTypeByte) {
|
|
135
|
+
case this.eciesConsts.ENCRYPTION_TYPE.SIMPLE:
|
|
136
|
+
actualEncryptionType = EciesEncryptionTypeEnum.Simple;
|
|
137
|
+
break;
|
|
138
|
+
case this.eciesConsts.ENCRYPTION_TYPE.SINGLE:
|
|
139
|
+
actualEncryptionType = EciesEncryptionTypeEnum.Single;
|
|
140
|
+
break;
|
|
141
|
+
case this.eciesConsts.ENCRYPTION_TYPE.MULTIPLE:
|
|
142
|
+
throw new Error(engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_MultipleEncryptionTypeNotSupportedInSingleRecipientMode));
|
|
143
|
+
default:
|
|
144
|
+
// convert the encryption type byte to hex
|
|
145
|
+
const encryptionTypeHex = actualEncryptionTypeByte.toString(16).padStart(2, '0');
|
|
146
|
+
throw new Error(engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_InvalidEncryptionTypeTemplate, { encryptionType: encryptionTypeHex }));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
encryptionType !== undefined &&
|
|
151
|
+
actualEncryptionType !== encryptionType
|
|
152
|
+
) {
|
|
153
|
+
const engine = getEciesI18nEngine();
|
|
154
|
+
throw new Error(
|
|
155
|
+
engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_EncryptionTypeMismatchTemplate, { encryptionType, actualEncryptionType }),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const includeLengthAndCrc =
|
|
160
|
+
actualEncryptionType === EciesEncryptionTypeEnum.Single;
|
|
161
|
+
const requiredSize = includeLengthAndCrc
|
|
162
|
+
? this.eciesConsts.SINGLE.FIXED_OVERHEAD_SIZE
|
|
163
|
+
: this.eciesConsts.SIMPLE.FIXED_OVERHEAD_SIZE;
|
|
164
|
+
|
|
165
|
+
if (data.length < requiredSize) {
|
|
166
|
+
const engine = getEciesI18nEngine();
|
|
167
|
+
throw new Error(
|
|
168
|
+
engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_DataTooShortTemplate, { requiredSize, dataLength: data.length }),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let offset = preambleSize;
|
|
173
|
+
const preamble = data.slice(0, preambleSize);
|
|
174
|
+
|
|
175
|
+
// Skip encryption type byte
|
|
176
|
+
offset += 1;
|
|
177
|
+
|
|
178
|
+
// Extract header components
|
|
179
|
+
const ephemeralPublicKey = data.slice(
|
|
180
|
+
offset,
|
|
181
|
+
offset + this.eciesConsts.PUBLIC_KEY_LENGTH,
|
|
182
|
+
);
|
|
183
|
+
offset += this.eciesConsts.PUBLIC_KEY_LENGTH;
|
|
184
|
+
|
|
185
|
+
const normalizedKey =
|
|
186
|
+
this.cryptoCore.normalizePublicKey(ephemeralPublicKey);
|
|
187
|
+
|
|
188
|
+
const iv = data.slice(offset, offset + this.eciesConsts.IV_SIZE);
|
|
189
|
+
offset += this.eciesConsts.IV_SIZE;
|
|
190
|
+
|
|
191
|
+
const authTag = data.slice(offset, offset + this.eciesConsts.AUTH_TAG_SIZE);
|
|
192
|
+
offset += this.eciesConsts.AUTH_TAG_SIZE;
|
|
193
|
+
// Extract length for single mode
|
|
194
|
+
const dataLengthArray = includeLengthAndCrc
|
|
195
|
+
? data.slice(offset, offset + this.eciesConsts.SINGLE.DATA_LENGTH_SIZE)
|
|
196
|
+
: new Uint8Array(0);
|
|
197
|
+
|
|
198
|
+
if (includeLengthAndCrc) {
|
|
199
|
+
offset += this.eciesConsts.SINGLE.DATA_LENGTH_SIZE;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const dataLength = includeLengthAndCrc
|
|
203
|
+
? Number(
|
|
204
|
+
new DataView(
|
|
205
|
+
dataLengthArray.buffer,
|
|
206
|
+
dataLengthArray.byteOffset,
|
|
207
|
+
dataLengthArray.byteLength,
|
|
208
|
+
).getBigUint64(0, false),
|
|
209
|
+
)
|
|
210
|
+
: options?.dataLength ?? -1;
|
|
211
|
+
|
|
212
|
+
if (
|
|
213
|
+
includeLengthAndCrc &&
|
|
214
|
+
options?.dataLength !== undefined &&
|
|
215
|
+
dataLength !== options.dataLength
|
|
216
|
+
) {
|
|
217
|
+
const engine = getEciesI18nEngine();
|
|
218
|
+
throw new Error(
|
|
219
|
+
engine.translate(EciesComponentId, EciesStringKey.Error_ECIESError_DataLengthMismatchTemplate, { expectedDataLength: dataLength, receivedDataLength: options.dataLength }),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// No CRC in Single encryption (AES-GCM provides authentication)
|
|
224
|
+
|
|
225
|
+
// For single mode, read all remaining data as encrypted data
|
|
226
|
+
// The dataLength represents the original message length, not encrypted length
|
|
227
|
+
const encryptedData = data.slice(offset);
|
|
228
|
+
|
|
229
|
+
const remainder = new Uint8Array(0);
|
|
230
|
+
|
|
231
|
+
// No CRC validation needed (AES-GCM provides authentication)
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
header: {
|
|
235
|
+
encryptionType: actualEncryptionType,
|
|
236
|
+
ephemeralPublicKey: normalizedKey,
|
|
237
|
+
iv,
|
|
238
|
+
authTag,
|
|
239
|
+
dataLength,
|
|
240
|
+
headerSize: includeLengthAndCrc
|
|
241
|
+
? this.eciesConsts.SINGLE.FIXED_OVERHEAD_SIZE
|
|
242
|
+
: this.eciesConsts.SIMPLE.FIXED_OVERHEAD_SIZE,
|
|
243
|
+
},
|
|
244
|
+
data: encryptedData,
|
|
245
|
+
remainder,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Decrypt with header
|
|
251
|
+
*/
|
|
252
|
+
public async decryptWithHeader(
|
|
253
|
+
encryptionType: EciesEncryptionTypeEnum | undefined,
|
|
254
|
+
privateKey: Uint8Array,
|
|
255
|
+
encryptedData: Uint8Array,
|
|
256
|
+
preambleSize: number = 0,
|
|
257
|
+
options?: { dataLength?: number },
|
|
258
|
+
): Promise<Uint8Array> {
|
|
259
|
+
const result = await this.decryptWithHeaderEx(
|
|
260
|
+
encryptionType,
|
|
261
|
+
privateKey,
|
|
262
|
+
encryptedData,
|
|
263
|
+
preambleSize,
|
|
264
|
+
options,
|
|
265
|
+
);
|
|
266
|
+
return result.decrypted;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Extended decrypt with header that returns additional info
|
|
271
|
+
*/
|
|
272
|
+
public async decryptWithHeaderEx(
|
|
273
|
+
encryptionType: EciesEncryptionTypeEnum | undefined,
|
|
274
|
+
privateKey: Uint8Array,
|
|
275
|
+
encryptedData: Uint8Array,
|
|
276
|
+
preambleSize: number = 0,
|
|
277
|
+
options?: { dataLength?: number },
|
|
278
|
+
): Promise<IDecryptionResult> {
|
|
279
|
+
const { data, header } = this.parseEncryptedMessage(
|
|
280
|
+
encryptionType,
|
|
281
|
+
encryptedData,
|
|
282
|
+
preambleSize,
|
|
283
|
+
options,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const decrypted = await this.decryptWithComponents(
|
|
287
|
+
privateKey,
|
|
288
|
+
header.ephemeralPublicKey,
|
|
289
|
+
header.iv,
|
|
290
|
+
header.authTag,
|
|
291
|
+
data,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
decrypted,
|
|
296
|
+
consumedBytes: preambleSize + header.headerSize + data.length,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Decrypt with individual components
|
|
302
|
+
*/
|
|
303
|
+
public async decryptWithComponents(
|
|
304
|
+
privateKey: Uint8Array,
|
|
305
|
+
ephemeralPublicKey: Uint8Array,
|
|
306
|
+
iv: Uint8Array,
|
|
307
|
+
authTag: Uint8Array,
|
|
308
|
+
encrypted: Uint8Array,
|
|
309
|
+
): Promise<Uint8Array> {
|
|
310
|
+
// Normalize ephemeral public key
|
|
311
|
+
const normalizedEphemeralKey =
|
|
312
|
+
this.cryptoCore.normalizePublicKey(ephemeralPublicKey);
|
|
313
|
+
|
|
314
|
+
// Compute shared secret
|
|
315
|
+
const sharedSecret = this.cryptoCore.computeSharedSecret(
|
|
316
|
+
privateKey,
|
|
317
|
+
normalizedEphemeralKey,
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
// Use first 32 bytes as symmetric key
|
|
321
|
+
const symKey = sharedSecret.slice(0, this.eciesConsts.SYMMETRIC.KEY_SIZE);
|
|
322
|
+
|
|
323
|
+
// Combine encrypted data with auth tag for AES-GCM
|
|
324
|
+
const encryptedWithTag = AESGCMService.combineEncryptedDataAndTag(
|
|
325
|
+
encrypted,
|
|
326
|
+
authTag,
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
// Decrypt
|
|
330
|
+
return await AESGCMService.decrypt(iv, encryptedWithTag, symKey, true, this.eciesConsts);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private arraysEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
334
|
+
if (a.length !== b.length) return false;
|
|
335
|
+
for (let i = 0; i < a.length; i++) {
|
|
336
|
+
if (a[i] !== b[i]) return false;
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { Wallet } from '@ethereumjs/wallet';
|
|
2
|
+
import { EciesEncryptionTypeEnum } from '../enumerations/ecies-encryption-type';
|
|
3
|
+
import { Pbkdf2ProfileEnum } from '../enumerations/pbkdf2-profile';
|
|
4
|
+
import { SecureString } from '../secure-string';
|
|
5
|
+
import { hexToUint8Array, uint8ArrayToHex } from '../utils';
|
|
6
|
+
import { AESGCMService } from './aes-gcm';
|
|
7
|
+
import { ECIESService } from './ecies/service';
|
|
8
|
+
import { Pbkdf2Service } from './pbkdf2';
|
|
9
|
+
import { EciesStringKey, PasswordLoginErrorTypeEnum } from '../enumerations';
|
|
10
|
+
import { buildReasonMap, CoreLanguageCode, PluginI18nEngine, PluginTranslatableGenericError, PluginTypedHandleableError } from '@digitaldefiance/i18n-lib';
|
|
11
|
+
import { IECIESConstants } from '../interfaces/ecies-consts';
|
|
12
|
+
import { Constants } from '../constants';
|
|
13
|
+
import { EciesComponentId } from '../i18n-setup';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export class PasswordLoginService<TLanguage extends CoreLanguageCode = CoreLanguageCode> {
|
|
17
|
+
protected readonly eciesService: ECIESService;
|
|
18
|
+
protected readonly pbkdf2Service: Pbkdf2Service<TLanguage>;
|
|
19
|
+
protected readonly engine: PluginI18nEngine<TLanguage>;
|
|
20
|
+
protected readonly eciesConsts: IECIESConstants;
|
|
21
|
+
public static readonly privateKeyStorageKey = 'encryptedPrivateKey';
|
|
22
|
+
public static readonly saltStorageKey = 'passwordLoginSalt';
|
|
23
|
+
public static readonly encryptedMnemonicStorageKey = 'encryptedMnemonic';
|
|
24
|
+
public static readonly profileStorageKey = 'pbkdf2Profile';
|
|
25
|
+
|
|
26
|
+
constructor(eciesService: ECIESService, pbkdf2Service: Pbkdf2Service<TLanguage>, engine: PluginI18nEngine<TLanguage>, eciesParams: IECIESConstants = Constants.ECIES) {
|
|
27
|
+
this.eciesService = eciesService;
|
|
28
|
+
this.pbkdf2Service = pbkdf2Service;
|
|
29
|
+
this.engine = engine;
|
|
30
|
+
this.eciesConsts = eciesParams;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async createPasswordLoginBundle(
|
|
34
|
+
mnemonic: SecureString,
|
|
35
|
+
password: SecureString,
|
|
36
|
+
profile: Pbkdf2ProfileEnum = Pbkdf2ProfileEnum.BROWSER_PASSWORD,
|
|
37
|
+
): Promise<{
|
|
38
|
+
salt: Uint8Array;
|
|
39
|
+
encryptedPrivateKey: Uint8Array;
|
|
40
|
+
encryptedMnemonic: Uint8Array;
|
|
41
|
+
wallet: Wallet;
|
|
42
|
+
}> {
|
|
43
|
+
const { wallet } = this.eciesService.walletAndSeedFromMnemonic(mnemonic);
|
|
44
|
+
|
|
45
|
+
const derivedKey =
|
|
46
|
+
await this.pbkdf2Service.deriveKeyFromPasswordWithProfileAsync(
|
|
47
|
+
password.valueAsUint8Array,
|
|
48
|
+
profile,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Encrypt private key with derived key
|
|
52
|
+
const privateKeyBytes = wallet.getPrivateKey();
|
|
53
|
+
const { encrypted, iv, tag } = await AESGCMService.encrypt(
|
|
54
|
+
privateKeyBytes,
|
|
55
|
+
derivedKey.hash,
|
|
56
|
+
true,
|
|
57
|
+
);
|
|
58
|
+
if (!tag) {
|
|
59
|
+
throw new PluginTranslatableGenericError(EciesComponentId, EciesStringKey.Error_Utils_EncryptionFailedNoAuthTag);
|
|
60
|
+
}
|
|
61
|
+
const encryptedPrivateKey = AESGCMService.combineIvTagAndEncryptedData(
|
|
62
|
+
iv,
|
|
63
|
+
encrypted,
|
|
64
|
+
tag,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// now use the public key to encrypt the mnemonic and store it
|
|
68
|
+
const encryptedMnemonic = await this.eciesService.encrypt(
|
|
69
|
+
EciesEncryptionTypeEnum.Simple,
|
|
70
|
+
[{ publicKey: wallet.getPublicKey() }],
|
|
71
|
+
mnemonic.valueAsUint8Array,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
salt: derivedKey.salt,
|
|
76
|
+
encryptedPrivateKey: encryptedPrivateKey,
|
|
77
|
+
encryptedMnemonic: encryptedMnemonic,
|
|
78
|
+
wallet,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set up password login by deriving a key from the password and using it to encrypt
|
|
84
|
+
* @param mnemonic The user's mnemonic
|
|
85
|
+
* @param password The user's password
|
|
86
|
+
*/
|
|
87
|
+
public async setupPasswordLoginLocalStorageBundle(
|
|
88
|
+
mnemonic: SecureString,
|
|
89
|
+
password: SecureString,
|
|
90
|
+
profile: Pbkdf2ProfileEnum = Pbkdf2ProfileEnum.BROWSER_PASSWORD,
|
|
91
|
+
): Promise<Wallet> {
|
|
92
|
+
const { salt, encryptedPrivateKey, encryptedMnemonic, wallet } =
|
|
93
|
+
await this.createPasswordLoginBundle(mnemonic, password, profile);
|
|
94
|
+
|
|
95
|
+
// store the salt and encrypted private key in local storage
|
|
96
|
+
try {
|
|
97
|
+
localStorage.setItem(
|
|
98
|
+
PasswordLoginService.saltStorageKey,
|
|
99
|
+
uint8ArrayToHex(salt),
|
|
100
|
+
);
|
|
101
|
+
localStorage.setItem(
|
|
102
|
+
PasswordLoginService.privateKeyStorageKey,
|
|
103
|
+
uint8ArrayToHex(encryptedPrivateKey),
|
|
104
|
+
);
|
|
105
|
+
localStorage.setItem(
|
|
106
|
+
PasswordLoginService.encryptedMnemonicStorageKey,
|
|
107
|
+
uint8ArrayToHex(encryptedMnemonic),
|
|
108
|
+
);
|
|
109
|
+
localStorage.setItem(
|
|
110
|
+
PasswordLoginService.profileStorageKey,
|
|
111
|
+
profile,
|
|
112
|
+
);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new PluginTypedHandleableError<typeof PasswordLoginErrorTypeEnum, EciesStringKey, TLanguage>(this.engine, EciesComponentId, PasswordLoginErrorTypeEnum.FailedToStoreLoginData, buildReasonMap<typeof PasswordLoginErrorTypeEnum, EciesStringKey>(PasswordLoginErrorTypeEnum, ['Error', 'PasswordLoginError']), new Error(), { cause: error instanceof Error ? error : undefined });
|
|
115
|
+
}
|
|
116
|
+
return wallet;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public async getWalletAndMnemonicFromEncryptedPasswordBundle(
|
|
120
|
+
salt: Uint8Array,
|
|
121
|
+
encryptedPrivateKey: Uint8Array,
|
|
122
|
+
encryptedMnemonic: Uint8Array,
|
|
123
|
+
password: SecureString,
|
|
124
|
+
profile: Pbkdf2ProfileEnum = Pbkdf2ProfileEnum.BROWSER_PASSWORD,
|
|
125
|
+
): Promise<{ wallet: Wallet; mnemonic: SecureString }> {
|
|
126
|
+
if (!salt || !encryptedPrivateKey || !encryptedMnemonic) {
|
|
127
|
+
throw new PluginTypedHandleableError<typeof PasswordLoginErrorTypeEnum, EciesStringKey, TLanguage>(this.engine, EciesComponentId, PasswordLoginErrorTypeEnum.PasswordLoginNotSetUp, buildReasonMap<typeof PasswordLoginErrorTypeEnum, EciesStringKey>(PasswordLoginErrorTypeEnum, ['Error', 'PasswordLoginError']), new Error());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const derivedKey =
|
|
131
|
+
await this.pbkdf2Service.deriveKeyFromPasswordWithProfileAsync(
|
|
132
|
+
password.valueAsUint8Array,
|
|
133
|
+
profile,
|
|
134
|
+
salt,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Decrypt private key with derived key
|
|
138
|
+
const { iv, encryptedDataWithTag } = AESGCMService.splitEncryptedData(
|
|
139
|
+
encryptedPrivateKey,
|
|
140
|
+
true,
|
|
141
|
+
this.eciesConsts,
|
|
142
|
+
);
|
|
143
|
+
const privateKeyBytes = await AESGCMService.decrypt(
|
|
144
|
+
iv,
|
|
145
|
+
encryptedDataWithTag,
|
|
146
|
+
derivedKey.hash,
|
|
147
|
+
true,
|
|
148
|
+
this.eciesConsts,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const wallet = Wallet.fromPrivateKey(privateKeyBytes);
|
|
152
|
+
|
|
153
|
+
// now decrypt the mnemonic
|
|
154
|
+
const decryptedMnemonic =
|
|
155
|
+
await this.eciesService.decryptSimpleOrSingleWithHeader(
|
|
156
|
+
true,
|
|
157
|
+
wallet.getPrivateKey(),
|
|
158
|
+
encryptedMnemonic,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return { wallet, mnemonic: new SecureString(decryptedMnemonic) };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Recover wallet and mnemonic from password
|
|
166
|
+
* @param password The user's password
|
|
167
|
+
* @returns The user's wallet and mnemonic
|
|
168
|
+
*/
|
|
169
|
+
public async getWalletAndMnemonicFromLocalStorageBundle(
|
|
170
|
+
password: SecureString,
|
|
171
|
+
): Promise<{ wallet: Wallet; mnemonic: SecureString }> {
|
|
172
|
+
const saltHex = localStorage.getItem(PasswordLoginService.saltStorageKey);
|
|
173
|
+
const encryptedPrivateKeyHex = localStorage.getItem(
|
|
174
|
+
PasswordLoginService.privateKeyStorageKey,
|
|
175
|
+
);
|
|
176
|
+
const encryptedMnemonicHex = localStorage.getItem(
|
|
177
|
+
PasswordLoginService.encryptedMnemonicStorageKey,
|
|
178
|
+
);
|
|
179
|
+
const profileStr = localStorage.getItem(PasswordLoginService.profileStorageKey);
|
|
180
|
+
|
|
181
|
+
if (
|
|
182
|
+
!saltHex ||
|
|
183
|
+
!encryptedPrivateKeyHex ||
|
|
184
|
+
!encryptedMnemonicHex ||
|
|
185
|
+
saltHex === '' ||
|
|
186
|
+
encryptedPrivateKeyHex === '' ||
|
|
187
|
+
encryptedMnemonicHex === ''
|
|
188
|
+
) {
|
|
189
|
+
throw new PluginTypedHandleableError<typeof PasswordLoginErrorTypeEnum, EciesStringKey, TLanguage>(this.engine, EciesComponentId, PasswordLoginErrorTypeEnum.PasswordLoginNotSetUp, buildReasonMap<typeof PasswordLoginErrorTypeEnum, EciesStringKey>(PasswordLoginErrorTypeEnum, ['Error', 'PasswordLoginError']), new Error());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const salt = hexToUint8Array(saltHex);
|
|
193
|
+
const encryptedPrivateKey = hexToUint8Array(encryptedPrivateKeyHex);
|
|
194
|
+
const encryptedMnemonic = hexToUint8Array(encryptedMnemonicHex);
|
|
195
|
+
const profile = (profileStr as Pbkdf2ProfileEnum) || Pbkdf2ProfileEnum.BROWSER_PASSWORD;
|
|
196
|
+
|
|
197
|
+
return await this.getWalletAndMnemonicFromEncryptedPasswordBundle(
|
|
198
|
+
salt,
|
|
199
|
+
encryptedPrivateKey,
|
|
200
|
+
encryptedMnemonic,
|
|
201
|
+
password,
|
|
202
|
+
profile,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @returns True if password login is set up (i.e. salt and encrypted private key are in local storage)
|
|
209
|
+
*/
|
|
210
|
+
public static isPasswordLoginSetup(): boolean {
|
|
211
|
+
const saltHex = localStorage.getItem(PasswordLoginService.saltStorageKey);
|
|
212
|
+
const encryptedPrivateKeyHex = localStorage.getItem(
|
|
213
|
+
PasswordLoginService.privateKeyStorageKey,
|
|
214
|
+
);
|
|
215
|
+
const encryptedMnemonicHex = localStorage.getItem(
|
|
216
|
+
PasswordLoginService.encryptedMnemonicStorageKey,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return !!(
|
|
220
|
+
saltHex &&
|
|
221
|
+
encryptedPrivateKeyHex &&
|
|
222
|
+
encryptedMnemonicHex &&
|
|
223
|
+
saltHex !== '' &&
|
|
224
|
+
encryptedPrivateKeyHex !== '' &&
|
|
225
|
+
encryptedMnemonicHex !== ''
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|