simple-crypto-utils 1.0.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.
- package/dist/crypto/decrypt.d.ts +1 -0
- package/dist/crypto/decrypt.d.ts.map +1 -0
- package/dist/crypto/decrypt.js +2 -0
- package/dist/crypto/decrypt.js.map +1 -0
- package/dist/crypto/encrypt.d.ts +1 -0
- package/dist/crypto/encrypt.d.ts.map +1 -0
- package/dist/crypto/encrypt.js +2 -0
- package/dist/crypto/encrypt.js.map +1 -0
- package/dist/crypto/signMessage.d.ts +1 -0
- package/dist/crypto/signMessage.d.ts.map +1 -0
- package/dist/crypto/signMessage.js +2 -0
- package/dist/crypto/signMessage.js.map +1 -0
- package/dist/crypto/verifyMessage.d.ts +1 -0
- package/dist/crypto/verifyMessage.d.ts.map +1 -0
- package/dist/crypto/verifyMessage.js +2 -0
- package/dist/crypto/verifyMessage.js.map +1 -0
- package/dist/crypto-asymmetricaly/file/decryptFile.d.ts +2 -0
- package/dist/crypto-asymmetricaly/file/decryptFile.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/file/decryptFile.js +93 -0
- package/dist/crypto-asymmetricaly/file/decryptFile.js.map +1 -0
- package/dist/crypto-asymmetricaly/file/encryptFile.d.ts +3 -0
- package/dist/crypto-asymmetricaly/file/encryptFile.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/file/encryptFile.js +110 -0
- package/dist/crypto-asymmetricaly/file/encryptFile.js.map +1 -0
- package/dist/crypto-asymmetricaly/index.d.ts +5 -0
- package/dist/crypto-asymmetricaly/index.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/index.js +5 -0
- package/dist/crypto-asymmetricaly/index.js.map +1 -0
- package/dist/crypto-asymmetricaly/keys/generateRSA.d.ts +5 -0
- package/dist/crypto-asymmetricaly/keys/generateRSA.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/keys/generateRSA.js +24 -0
- package/dist/crypto-asymmetricaly/keys/generateRSA.js.map +1 -0
- package/dist/crypto-asymmetricaly/rsa/unwrapKey.d.ts +1 -0
- package/dist/crypto-asymmetricaly/rsa/unwrapKey.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/rsa/unwrapKey.js +2 -0
- package/dist/crypto-asymmetricaly/rsa/unwrapKey.js.map +1 -0
- package/dist/crypto-asymmetricaly/rsa/wrapKey.d.ts +1 -0
- package/dist/crypto-asymmetricaly/rsa/wrapKey.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/rsa/wrapKey.js +2 -0
- package/dist/crypto-asymmetricaly/rsa/wrapKey.js.map +1 -0
- package/dist/crypto-asymmetricaly/string/decryptString.d.ts +2 -0
- package/dist/crypto-asymmetricaly/string/decryptString.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/string/decryptString.js +47 -0
- package/dist/crypto-asymmetricaly/string/decryptString.js.map +1 -0
- package/dist/crypto-asymmetricaly/string/encryptString.d.ts +7 -0
- package/dist/crypto-asymmetricaly/string/encryptString.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/string/encryptString.js +26 -0
- package/dist/crypto-asymmetricaly/string/encryptString.js.map +1 -0
- package/dist/crypto-asymmetricaly/string/generateAES.d.ts +5 -0
- package/dist/crypto-asymmetricaly/string/generateAES.d.ts.map +1 -0
- package/dist/crypto-asymmetricaly/string/generateAES.js +7 -0
- package/dist/crypto-asymmetricaly/string/generateAES.js.map +1 -0
- package/dist/crypto-symmetricaly/decrypt.d.ts +2 -0
- package/dist/crypto-symmetricaly/decrypt.d.ts.map +1 -0
- package/dist/crypto-symmetricaly/decrypt.js +14 -0
- package/dist/crypto-symmetricaly/decrypt.js.map +1 -0
- package/dist/crypto-symmetricaly/encrypt.d.ts +2 -0
- package/dist/crypto-symmetricaly/encrypt.d.ts.map +1 -0
- package/dist/crypto-symmetricaly/encrypt.js +16 -0
- package/dist/crypto-symmetricaly/encrypt.js.map +1 -0
- package/dist/crypto-symmetricaly/index.d.ts +3 -0
- package/dist/crypto-symmetricaly/index.d.ts.map +1 -0
- package/dist/crypto-symmetricaly/index.js +3 -0
- package/dist/crypto-symmetricaly/index.js.map +1 -0
- package/dist/crypto-symmetricaly/signMessage.d.ts +1 -0
- package/dist/crypto-symmetricaly/signMessage.d.ts.map +1 -0
- package/dist/crypto-symmetricaly/signMessage.js +2 -0
- package/dist/crypto-symmetricaly/signMessage.js.map +1 -0
- package/dist/crypto-symmetricaly/verifyMessage.d.ts +1 -0
- package/dist/crypto-symmetricaly/verifyMessage.d.ts.map +1 -0
- package/dist/crypto-symmetricaly/verifyMessage.js +2 -0
- package/dist/crypto-symmetricaly/verifyMessage.js.map +1 -0
- package/dist/hash/hash.d.ts +2 -0
- package/dist/hash/hash.d.ts.map +1 -0
- package/dist/hash/hash.js +7 -0
- package/dist/hash/hash.js.map +1 -0
- package/dist/hash/hashHmac.d.ts +2 -0
- package/dist/hash/hashHmac.d.ts.map +1 -0
- package/dist/hash/hashHmac.js +5 -0
- package/dist/hash/hashHmac.js.map +1 -0
- package/dist/hash/index.d.ts +4 -0
- package/dist/hash/index.d.ts.map +1 -0
- package/dist/hash/index.js +4 -0
- package/dist/hash/index.js.map +1 -0
- package/dist/hash/verifyHmac.d.ts +2 -0
- package/dist/hash/verifyHmac.d.ts.map +1 -0
- package/dist/hash/verifyHmac.js +10 -0
- package/dist/hash/verifyHmac.js.map +1 -0
- package/dist/index.cjs +1569 -0
- package/dist/index.d.cts +350 -0
- package/dist/index.d.ts +350 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1546 -0
- package/dist/index.js.map +1 -0
- package/dist/otp/TOTP.d.ts +10 -0
- package/dist/otp/TOTP.d.ts.map +1 -0
- package/dist/otp/TOTP.js +33 -0
- package/dist/otp/TOTP.js.map +1 -0
- package/dist/otp/generate.d.ts +2 -0
- package/dist/otp/generate.d.ts.map +1 -0
- package/dist/otp/generate.js +7 -0
- package/dist/otp/generate.js.map +1 -0
- package/dist/otp/generateOTP.d.ts +1 -0
- package/dist/otp/generateOTP.d.ts.map +1 -0
- package/dist/otp/generateOTP.js +2 -0
- package/dist/otp/generateOTP.js.map +1 -0
- package/dist/otp/hash.d.ts +2 -0
- package/dist/otp/hash.d.ts.map +1 -0
- package/dist/otp/hash.js +5 -0
- package/dist/otp/hash.js.map +1 -0
- package/dist/otp/index.d.ts +3 -0
- package/dist/otp/index.d.ts.map +1 -0
- package/dist/otp/index.js +3 -0
- package/dist/otp/index.js.map +1 -0
- package/dist/otp/verifyOTP.d.ts +1 -0
- package/dist/otp/verifyOTP.d.ts.map +1 -0
- package/dist/otp/verifyOTP.js +2 -0
- package/dist/otp/verifyOTP.js.map +1 -0
- package/dist/password/generate.d.ts +15 -0
- package/dist/password/generate.d.ts.map +1 -0
- package/dist/password/generate.js +44 -0
- package/dist/password/generate.js.map +1 -0
- package/dist/password/generatePassword.d.ts +2 -0
- package/dist/password/generatePassword.d.ts.map +1 -0
- package/dist/password/generatePassword.js +10 -0
- package/dist/password/generatePassword.js.map +1 -0
- package/dist/password/hash.d.ts +2 -0
- package/dist/password/hash.d.ts.map +1 -0
- package/dist/password/hash.js +12 -0
- package/dist/password/hash.js.map +1 -0
- package/dist/password/hashPassword.d.ts +1 -0
- package/dist/password/hashPassword.d.ts.map +1 -0
- package/dist/password/hashPassword.js +2 -0
- package/dist/password/hashPassword.js.map +1 -0
- package/dist/password/index.d.ts +4 -0
- package/dist/password/index.d.ts.map +1 -0
- package/dist/password/index.js +4 -0
- package/dist/password/index.js.map +1 -0
- package/dist/password/validatePassword.d.ts +1 -0
- package/dist/password/validatePassword.d.ts.map +1 -0
- package/dist/password/validatePassword.js +2 -0
- package/dist/password/validatePassword.js.map +1 -0
- package/dist/password/verify.d.ts +2 -0
- package/dist/password/verify.d.ts.map +1 -0
- package/dist/password/verify.js +33 -0
- package/dist/password/verify.js.map +1 -0
- package/dist/password/verifyPassword.d.ts +1 -0
- package/dist/password/verifyPassword.d.ts.map +1 -0
- package/dist/password/verifyPassword.js +2 -0
- package/dist/password/verifyPassword.js.map +1 -0
- package/dist/signature/index.d.ts +46 -0
- package/dist/signature/index.d.ts.map +1 -0
- package/dist/signature/index.js +51 -0
- package/dist/signature/index.js.map +1 -0
- package/dist/signature/serialize.d.ts +4 -0
- package/dist/signature/serialize.d.ts.map +1 -0
- package/dist/signature/serialize.js +33 -0
- package/dist/signature/serialize.js.map +1 -0
- package/dist/signature/sign.d.ts +3 -0
- package/dist/signature/sign.d.ts.map +1 -0
- package/dist/signature/sign.js +26 -0
- package/dist/signature/sign.js.map +1 -0
- package/dist/signature/types.d.ts +16 -0
- package/dist/signature/types.d.ts.map +1 -0
- package/dist/signature/types.js +2 -0
- package/dist/signature/types.js.map +1 -0
- package/dist/signature/verify.d.ts +3 -0
- package/dist/signature/verify.d.ts.map +1 -0
- package/dist/signature/verify.js +26 -0
- package/dist/signature/verify.js.map +1 -0
- package/dist/test-minimal.d.ts +1 -0
- package/dist/test-minimal.d.ts.map +1 -0
- package/dist/test-minimal.js +4 -0
- package/dist/test-minimal.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +33 -0
- package/dist/test.js.map +1 -0
- package/dist/token/generateRandomToken.d.ts +1 -0
- package/dist/token/generateRandomToken.d.ts.map +1 -0
- package/dist/token/generateRandomToken.js +2 -0
- package/dist/token/generateRandomToken.js.map +1 -0
- package/dist/token/generateUUID.d.ts +1 -0
- package/dist/token/generateUUID.d.ts.map +1 -0
- package/dist/token/generateUUID.js +2 -0
- package/dist/token/generateUUID.js.map +1 -0
- package/dist/token/hashToken.d.ts +1 -0
- package/dist/token/hashToken.d.ts.map +1 -0
- package/dist/token/hashToken.js +2 -0
- package/dist/token/hashToken.js.map +1 -0
- package/dist/utlis/constants.d.ts +4 -0
- package/dist/utlis/constants.d.ts.map +1 -0
- package/dist/utlis/constants.js +4 -0
- package/dist/utlis/constants.js.map +1 -0
- package/dist/utlis/entropyScore.d.ts +1 -0
- package/dist/utlis/entropyScore.d.ts.map +1 -0
- package/dist/utlis/entropyScore.js +2 -0
- package/dist/utlis/entropyScore.js.map +1 -0
- package/dist/utlis/generateRandomBytes.d.ts +2 -0
- package/dist/utlis/generateRandomBytes.d.ts.map +1 -0
- package/dist/utlis/generateRandomBytes.js +9 -0
- package/dist/utlis/generateRandomBytes.js.map +1 -0
- package/dist/utlis/maskSensitive.d.ts +1 -0
- package/dist/utlis/maskSensitive.d.ts.map +1 -0
- package/dist/utlis/maskSensitive.js +2 -0
- package/dist/utlis/maskSensitive.js.map +1 -0
- package/dist/utlis/safeCompare.d.ts +1 -0
- package/dist/utlis/safeCompare.d.ts.map +1 -0
- package/dist/utlis/safeCompare.js +2 -0
- package/dist/utlis/safeCompare.js.map +1 -0
- package/dist/uuid/generate.d.ts +2 -0
- package/dist/uuid/generate.d.ts.map +1 -0
- package/dist/uuid/generate.js +5 -0
- package/dist/uuid/generate.js.map +1 -0
- package/dist/uuid/hashToken.d.ts +1 -0
- package/dist/uuid/hashToken.d.ts.map +1 -0
- package/dist/uuid/hashToken.js +6 -0
- package/dist/uuid/hashToken.js.map +1 -0
- package/dist/uuid/index.d.ts +2 -0
- package/dist/uuid/index.d.ts.map +1 -0
- package/dist/uuid/index.js +2 -0
- package/dist/uuid/index.js.map +1 -0
- package/license +18 -0
- package/package.json +74 -0
- package/readme.md +697 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1569 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
LIBRARY_VERSION: () => LIBRARY_VERSION,
|
|
24
|
+
MAX_MESSAGE_AGE: () => MAX_MESSAGE_AGE,
|
|
25
|
+
MESSAGE_MAX_AGE_MS: () => MESSAGE_MAX_AGE_MS,
|
|
26
|
+
MINIMUM_PASSWORD_LENGTH: () => MINIMUM_PASSWORD_LENGTH,
|
|
27
|
+
MIN_PASSWORD_LENGTH: () => MIN_PASSWORD_LENGTH,
|
|
28
|
+
VERSION: () => VERSION,
|
|
29
|
+
decrypt: () => decrypt,
|
|
30
|
+
decryptFile: () => decryptFile,
|
|
31
|
+
decryptMessage: () => decryptMessage,
|
|
32
|
+
deriveAESKeyForDecryption: () => deriveAESKeyForDecryption,
|
|
33
|
+
deriveAESKeyForEncryption: () => deriveAESKeyForEncryption,
|
|
34
|
+
encrypt: () => encrypt,
|
|
35
|
+
encryptFileStreaming: () => encryptFileStreaming,
|
|
36
|
+
encryptMessage: () => encryptMessage,
|
|
37
|
+
hash: () => hash_exports,
|
|
38
|
+
keys: () => keys_exports,
|
|
39
|
+
otp: () => otp_exports,
|
|
40
|
+
password: () => password_exports,
|
|
41
|
+
signature: () => signature_exports,
|
|
42
|
+
uuid: () => uuid_exports,
|
|
43
|
+
validatePassword: () => validatePassword,
|
|
44
|
+
validatePrivateKey: () => validatePrivateKey,
|
|
45
|
+
validatePublicKey: () => validatePublicKey,
|
|
46
|
+
validateTimestamp: () => validateTimestamp,
|
|
47
|
+
validateVersion: () => validateVersion
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(index_exports);
|
|
50
|
+
|
|
51
|
+
// src/crypto/encrypt.ts
|
|
52
|
+
var import_fs = require("fs");
|
|
53
|
+
var import_promises = require("fs/promises");
|
|
54
|
+
var import_crypto = require("crypto");
|
|
55
|
+
var import_promises2 = require("stream/promises");
|
|
56
|
+
var import_os = require("os");
|
|
57
|
+
var import_path = require("path");
|
|
58
|
+
var VERSION = 1;
|
|
59
|
+
var MIN_PASSWORD_LENGTH = 12;
|
|
60
|
+
var MESSAGE_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
61
|
+
function validatePassword(password, strictMode = false) {
|
|
62
|
+
if (!password || password.length < MIN_PASSWORD_LENGTH) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Password must be at least ${MIN_PASSWORD_LENGTH} characters`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const hasUpperCase = /[A-Z]/.test(password);
|
|
68
|
+
const hasLowerCase = /[a-z]/.test(password);
|
|
69
|
+
const hasNumber = /[0-9]/.test(password);
|
|
70
|
+
const hasSpecial = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password);
|
|
71
|
+
const strength = [hasUpperCase, hasLowerCase, hasNumber, hasSpecial].filter(
|
|
72
|
+
Boolean
|
|
73
|
+
).length;
|
|
74
|
+
if (strictMode && strength < 3) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"Strict mode: password must contain uppercase, lowercase, numbers, and special characters"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (!strictMode && strength < 2) {
|
|
80
|
+
console.warn(
|
|
81
|
+
"\u26A0\uFE0F Weak password: consider using uppercase, lowercase, numbers, and special characters"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function validatePublicKey(keyStr, expectedType) {
|
|
86
|
+
try {
|
|
87
|
+
const keyBuffer = Buffer.from(keyStr, "base64");
|
|
88
|
+
const key = (0, import_crypto.createPublicKey)({
|
|
89
|
+
key: keyBuffer,
|
|
90
|
+
format: "der",
|
|
91
|
+
type: "spki"
|
|
92
|
+
});
|
|
93
|
+
if (key.asymmetricKeyType !== expectedType) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Expected ${expectedType} key, got ${key.asymmetricKeyType}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
throw new Error(`Invalid ${expectedType} public key: ${err.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function validatePrivateKey(keyStr, expectedType) {
|
|
103
|
+
try {
|
|
104
|
+
const keyBuffer = Buffer.from(keyStr, "base64");
|
|
105
|
+
const key = (0, import_crypto.createPrivateKey)({
|
|
106
|
+
key: keyBuffer,
|
|
107
|
+
format: "der",
|
|
108
|
+
type: "pkcs8"
|
|
109
|
+
});
|
|
110
|
+
if (key.asymmetricKeyType !== expectedType) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Expected ${expectedType} key, got ${key.asymmetricKeyType}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error(`Invalid ${expectedType} private key: ${err.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function secureDelete(filePath) {
|
|
120
|
+
try {
|
|
121
|
+
await (0, import_promises.unlink)(filePath);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.error(`\u26A0\uFE0F Failed to delete temp file ${filePath}:`, err.message);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function createTimestampBuffer() {
|
|
127
|
+
const timestamp = Date.now();
|
|
128
|
+
const buf = Buffer.alloc(8);
|
|
129
|
+
buf.writeBigUInt64BE(BigInt(timestamp), 0);
|
|
130
|
+
return buf;
|
|
131
|
+
}
|
|
132
|
+
async function encrypt(options, data, inputPath, outputPath) {
|
|
133
|
+
if (!data && !inputPath) {
|
|
134
|
+
throw new Error("No data to encrypt");
|
|
135
|
+
}
|
|
136
|
+
if (options.strictMode) {
|
|
137
|
+
console.log("\u{1F512} Strict mode enabled - all security checks active");
|
|
138
|
+
}
|
|
139
|
+
const isFile = inputPath && outputPath;
|
|
140
|
+
if (isFile) {
|
|
141
|
+
await encryptFile(options, inputPath, outputPath);
|
|
142
|
+
return { type: "file", outputPath };
|
|
143
|
+
} else {
|
|
144
|
+
let messageData;
|
|
145
|
+
if (Buffer.isBuffer(data)) {
|
|
146
|
+
messageData = data.toString("utf8");
|
|
147
|
+
} else if (typeof data === "string") {
|
|
148
|
+
messageData = data;
|
|
149
|
+
} else {
|
|
150
|
+
messageData = data;
|
|
151
|
+
}
|
|
152
|
+
const encrypted = encryptMessage(options, messageData);
|
|
153
|
+
return { type: "message", data: encrypted };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function encryptFile(options, inputPath, outputPath) {
|
|
157
|
+
await encryptFileStreaming(options, inputPath, outputPath);
|
|
158
|
+
}
|
|
159
|
+
async function encryptFileStreaming(options, inputPath, outputPath) {
|
|
160
|
+
const aesKey = (0, import_crypto.randomBytes)(32);
|
|
161
|
+
const iv = (0, import_crypto.randomBytes)(12);
|
|
162
|
+
const tempPath = (0, import_path.join)(
|
|
163
|
+
(0, import_os.tmpdir)(),
|
|
164
|
+
`temp-encrypt-${Date.now()}-${(0, import_crypto.randomBytes)(4).toString("hex")}.tmp`
|
|
165
|
+
);
|
|
166
|
+
try {
|
|
167
|
+
const tempStream = (0, import_fs.createWriteStream)(tempPath);
|
|
168
|
+
const inputStream = (0, import_fs.createReadStream)(inputPath);
|
|
169
|
+
let header;
|
|
170
|
+
switch (options.type) {
|
|
171
|
+
case "symmetric-password":
|
|
172
|
+
validatePassword(options.password, options.strictMode);
|
|
173
|
+
const salt = (0, import_crypto.randomBytes)(16);
|
|
174
|
+
const key = (0, import_crypto.scryptSync)(options.password, salt, 32);
|
|
175
|
+
const cipherSymmetric = (0, import_crypto.createCipheriv)("aes-256-gcm", key, iv);
|
|
176
|
+
await (0, import_promises2.pipeline)(inputStream, cipherSymmetric, tempStream);
|
|
177
|
+
const authTagSymmetric = cipherSymmetric.getAuthTag();
|
|
178
|
+
header = {
|
|
179
|
+
version: VERSION,
|
|
180
|
+
iv: iv.toString("base64"),
|
|
181
|
+
authTag: authTagSymmetric.toString("base64"),
|
|
182
|
+
salt: salt.toString("base64")
|
|
183
|
+
};
|
|
184
|
+
break;
|
|
185
|
+
case "sealEnvelope":
|
|
186
|
+
validatePublicKey(options.recipientPublicKey, "rsa");
|
|
187
|
+
const cipherSeal = (0, import_crypto.createCipheriv)("aes-256-gcm", aesKey, iv);
|
|
188
|
+
await (0, import_promises2.pipeline)(inputStream, cipherSeal, tempStream);
|
|
189
|
+
const authTagSeal = cipherSeal.getAuthTag();
|
|
190
|
+
const recipientPubKey = (0, import_crypto.createPublicKey)({
|
|
191
|
+
key: Buffer.from(options.recipientPublicKey, "base64"),
|
|
192
|
+
format: "der",
|
|
193
|
+
type: "spki"
|
|
194
|
+
});
|
|
195
|
+
const encryptedAESKey = (0, import_crypto.publicEncrypt)(
|
|
196
|
+
{
|
|
197
|
+
key: recipientPubKey,
|
|
198
|
+
// Use KeyObject instead of string
|
|
199
|
+
padding: import_crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
200
|
+
oaepHash: "sha256"
|
|
201
|
+
},
|
|
202
|
+
aesKey
|
|
203
|
+
);
|
|
204
|
+
header = {
|
|
205
|
+
version: VERSION,
|
|
206
|
+
encryptedKey: encryptedAESKey.toString("base64"),
|
|
207
|
+
iv: iv.toString("base64"),
|
|
208
|
+
authTag: authTagSeal.toString("base64")
|
|
209
|
+
};
|
|
210
|
+
break;
|
|
211
|
+
case "secure-channel":
|
|
212
|
+
validatePublicKey(options.recipientPublicKey, "x25519");
|
|
213
|
+
const ephemeralData = deriveAESKeyForEncryption(
|
|
214
|
+
options.recipientPublicKey
|
|
215
|
+
);
|
|
216
|
+
const cipherECDH = (0, import_crypto.createCipheriv)(
|
|
217
|
+
"aes-256-gcm",
|
|
218
|
+
ephemeralData.aesKey,
|
|
219
|
+
iv
|
|
220
|
+
);
|
|
221
|
+
await (0, import_promises2.pipeline)(inputStream, cipherECDH, tempStream);
|
|
222
|
+
const authTagECDH = cipherECDH.getAuthTag();
|
|
223
|
+
header = {
|
|
224
|
+
version: VERSION,
|
|
225
|
+
ephemeralPublicKey: ephemeralData.ephemeralPublicKey,
|
|
226
|
+
salt: ephemeralData.salt.toString("base64"),
|
|
227
|
+
iv: iv.toString("base64"),
|
|
228
|
+
authTag: authTagECDH.toString("base64")
|
|
229
|
+
};
|
|
230
|
+
if (options.includeTimestamp !== false) {
|
|
231
|
+
header.timestamp = Date.now();
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case "authenticated-channel":
|
|
235
|
+
validatePublicKey(options.recipientPublicKey, "x25519");
|
|
236
|
+
validatePrivateKey(options.senderPrivateKey, "ed25519");
|
|
237
|
+
const ephemeralAuthData = deriveAESKeyForEncryption(
|
|
238
|
+
options.recipientPublicKey
|
|
239
|
+
);
|
|
240
|
+
const cipherAuth = (0, import_crypto.createCipheriv)(
|
|
241
|
+
"aes-256-gcm",
|
|
242
|
+
ephemeralAuthData.aesKey,
|
|
243
|
+
iv
|
|
244
|
+
);
|
|
245
|
+
await (0, import_promises2.pipeline)(inputStream, cipherAuth, tempStream);
|
|
246
|
+
const authTagAuth = cipherAuth.getAuthTag();
|
|
247
|
+
const senderPrivKey = (0, import_crypto.createPrivateKey)({
|
|
248
|
+
key: Buffer.from(options.senderPrivateKey, "base64"),
|
|
249
|
+
format: "der",
|
|
250
|
+
type: "pkcs8"
|
|
251
|
+
});
|
|
252
|
+
const dataToSign = Buffer.concat([
|
|
253
|
+
Buffer.from(ephemeralAuthData.ephemeralPublicKey, "base64"),
|
|
254
|
+
iv,
|
|
255
|
+
authTagAuth
|
|
256
|
+
]);
|
|
257
|
+
const signature = (0, import_crypto.sign)(null, dataToSign, senderPrivKey);
|
|
258
|
+
header = {
|
|
259
|
+
version: VERSION,
|
|
260
|
+
ephemeralPublicKey: ephemeralAuthData.ephemeralPublicKey,
|
|
261
|
+
salt: ephemeralAuthData.salt.toString("base64"),
|
|
262
|
+
signature: signature.toString("base64"),
|
|
263
|
+
iv: iv.toString("base64"),
|
|
264
|
+
authTag: authTagAuth.toString("base64")
|
|
265
|
+
};
|
|
266
|
+
if (options.includeTimestamp !== false) {
|
|
267
|
+
header.timestamp = Date.now();
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
const headerJson = Buffer.from(JSON.stringify(header), "utf8");
|
|
272
|
+
const headerLengthBuf = Buffer.alloc(4);
|
|
273
|
+
headerLengthBuf.writeUInt32BE(headerJson.length, 0);
|
|
274
|
+
const outputStream = (0, import_fs.createWriteStream)(outputPath);
|
|
275
|
+
outputStream.write(headerLengthBuf);
|
|
276
|
+
outputStream.write(headerJson);
|
|
277
|
+
const tempReadStream = (0, import_fs.createReadStream)(tempPath);
|
|
278
|
+
await (0, import_promises2.pipeline)(tempReadStream, outputStream);
|
|
279
|
+
console.log("\u2705 File encrypted successfully");
|
|
280
|
+
} finally {
|
|
281
|
+
await secureDelete(tempPath);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function encryptMessage(options, data) {
|
|
285
|
+
const isString = typeof data === "string";
|
|
286
|
+
const stringData = isString ? data : JSON.stringify(data);
|
|
287
|
+
const versionByte = Buffer.from([VERSION]);
|
|
288
|
+
const typeFlag = Buffer.from([isString ? 0 : 1]);
|
|
289
|
+
const iv = (0, import_crypto.randomBytes)(12);
|
|
290
|
+
const aesKey = (0, import_crypto.randomBytes)(32);
|
|
291
|
+
switch (options.type) {
|
|
292
|
+
case "symmetric-password":
|
|
293
|
+
validatePassword(options.password, options.strictMode);
|
|
294
|
+
const salt = (0, import_crypto.randomBytes)(16);
|
|
295
|
+
const key = (0, import_crypto.scryptSync)(options.password, salt, 32);
|
|
296
|
+
const cipher = (0, import_crypto.createCipheriv)("aes-256-gcm", key, iv);
|
|
297
|
+
const encrypted = Buffer.concat([
|
|
298
|
+
cipher.update(stringData, "utf8"),
|
|
299
|
+
cipher.final()
|
|
300
|
+
]);
|
|
301
|
+
const tag = cipher.getAuthTag();
|
|
302
|
+
return Buffer.concat([
|
|
303
|
+
versionByte,
|
|
304
|
+
typeFlag,
|
|
305
|
+
salt,
|
|
306
|
+
iv,
|
|
307
|
+
tag,
|
|
308
|
+
encrypted
|
|
309
|
+
]).toString("hex");
|
|
310
|
+
case "sealEnvelope":
|
|
311
|
+
validatePublicKey(options.recipientPublicKey, "rsa");
|
|
312
|
+
const cipherSeal = (0, import_crypto.createCipheriv)("aes-256-gcm", aesKey, iv);
|
|
313
|
+
const encryptedSeal = Buffer.concat([
|
|
314
|
+
cipherSeal.update(stringData, "utf8"),
|
|
315
|
+
cipherSeal.final()
|
|
316
|
+
]);
|
|
317
|
+
const tagSeal = cipherSeal.getAuthTag();
|
|
318
|
+
const recipientPubKey = (0, import_crypto.createPublicKey)({
|
|
319
|
+
key: Buffer.from(options.recipientPublicKey, "base64"),
|
|
320
|
+
format: "der",
|
|
321
|
+
type: "spki"
|
|
322
|
+
});
|
|
323
|
+
const encryptedKey = (0, import_crypto.publicEncrypt)(
|
|
324
|
+
{
|
|
325
|
+
key: recipientPubKey,
|
|
326
|
+
// Use KeyObject instead of string
|
|
327
|
+
padding: import_crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
328
|
+
oaepHash: "sha256"
|
|
329
|
+
},
|
|
330
|
+
aesKey
|
|
331
|
+
);
|
|
332
|
+
const keyLengthBuf = Buffer.alloc(2);
|
|
333
|
+
keyLengthBuf.writeUInt16BE(encryptedKey.length, 0);
|
|
334
|
+
return Buffer.concat([
|
|
335
|
+
versionByte,
|
|
336
|
+
typeFlag,
|
|
337
|
+
keyLengthBuf,
|
|
338
|
+
encryptedKey,
|
|
339
|
+
iv,
|
|
340
|
+
tagSeal,
|
|
341
|
+
encryptedSeal
|
|
342
|
+
]).toString("hex");
|
|
343
|
+
case "secure-channel":
|
|
344
|
+
validatePublicKey(options.recipientPublicKey, "x25519");
|
|
345
|
+
const ephemeralData = deriveAESKeyForEncryption(
|
|
346
|
+
options.recipientPublicKey
|
|
347
|
+
);
|
|
348
|
+
let dataToEncrypt = stringData;
|
|
349
|
+
let hasTimestamp = false;
|
|
350
|
+
if (options.includeTimestamp !== false) {
|
|
351
|
+
const timestampBuf = createTimestampBuffer();
|
|
352
|
+
dataToEncrypt = Buffer.concat([
|
|
353
|
+
timestampBuf,
|
|
354
|
+
Buffer.from(stringData, "utf8")
|
|
355
|
+
]).toString("base64");
|
|
356
|
+
hasTimestamp = true;
|
|
357
|
+
}
|
|
358
|
+
const cipherECDH = (0, import_crypto.createCipheriv)(
|
|
359
|
+
"aes-256-gcm",
|
|
360
|
+
ephemeralData.aesKey,
|
|
361
|
+
iv
|
|
362
|
+
);
|
|
363
|
+
const encryptedECDH = Buffer.concat([
|
|
364
|
+
cipherECDH.update(dataToEncrypt, "utf8"),
|
|
365
|
+
cipherECDH.final()
|
|
366
|
+
]);
|
|
367
|
+
const tagECDH = cipherECDH.getAuthTag();
|
|
368
|
+
const ephemeralKeyBuffer = Buffer.from(
|
|
369
|
+
ephemeralData.ephemeralPublicKey,
|
|
370
|
+
"base64"
|
|
371
|
+
);
|
|
372
|
+
const ephemeralKeyLenBuf = Buffer.alloc(2);
|
|
373
|
+
ephemeralKeyLenBuf.writeUInt16BE(ephemeralKeyBuffer.length, 0);
|
|
374
|
+
const timestampFlag = Buffer.from([hasTimestamp ? 1 : 0]);
|
|
375
|
+
return Buffer.concat([
|
|
376
|
+
versionByte,
|
|
377
|
+
typeFlag,
|
|
378
|
+
timestampFlag,
|
|
379
|
+
ephemeralKeyLenBuf,
|
|
380
|
+
ephemeralKeyBuffer,
|
|
381
|
+
ephemeralData.salt,
|
|
382
|
+
iv,
|
|
383
|
+
tagECDH,
|
|
384
|
+
encryptedECDH
|
|
385
|
+
]).toString("hex");
|
|
386
|
+
case "authenticated-channel":
|
|
387
|
+
validatePublicKey(options.recipientPublicKey, "x25519");
|
|
388
|
+
validatePrivateKey(options.senderPrivateKey, "ed25519");
|
|
389
|
+
const ephemeralAuthData = deriveAESKeyForEncryption(
|
|
390
|
+
options.recipientPublicKey
|
|
391
|
+
);
|
|
392
|
+
let dataToEncryptAuth = stringData;
|
|
393
|
+
let hasTimestampAuth = false;
|
|
394
|
+
if (options.includeTimestamp !== false) {
|
|
395
|
+
const timestampBuf = createTimestampBuffer();
|
|
396
|
+
dataToEncryptAuth = Buffer.concat([
|
|
397
|
+
timestampBuf,
|
|
398
|
+
Buffer.from(stringData, "utf8")
|
|
399
|
+
]).toString("base64");
|
|
400
|
+
hasTimestampAuth = true;
|
|
401
|
+
}
|
|
402
|
+
const cipherAuth = (0, import_crypto.createCipheriv)(
|
|
403
|
+
"aes-256-gcm",
|
|
404
|
+
ephemeralAuthData.aesKey,
|
|
405
|
+
iv
|
|
406
|
+
);
|
|
407
|
+
const encryptedAuth = Buffer.concat([
|
|
408
|
+
cipherAuth.update(dataToEncryptAuth, "utf8"),
|
|
409
|
+
cipherAuth.final()
|
|
410
|
+
]);
|
|
411
|
+
const tagAuth = cipherAuth.getAuthTag();
|
|
412
|
+
const senderPrivKey = (0, import_crypto.createPrivateKey)({
|
|
413
|
+
key: Buffer.from(options.senderPrivateKey, "base64"),
|
|
414
|
+
format: "der",
|
|
415
|
+
type: "pkcs8"
|
|
416
|
+
});
|
|
417
|
+
const ephemeralKeyBufferAuth = Buffer.from(
|
|
418
|
+
ephemeralAuthData.ephemeralPublicKey,
|
|
419
|
+
"base64"
|
|
420
|
+
);
|
|
421
|
+
const dataToSign = Buffer.concat([ephemeralKeyBufferAuth, iv, tagAuth]);
|
|
422
|
+
const signature = (0, import_crypto.sign)(null, dataToSign, senderPrivKey);
|
|
423
|
+
const ephemeralKeyLenBufAuth = Buffer.alloc(2);
|
|
424
|
+
ephemeralKeyLenBufAuth.writeUInt16BE(ephemeralKeyBufferAuth.length, 0);
|
|
425
|
+
const signatureLenBuf = Buffer.alloc(2);
|
|
426
|
+
signatureLenBuf.writeUInt16BE(signature.length, 0);
|
|
427
|
+
const timestampFlagAuth = Buffer.from([hasTimestampAuth ? 1 : 0]);
|
|
428
|
+
return Buffer.concat([
|
|
429
|
+
versionByte,
|
|
430
|
+
typeFlag,
|
|
431
|
+
timestampFlagAuth,
|
|
432
|
+
ephemeralKeyLenBufAuth,
|
|
433
|
+
ephemeralKeyBufferAuth,
|
|
434
|
+
signatureLenBuf,
|
|
435
|
+
signature,
|
|
436
|
+
ephemeralAuthData.salt,
|
|
437
|
+
iv,
|
|
438
|
+
tagAuth,
|
|
439
|
+
encryptedAuth
|
|
440
|
+
]).toString("hex");
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function deriveAESKeyForEncryption(recipientPublicKeyStr) {
|
|
444
|
+
const { publicKey, privateKey } = (0, import_crypto.generateKeyPairSync)("x25519");
|
|
445
|
+
const recipientPublicKey = (0, import_crypto.createPublicKey)({
|
|
446
|
+
key: Buffer.from(recipientPublicKeyStr, "base64"),
|
|
447
|
+
format: "der",
|
|
448
|
+
type: "spki"
|
|
449
|
+
});
|
|
450
|
+
const salt = (0, import_crypto.randomBytes)(16);
|
|
451
|
+
const sharedSecret = (0, import_crypto.diffieHellman)({
|
|
452
|
+
privateKey,
|
|
453
|
+
publicKey: recipientPublicKey
|
|
454
|
+
});
|
|
455
|
+
const aesKey = Buffer.from(
|
|
456
|
+
(0, import_crypto.hkdfSync)("sha256", sharedSecret, salt, "secure-channel-aes-key", 32)
|
|
457
|
+
);
|
|
458
|
+
return {
|
|
459
|
+
aesKey,
|
|
460
|
+
ephemeralPublicKey: publicKey.export({ type: "spki", format: "der" }).toString("base64"),
|
|
461
|
+
ephemeralPrivateKey: privateKey,
|
|
462
|
+
salt
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/password/index.ts
|
|
467
|
+
var password_exports = {};
|
|
468
|
+
__export(password_exports, {
|
|
469
|
+
generatePassword: () => generatePassword,
|
|
470
|
+
hashPassword: () => hashPassword,
|
|
471
|
+
verifyPassword: () => verifyPassword
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// src/password/generate.ts
|
|
475
|
+
var import_crypto2 = require("crypto");
|
|
476
|
+
var charsetMap = {
|
|
477
|
+
letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
|
478
|
+
numbers: "0123456789",
|
|
479
|
+
symbols: "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
|
480
|
+
};
|
|
481
|
+
function generatePassword(lengthOrOptions) {
|
|
482
|
+
let length = 16;
|
|
483
|
+
let letters = true;
|
|
484
|
+
let numbers = true;
|
|
485
|
+
let symbols = true;
|
|
486
|
+
if (typeof lengthOrOptions === "number") {
|
|
487
|
+
length = lengthOrOptions;
|
|
488
|
+
} else if (typeof lengthOrOptions === "object") {
|
|
489
|
+
length = lengthOrOptions.length ?? 16;
|
|
490
|
+
letters = lengthOrOptions.letters ?? true;
|
|
491
|
+
numbers = lengthOrOptions.numbers ?? true;
|
|
492
|
+
symbols = lengthOrOptions.symbols ?? true;
|
|
493
|
+
}
|
|
494
|
+
let charset = "";
|
|
495
|
+
if (letters) charset += charsetMap.letters;
|
|
496
|
+
if (numbers) charset += charsetMap.numbers;
|
|
497
|
+
if (symbols) charset += charsetMap.symbols;
|
|
498
|
+
if (charset.length === 0) {
|
|
499
|
+
charset = Object.values(charsetMap).join("");
|
|
500
|
+
}
|
|
501
|
+
if (length < 1 || length > 1024) {
|
|
502
|
+
throw new Error("Length must be between 1 and 1024");
|
|
503
|
+
}
|
|
504
|
+
const bytes = (0, import_crypto2.randomBytes)(length);
|
|
505
|
+
const password = Array.from(
|
|
506
|
+
bytes,
|
|
507
|
+
(byte) => charset[byte % charset.length]
|
|
508
|
+
).join("");
|
|
509
|
+
return password;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/password/hash.ts
|
|
513
|
+
var import_crypto3 = require("crypto");
|
|
514
|
+
var import_util = require("util");
|
|
515
|
+
var scryptAsync = (0, import_util.promisify)(import_crypto3.scrypt);
|
|
516
|
+
async function hashPassword(password) {
|
|
517
|
+
const salt = (0, import_crypto3.randomBytes)(16);
|
|
518
|
+
const derivedKey = await scryptAsync(password, salt, 64);
|
|
519
|
+
const saltBase64 = salt.toString("base64");
|
|
520
|
+
const hashBase64 = derivedKey.toString("base64");
|
|
521
|
+
return `scrypt$16$${saltBase64}$${hashBase64}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/password/verify.ts
|
|
525
|
+
var import_crypto4 = require("crypto");
|
|
526
|
+
var import_util2 = require("util");
|
|
527
|
+
var scryptAsync2 = (0, import_util2.promisify)(import_crypto4.scrypt);
|
|
528
|
+
async function scryptTyped(password, salt, keylen) {
|
|
529
|
+
const result = await scryptAsync2(password, salt, keylen);
|
|
530
|
+
if (!result) throw new Error("Scrypt derivation failed");
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
async function verifyPassword(password, storedHash) {
|
|
534
|
+
const [method, saltLengthStr, saltBase64, hashBase64] = storedHash.split("$");
|
|
535
|
+
if (method !== "scrypt") throw new Error("Unsupported hash method");
|
|
536
|
+
if (!saltBase64 || !hashBase64 || !saltLengthStr)
|
|
537
|
+
throw new Error("Invalid stored hash format");
|
|
538
|
+
const salt = Buffer.from(saltBase64, "base64");
|
|
539
|
+
const derivedKey = await scryptTyped(password, salt, 64);
|
|
540
|
+
const hashBuffer = Buffer.from(hashBase64, "base64");
|
|
541
|
+
if (derivedKey.length !== hashBuffer.length) return false;
|
|
542
|
+
let diff = 0;
|
|
543
|
+
for (let i = 0; i < derivedKey.length; i++) {
|
|
544
|
+
diff |= derivedKey[i] ^ hashBuffer[i];
|
|
545
|
+
}
|
|
546
|
+
return diff === 0;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/uuid/index.ts
|
|
550
|
+
var uuid_exports = {};
|
|
551
|
+
__export(uuid_exports, {
|
|
552
|
+
generateUUID: () => generateUUID
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// src/uuid/generate.ts
|
|
556
|
+
var import_crypto5 = require("crypto");
|
|
557
|
+
function generateUUID() {
|
|
558
|
+
return (0, import_crypto5.randomUUID)();
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/signature/index.ts
|
|
562
|
+
var signature_exports = {};
|
|
563
|
+
__export(signature_exports, {
|
|
564
|
+
Signer: () => Signer,
|
|
565
|
+
default: () => Signer,
|
|
566
|
+
envelope: () => envelope,
|
|
567
|
+
openEnvelope: () => openEnvelope,
|
|
568
|
+
sign: () => sign2,
|
|
569
|
+
verify: () => verify
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// src/signature/sign.ts
|
|
573
|
+
var import_crypto6 = require("crypto");
|
|
574
|
+
|
|
575
|
+
// src/signature/serialize.ts
|
|
576
|
+
function canonicalStringify(obj) {
|
|
577
|
+
if (obj === null || typeof obj !== "object") {
|
|
578
|
+
return JSON.stringify(obj);
|
|
579
|
+
}
|
|
580
|
+
if (Array.isArray(obj)) {
|
|
581
|
+
return "[" + obj.map(canonicalStringify).join(",") + "]";
|
|
582
|
+
}
|
|
583
|
+
const keys = Object.keys(obj).sort();
|
|
584
|
+
const pairs = keys.map(
|
|
585
|
+
(key) => JSON.stringify(key) + ":" + canonicalStringify(obj[key])
|
|
586
|
+
);
|
|
587
|
+
return "{" + pairs.join(",") + "}";
|
|
588
|
+
}
|
|
589
|
+
function serialize(data, strategy, fields) {
|
|
590
|
+
switch (strategy) {
|
|
591
|
+
case "canonical":
|
|
592
|
+
return canonicalStringify(data);
|
|
593
|
+
case "raw":
|
|
594
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
595
|
+
case "selective":
|
|
596
|
+
if (!fields || fields.length === 0) {
|
|
597
|
+
throw new Error("Selective strategy requires fields parameter");
|
|
598
|
+
}
|
|
599
|
+
const selected = {};
|
|
600
|
+
for (const field of fields) {
|
|
601
|
+
if (field in data) {
|
|
602
|
+
selected[field] = data[field];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return canonicalStringify(selected);
|
|
606
|
+
default:
|
|
607
|
+
throw new Error(`Unknown strategy: ${strategy}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// src/signature/sign.ts
|
|
612
|
+
function parsePrivateKey(key) {
|
|
613
|
+
const keyObject = (0, import_crypto6.createPrivateKey)({
|
|
614
|
+
key: Buffer.from(key, "base64"),
|
|
615
|
+
format: "der",
|
|
616
|
+
type: "pkcs8"
|
|
617
|
+
});
|
|
618
|
+
if (keyObject.asymmetricKeyType !== "ed25519") {
|
|
619
|
+
throw new Error(`Expected ed25519 key, got ${keyObject.asymmetricKeyType}`);
|
|
620
|
+
}
|
|
621
|
+
return keyObject;
|
|
622
|
+
}
|
|
623
|
+
function sign2(data, privateKey, options) {
|
|
624
|
+
const keyObject = parsePrivateKey(privateKey);
|
|
625
|
+
const serialized = serialize(
|
|
626
|
+
data,
|
|
627
|
+
options?.strategy ?? "canonical",
|
|
628
|
+
options?.fields ?? []
|
|
629
|
+
);
|
|
630
|
+
return (0, import_crypto6.sign)(
|
|
631
|
+
null,
|
|
632
|
+
// ✅ Ed25519 requires null
|
|
633
|
+
Buffer.from(serialized),
|
|
634
|
+
keyObject
|
|
635
|
+
).toString(options?.encoding ?? "base64");
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/signature/verify.ts
|
|
639
|
+
var import_crypto7 = require("crypto");
|
|
640
|
+
function parsePublicKey(key) {
|
|
641
|
+
const keyObject = (0, import_crypto7.createPublicKey)({
|
|
642
|
+
key: Buffer.from(key, "base64"),
|
|
643
|
+
format: "der",
|
|
644
|
+
type: "spki"
|
|
645
|
+
});
|
|
646
|
+
if (keyObject.asymmetricKeyType !== "ed25519") {
|
|
647
|
+
throw new Error(`Expected ed25519 key, got ${keyObject.asymmetricKeyType}`);
|
|
648
|
+
}
|
|
649
|
+
return keyObject;
|
|
650
|
+
}
|
|
651
|
+
function verify(data, signature, publicKey, options) {
|
|
652
|
+
const keyObject = parsePublicKey(publicKey);
|
|
653
|
+
const serialized = serialize(
|
|
654
|
+
data,
|
|
655
|
+
options?.strategy ?? "canonical",
|
|
656
|
+
options?.fields ?? []
|
|
657
|
+
);
|
|
658
|
+
return (0, import_crypto7.verify)(
|
|
659
|
+
null,
|
|
660
|
+
// ✅ required for ed25519
|
|
661
|
+
Buffer.from(serialized),
|
|
662
|
+
keyObject,
|
|
663
|
+
Buffer.from(signature, options?.encoding ?? "base64")
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/signature/index.ts
|
|
668
|
+
var Signer = class _Signer {
|
|
669
|
+
/**
|
|
670
|
+
* Creates a new Signer instance with optional default options.
|
|
671
|
+
*
|
|
672
|
+
* @param defaultOptions - Partial default options to override
|
|
673
|
+
* serialization strategy, fields, algorithm, encoding, or preHash.
|
|
674
|
+
*/
|
|
675
|
+
constructor(defaultOptions) {
|
|
676
|
+
this.defaultOptions = {
|
|
677
|
+
strategy: defaultOptions?.strategy ?? "canonical",
|
|
678
|
+
fields: defaultOptions?.fields ?? [],
|
|
679
|
+
algorithm: defaultOptions?.algorithm ?? "SHA256",
|
|
680
|
+
encoding: defaultOptions?.encoding ?? "base64",
|
|
681
|
+
preHash: defaultOptions?.preHash ?? false
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Sign data with a private key (static method).
|
|
686
|
+
*
|
|
687
|
+
* @param data - The data to sign.
|
|
688
|
+
* @param privateKey - The private key to use for signing.
|
|
689
|
+
* @param options - Optional signing options.
|
|
690
|
+
* @returns The digital signature as a string.
|
|
691
|
+
*
|
|
692
|
+
* @example
|
|
693
|
+
* ```ts
|
|
694
|
+
* const signature = Signer.sign({ message: "Hello" }, privateKey);
|
|
695
|
+
* ```
|
|
696
|
+
*/
|
|
697
|
+
static sign(data, privateKey, options) {
|
|
698
|
+
const opts = {
|
|
699
|
+
strategy: options?.strategy ?? "canonical",
|
|
700
|
+
fields: options?.fields ?? [],
|
|
701
|
+
encoding: options?.encoding ?? "base64"
|
|
702
|
+
};
|
|
703
|
+
const signOpts = {
|
|
704
|
+
fields: opts.fields,
|
|
705
|
+
encoding: opts.encoding
|
|
706
|
+
};
|
|
707
|
+
if (opts.strategy === "canonical") {
|
|
708
|
+
signOpts.strategy = "canonical";
|
|
709
|
+
}
|
|
710
|
+
return sign2(data, privateKey, signOpts);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Verify a signature with a public key (static method).
|
|
714
|
+
*
|
|
715
|
+
* @param data - The original data.
|
|
716
|
+
* @param signature - The signature to verify.
|
|
717
|
+
* @param publicKey - The public key corresponding to the signer.
|
|
718
|
+
* @param options - Optional verification options.
|
|
719
|
+
* @returns `true` if the signature is valid, `false` otherwise.
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* ```ts
|
|
723
|
+
* const isValid = Signer.verify({ message: "Hello" }, signature, publicKey);
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
static verify(data, signature, publicKey, options) {
|
|
727
|
+
const opts = {
|
|
728
|
+
strategy: options?.strategy ?? "canonical",
|
|
729
|
+
fields: options?.fields ?? [],
|
|
730
|
+
encoding: options?.encoding ?? "base64"
|
|
731
|
+
};
|
|
732
|
+
const verifyOpts = {
|
|
733
|
+
fields: opts.fields,
|
|
734
|
+
encoding: opts.encoding
|
|
735
|
+
};
|
|
736
|
+
if (opts.strategy === "canonical") {
|
|
737
|
+
verifyOpts.strategy = "canonical";
|
|
738
|
+
}
|
|
739
|
+
return verify(data, signature, publicKey, verifyOpts);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Create a signed envelope containing the data and its signature (static method).
|
|
743
|
+
*
|
|
744
|
+
* @param data - The data to include in the envelope.
|
|
745
|
+
* @param privateKey - The private key for signing.
|
|
746
|
+
* @param options - Optional signing options.
|
|
747
|
+
* @returns An object containing `{ data, signature }`.
|
|
748
|
+
*
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* const envelope = Signer.envelope({ message: "Hello" }, privateKey);
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
static envelope(data, privateKey, options) {
|
|
755
|
+
return {
|
|
756
|
+
data,
|
|
757
|
+
signature: _Signer.sign(data, privateKey, options)
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Verify and extract data from a signed envelope (static method).
|
|
762
|
+
*
|
|
763
|
+
* @param envelope - The envelope object `{ data, signature }`.
|
|
764
|
+
* @param publicKey - The public key to verify the signature.
|
|
765
|
+
* @param options - Optional verification options.
|
|
766
|
+
* @returns An object `{ valid, data }` indicating whether the signature is valid.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* const result = Signer.openEnvelope(envelope, publicKey);
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
static openEnvelope(envelope2, publicKey, options) {
|
|
774
|
+
const valid = _Signer.verify(
|
|
775
|
+
envelope2.data,
|
|
776
|
+
envelope2.signature,
|
|
777
|
+
publicKey,
|
|
778
|
+
options
|
|
779
|
+
);
|
|
780
|
+
return { valid, data: envelope2.data };
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Sign data with a private key (instance method).
|
|
784
|
+
*
|
|
785
|
+
* @param data - The data to sign.
|
|
786
|
+
* @param privateKey - The private key to use for signing.
|
|
787
|
+
* @param options - Optional signing options.
|
|
788
|
+
* @returns The digital signature as a string.
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* const signer = new Signer();
|
|
793
|
+
* const signature = signer.sign({ message: "Hello" }, privateKey);
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
sign(data, privateKey, options) {
|
|
797
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
798
|
+
const signOpts = {
|
|
799
|
+
fields: opts.fields,
|
|
800
|
+
encoding: opts.encoding
|
|
801
|
+
};
|
|
802
|
+
if (opts.strategy === "canonical") {
|
|
803
|
+
signOpts.strategy = "canonical";
|
|
804
|
+
}
|
|
805
|
+
return sign2(data, privateKey, signOpts);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Verify a signature with a public key (instance method).
|
|
809
|
+
*
|
|
810
|
+
* @param data - The original data.
|
|
811
|
+
* @param signature - The signature to verify.
|
|
812
|
+
* @param publicKey - The public key corresponding to the signer.
|
|
813
|
+
* @param options - Optional verification options.
|
|
814
|
+
* @returns `true` if the signature is valid, `false` otherwise.
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```ts
|
|
818
|
+
* const isValid = signer.verify({ message: "Hello" }, signature, publicKey);
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
verify(data, signature, publicKey, options) {
|
|
822
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
823
|
+
const verifyOpts = {
|
|
824
|
+
fields: opts.fields,
|
|
825
|
+
encoding: opts.encoding
|
|
826
|
+
};
|
|
827
|
+
if (opts.strategy === "canonical") {
|
|
828
|
+
verifyOpts.strategy = "canonical";
|
|
829
|
+
}
|
|
830
|
+
return verify(data, signature, publicKey, verifyOpts);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Create a signed envelope containing the data and its signature (instance method).
|
|
834
|
+
*
|
|
835
|
+
* @param data - The data to include in the envelope.
|
|
836
|
+
* @param privateKey - The private key for signing.
|
|
837
|
+
* @param options - Optional signing options.
|
|
838
|
+
* @returns An object containing `{ data, signature }`.
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* ```ts
|
|
842
|
+
* const envelope = signer.envelope({ message: "Hello" }, privateKey);
|
|
843
|
+
* ```
|
|
844
|
+
*/
|
|
845
|
+
envelope(data, privateKey, options) {
|
|
846
|
+
return {
|
|
847
|
+
data,
|
|
848
|
+
signature: this.sign(data, privateKey, options)
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Verify and extract data from a signed envelope (instance method).
|
|
853
|
+
*
|
|
854
|
+
* @param envelope - The envelope object `{ data, signature }`.
|
|
855
|
+
* @param publicKey - The public key to verify the signature.
|
|
856
|
+
* @param options - Optional verification options.
|
|
857
|
+
* @returns An object `{ valid, data }` indicating whether the signature is valid.
|
|
858
|
+
*
|
|
859
|
+
* @example
|
|
860
|
+
* ```ts
|
|
861
|
+
* const result = signer.openEnvelope(envelope, publicKey);
|
|
862
|
+
* ```
|
|
863
|
+
*/
|
|
864
|
+
openEnvelope(envelope2, publicKey, options) {
|
|
865
|
+
const valid = this.verify(
|
|
866
|
+
envelope2.data,
|
|
867
|
+
envelope2.signature,
|
|
868
|
+
publicKey,
|
|
869
|
+
options
|
|
870
|
+
);
|
|
871
|
+
return { valid, data: envelope2.data };
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
var defaultSigner = new Signer();
|
|
875
|
+
var envelope = defaultSigner.envelope.bind(defaultSigner);
|
|
876
|
+
var openEnvelope = defaultSigner.openEnvelope.bind(defaultSigner);
|
|
877
|
+
|
|
878
|
+
// src/hash/index.ts
|
|
879
|
+
var hash_exports = {};
|
|
880
|
+
__export(hash_exports, {
|
|
881
|
+
hash: () => hash,
|
|
882
|
+
hashHmac: () => hashHmac,
|
|
883
|
+
verifyHmac: () => verifyHmac
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// src/hash/hash.ts
|
|
887
|
+
var import_crypto8 = require("crypto");
|
|
888
|
+
function hash(data) {
|
|
889
|
+
const hash2 = (0, import_crypto8.createHash)("sha256");
|
|
890
|
+
hash2.update(data);
|
|
891
|
+
return hash2.digest("hex");
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// src/hash/hashHmac.ts
|
|
895
|
+
var import_crypto9 = require("crypto");
|
|
896
|
+
function hashHmac(secret, data) {
|
|
897
|
+
return (0, import_crypto9.createHmac)("sha256", secret).update(data).digest("hex");
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/hash/verifyHmac.ts
|
|
901
|
+
var import_crypto10 = require("crypto");
|
|
902
|
+
function verifyHmac(secret, data, expectedHex) {
|
|
903
|
+
const actual = (0, import_crypto10.createHmac)("sha256", secret).update(data).digest();
|
|
904
|
+
const expected = Buffer.from(expectedHex, "hex");
|
|
905
|
+
if (actual.length !== expected.length) {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
return (0, import_crypto10.timingSafeEqual)(actual, expected);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/keys/index.ts
|
|
912
|
+
var keys_exports = {};
|
|
913
|
+
__export(keys_exports, {
|
|
914
|
+
Key: () => Key,
|
|
915
|
+
generateECDHKeyPair: () => generateECDHKeyPair,
|
|
916
|
+
generateRSAKeyPair: () => generateRSAKeyPair
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
// src/keys/rsa.ts
|
|
920
|
+
var import_crypto11 = require("crypto");
|
|
921
|
+
function generateRSAKeyPair() {
|
|
922
|
+
return new Promise((resolve, reject) => {
|
|
923
|
+
(0, import_crypto11.generateKeyPair)(
|
|
924
|
+
"rsa",
|
|
925
|
+
{
|
|
926
|
+
modulusLength: 2048,
|
|
927
|
+
publicKeyEncoding: {
|
|
928
|
+
type: "spki",
|
|
929
|
+
format: "der"
|
|
930
|
+
// Changed from "pem" to "der"
|
|
931
|
+
},
|
|
932
|
+
privateKeyEncoding: {
|
|
933
|
+
type: "pkcs8",
|
|
934
|
+
format: "der"
|
|
935
|
+
// Changed from "pem" to "der"
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
(err, publicKey, privateKey) => {
|
|
939
|
+
if (err) return reject(err);
|
|
940
|
+
resolve({
|
|
941
|
+
publicKey: publicKey.toString("base64"),
|
|
942
|
+
privateKey: privateKey.toString("base64")
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// src/keys/ed25519.ts
|
|
950
|
+
var import_crypto12 = require("crypto");
|
|
951
|
+
function generateEd25519KeyPair() {
|
|
952
|
+
const { publicKey, privateKey } = (0, import_crypto12.generateKeyPairSync)("ed25519", {
|
|
953
|
+
publicKeyEncoding: {
|
|
954
|
+
type: "spki",
|
|
955
|
+
format: "der"
|
|
956
|
+
},
|
|
957
|
+
privateKeyEncoding: {
|
|
958
|
+
type: "pkcs8",
|
|
959
|
+
format: "der"
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
return {
|
|
963
|
+
publicKey: publicKey.toString("base64"),
|
|
964
|
+
privateKey: privateKey.toString("base64")
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/keys/x25519.ts
|
|
969
|
+
var import_crypto13 = require("crypto");
|
|
970
|
+
function generateX25519KeyPair() {
|
|
971
|
+
const { publicKey, privateKey } = (0, import_crypto13.generateKeyPairSync)("x25519", {
|
|
972
|
+
publicKeyEncoding: {
|
|
973
|
+
type: "spki",
|
|
974
|
+
format: "der"
|
|
975
|
+
},
|
|
976
|
+
privateKeyEncoding: {
|
|
977
|
+
type: "pkcs8",
|
|
978
|
+
format: "der"
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
return {
|
|
982
|
+
publicKey: publicKey.toString("base64"),
|
|
983
|
+
privateKey: privateKey.toString("base64")
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/keys/authenticated.ts
|
|
988
|
+
function generateAuthenticatedKeySet() {
|
|
989
|
+
return {
|
|
990
|
+
encryption: generateX25519KeyPair(),
|
|
991
|
+
signing: generateEd25519KeyPair()
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/keys/ecdh.ts
|
|
996
|
+
var import_crypto14 = require("crypto");
|
|
997
|
+
function generateECDHKeyPair() {
|
|
998
|
+
const { publicKey, privateKey } = (0, import_crypto14.generateKeyPairSync)("x25519");
|
|
999
|
+
return {
|
|
1000
|
+
publicKey: publicKey.export({ type: "spki", format: "der" }).toString("base64"),
|
|
1001
|
+
privateKey: privateKey.export({ type: "pkcs8", format: "der" }).toString("base64")
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/keys/index.ts
|
|
1006
|
+
var Key = class _Key {
|
|
1007
|
+
/**
|
|
1008
|
+
* Generates a new Key instance for the specified `keyType`.
|
|
1009
|
+
*
|
|
1010
|
+
* @param key - The type of key to generate:
|
|
1011
|
+
* - `"seal"`: RSA key pair for encryption/signing
|
|
1012
|
+
* - `"sign"`: Ed25519 key pair for signing
|
|
1013
|
+
* - `"secure-channel"`: X25519 key pair for ECDH (secure channel)
|
|
1014
|
+
* - `"authenticated-channel"`: Combined X25519 + Ed25519 key pair
|
|
1015
|
+
*
|
|
1016
|
+
* @returns A Promise that resolves to a `Key` instance with the generated keys.
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```ts
|
|
1020
|
+
* import { Key } from "./key";
|
|
1021
|
+
*
|
|
1022
|
+
* async function main() {
|
|
1023
|
+
* const sealKey = await Key.generate("seal");
|
|
1024
|
+
* console.log(sealKey.publicKey);
|
|
1025
|
+
* console.log(sealKey.privateKey);
|
|
1026
|
+
*
|
|
1027
|
+
* const authKey = await Key.generate("authenticated-channel");
|
|
1028
|
+
* console.log(authKey.publicKey); // Encryption key
|
|
1029
|
+
* console.log(authKey.signingPublicKey); // Signing key
|
|
1030
|
+
* }
|
|
1031
|
+
*
|
|
1032
|
+
* main();
|
|
1033
|
+
* ```
|
|
1034
|
+
*/
|
|
1035
|
+
static async generate(key) {
|
|
1036
|
+
const k = new _Key();
|
|
1037
|
+
switch (key) {
|
|
1038
|
+
case "authenticated-channel": {
|
|
1039
|
+
const key2 = generateAuthenticatedKeySet();
|
|
1040
|
+
k.publicKey = key2.encryption.publicKey;
|
|
1041
|
+
k.privateKey = key2.encryption.privateKey;
|
|
1042
|
+
k.signingPublicKey = key2.signing.publicKey;
|
|
1043
|
+
k.signingPrivateKey = key2.signing.privateKey;
|
|
1044
|
+
break;
|
|
1045
|
+
}
|
|
1046
|
+
case "secure-channel": {
|
|
1047
|
+
const { publicKey, privateKey } = generateX25519KeyPair();
|
|
1048
|
+
k.publicKey = publicKey;
|
|
1049
|
+
k.privateKey = privateKey;
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
case "seal": {
|
|
1053
|
+
const { publicKey, privateKey } = await generateRSAKeyPair();
|
|
1054
|
+
k.publicKey = publicKey;
|
|
1055
|
+
k.privateKey = privateKey;
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
case "sign": {
|
|
1059
|
+
const { publicKey, privateKey } = generateEd25519KeyPair();
|
|
1060
|
+
k.publicKey = publicKey;
|
|
1061
|
+
k.privateKey = privateKey;
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
default:
|
|
1065
|
+
throw new Error(`Unknown key type: ${key}`);
|
|
1066
|
+
}
|
|
1067
|
+
return k;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// src/otp/index.ts
|
|
1072
|
+
var otp_exports = {};
|
|
1073
|
+
__export(otp_exports, {
|
|
1074
|
+
generateOTP: () => generateOTP,
|
|
1075
|
+
generateTOTP: () => generateTOTP
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// src/otp/totp.ts
|
|
1079
|
+
var import_crypto15 = require("crypto");
|
|
1080
|
+
function base32ToBuffer(base32) {
|
|
1081
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
1082
|
+
let bits = "";
|
|
1083
|
+
let bytes = [];
|
|
1084
|
+
base32 = base32.replace(/=+$/, "").toUpperCase();
|
|
1085
|
+
for (const char of base32) {
|
|
1086
|
+
const val = alphabet.indexOf(char);
|
|
1087
|
+
bits += val.toString(2).padStart(5, "0");
|
|
1088
|
+
}
|
|
1089
|
+
for (let i = 0; i + 8 <= bits.length; i += 8) {
|
|
1090
|
+
bytes.push(parseInt(bits.substring(i, i + 8), 2));
|
|
1091
|
+
}
|
|
1092
|
+
return Buffer.from(bytes);
|
|
1093
|
+
}
|
|
1094
|
+
function generateTOTP(secret, digits = 6, period = 30, timestamp = Date.now()) {
|
|
1095
|
+
const key = base32ToBuffer(secret);
|
|
1096
|
+
let counter = Math.floor(timestamp / 1e3 / period);
|
|
1097
|
+
const buffer = Buffer.alloc(8);
|
|
1098
|
+
for (let i = 7; i >= 0; i--) {
|
|
1099
|
+
buffer[i] = counter & 255;
|
|
1100
|
+
counter >>= 8;
|
|
1101
|
+
}
|
|
1102
|
+
const hmac = (0, import_crypto15.createHmac)("sha1", key).update(buffer).digest();
|
|
1103
|
+
const offset = hmac[hmac.length - 1] & 15;
|
|
1104
|
+
const code = (hmac[offset] & 127) << 24 | (hmac[offset + 1] & 255) << 16 | (hmac[offset + 2] & 255) << 8 | hmac[offset + 3] & 255;
|
|
1105
|
+
return (code % 10 ** digits).toString().padStart(digits, "0");
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// src/otp/otp.ts
|
|
1109
|
+
var import_crypto16 = require("crypto");
|
|
1110
|
+
function generateOTP(length = 6) {
|
|
1111
|
+
const max = 10 ** length;
|
|
1112
|
+
const randomNumber = parseInt((0, import_crypto16.randomBytes)(4).toString("hex"), 16) % max;
|
|
1113
|
+
return randomNumber.toString().padStart(length, "0");
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/crypto/decrypt.ts
|
|
1117
|
+
var import_fs2 = require("fs");
|
|
1118
|
+
var import_crypto17 = require("crypto");
|
|
1119
|
+
var import_promises3 = require("stream/promises");
|
|
1120
|
+
function validateTimestamp(timestamp, maxAge = MESSAGE_MAX_AGE_MS) {
|
|
1121
|
+
const now = Date.now();
|
|
1122
|
+
const age = now - timestamp;
|
|
1123
|
+
if (age < 0) {
|
|
1124
|
+
throw new Error(
|
|
1125
|
+
"Message timestamp is in the future - possible clock skew or attack"
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
if (age > maxAge) {
|
|
1129
|
+
throw new Error(
|
|
1130
|
+
`Message expired (age: ${Math.floor(age / 1e3)}s, max: ${Math.floor(
|
|
1131
|
+
maxAge / 1e3
|
|
1132
|
+
)}s) - possible replay attack`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
function validateVersion(version) {
|
|
1137
|
+
if (version !== VERSION) {
|
|
1138
|
+
throw new Error(
|
|
1139
|
+
`Unsupported format version: ${version} (expected: ${VERSION})`
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
async function decrypt(options, data, inputPath, outputPath) {
|
|
1144
|
+
if (!data && !inputPath) {
|
|
1145
|
+
throw new Error("No data to decrypt");
|
|
1146
|
+
}
|
|
1147
|
+
if (options.strictMode) {
|
|
1148
|
+
console.log("\u{1F512} Strict mode enabled - all security checks active");
|
|
1149
|
+
}
|
|
1150
|
+
const isFile = inputPath && outputPath;
|
|
1151
|
+
if (isFile) {
|
|
1152
|
+
await decryptFile(options, inputPath, outputPath);
|
|
1153
|
+
return { type: "file", outputPath };
|
|
1154
|
+
} else {
|
|
1155
|
+
const encryptedHex = typeof data === "string" ? data : data.toString("hex");
|
|
1156
|
+
const result = decryptMessage(options, encryptedHex);
|
|
1157
|
+
return {
|
|
1158
|
+
type: "message",
|
|
1159
|
+
data: result.data,
|
|
1160
|
+
metadata: result.metadata
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
async function decryptFile(options, inputPath, outputPath) {
|
|
1165
|
+
async function readFileHeader(filePath) {
|
|
1166
|
+
return new Promise((resolve, reject) => {
|
|
1167
|
+
const stream = (0, import_fs2.createReadStream)(filePath, { start: 0 });
|
|
1168
|
+
const chunks = [];
|
|
1169
|
+
let bytesRead = 0;
|
|
1170
|
+
let headerLength = 0;
|
|
1171
|
+
let headerBuffer = null;
|
|
1172
|
+
stream.on("data", (chunk) => {
|
|
1173
|
+
chunks.push(chunk);
|
|
1174
|
+
bytesRead += chunk.length;
|
|
1175
|
+
if (bytesRead >= 4 && headerLength === 0) {
|
|
1176
|
+
const allData = Buffer.concat(chunks);
|
|
1177
|
+
headerLength = allData.readUInt32BE(0);
|
|
1178
|
+
if (bytesRead >= 4 + headerLength) {
|
|
1179
|
+
headerBuffer = allData.subarray(4, 4 + headerLength);
|
|
1180
|
+
stream.destroy();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
stream.on("close", () => {
|
|
1185
|
+
if (!headerBuffer) return reject(new Error("Could not read header"));
|
|
1186
|
+
const headerJson = headerBuffer.toString("utf8");
|
|
1187
|
+
const header2 = JSON.parse(headerJson);
|
|
1188
|
+
validateVersion(header2.version);
|
|
1189
|
+
const encryptedDataOffset2 = 4 + headerLength;
|
|
1190
|
+
resolve({ header: header2, encryptedDataOffset: encryptedDataOffset2 });
|
|
1191
|
+
});
|
|
1192
|
+
stream.on("error", reject);
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
const { header, encryptedDataOffset } = await readFileHeader(inputPath);
|
|
1196
|
+
if (header.timestamp) {
|
|
1197
|
+
const shouldValidate = options.type === "secure-channel" && options.validateTimestamp !== false || options.type === "authenticated-channel" && options.validateTimestamp !== false || options.strictMode;
|
|
1198
|
+
if (shouldValidate) {
|
|
1199
|
+
validateTimestamp(header.timestamp);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
await decryptFileStreaming(
|
|
1203
|
+
options,
|
|
1204
|
+
inputPath,
|
|
1205
|
+
outputPath,
|
|
1206
|
+
header,
|
|
1207
|
+
encryptedDataOffset
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
async function decryptFileStreaming(options, inputPath, outputPath, header, dataOffset) {
|
|
1211
|
+
const iv = Buffer.from(header.iv, "base64");
|
|
1212
|
+
const authTag = Buffer.from(header.authTag, "base64");
|
|
1213
|
+
let decipher;
|
|
1214
|
+
switch (options.type) {
|
|
1215
|
+
case "symmetric-password":
|
|
1216
|
+
if (!options.password) {
|
|
1217
|
+
throw new Error("Password required for symmetric decryption");
|
|
1218
|
+
}
|
|
1219
|
+
if (!header.salt) {
|
|
1220
|
+
throw new Error("Salt missing from encrypted file");
|
|
1221
|
+
}
|
|
1222
|
+
const salt = Buffer.from(header.salt, "base64");
|
|
1223
|
+
const key = (0, import_crypto17.scryptSync)(options.password, salt, 32);
|
|
1224
|
+
decipher = (0, import_crypto17.createDecipheriv)("aes-256-gcm", key, iv);
|
|
1225
|
+
decipher.setAuthTag(authTag);
|
|
1226
|
+
break;
|
|
1227
|
+
case "openEnvelope":
|
|
1228
|
+
if (!options.recipientPrivateKey) {
|
|
1229
|
+
throw new Error("Recipient private key required for decryption");
|
|
1230
|
+
}
|
|
1231
|
+
if (!header.encryptedKey) {
|
|
1232
|
+
throw new Error("Encrypted key missing from file header");
|
|
1233
|
+
}
|
|
1234
|
+
const encryptedAESKey = Buffer.from(header.encryptedKey, "base64");
|
|
1235
|
+
const recipientPrivKey = (0, import_crypto17.createPrivateKey)({
|
|
1236
|
+
key: Buffer.from(options.recipientPrivateKey, "base64"),
|
|
1237
|
+
format: "der",
|
|
1238
|
+
type: "pkcs8"
|
|
1239
|
+
});
|
|
1240
|
+
const aesKey = (0, import_crypto17.privateDecrypt)(
|
|
1241
|
+
{
|
|
1242
|
+
key: recipientPrivKey,
|
|
1243
|
+
// Use KeyObject instead of string
|
|
1244
|
+
padding: import_crypto17.constants.RSA_PKCS1_OAEP_PADDING,
|
|
1245
|
+
oaepHash: "sha256"
|
|
1246
|
+
},
|
|
1247
|
+
encryptedAESKey
|
|
1248
|
+
);
|
|
1249
|
+
decipher = (0, import_crypto17.createDecipheriv)("aes-256-gcm", aesKey, iv);
|
|
1250
|
+
decipher.setAuthTag(authTag);
|
|
1251
|
+
break;
|
|
1252
|
+
case "secure-channel":
|
|
1253
|
+
if (!options.recipientPrivateKey) {
|
|
1254
|
+
throw new Error("Recipient private key required for secure channel");
|
|
1255
|
+
}
|
|
1256
|
+
if (!header.ephemeralPublicKey || !header.salt) {
|
|
1257
|
+
throw new Error("Ephemeral public key or salt missing from header");
|
|
1258
|
+
}
|
|
1259
|
+
const sharedSecret = deriveAESKeyForDecryption(
|
|
1260
|
+
options.recipientPrivateKey,
|
|
1261
|
+
header.ephemeralPublicKey,
|
|
1262
|
+
Buffer.from(header.salt, "base64")
|
|
1263
|
+
);
|
|
1264
|
+
decipher = (0, import_crypto17.createDecipheriv)("aes-256-gcm", sharedSecret, iv);
|
|
1265
|
+
decipher.setAuthTag(authTag);
|
|
1266
|
+
break;
|
|
1267
|
+
case "authenticated-channel":
|
|
1268
|
+
if (!options.recipientPrivateKey) {
|
|
1269
|
+
throw new Error(
|
|
1270
|
+
"Recipient private key required for authenticated channel"
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
if (!options.senderPublicKey) {
|
|
1274
|
+
throw new Error(
|
|
1275
|
+
"Sender public key required for signature verification"
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
if (!header.ephemeralPublicKey || !header.salt || !header.signature) {
|
|
1279
|
+
throw new Error(
|
|
1280
|
+
"Ephemeral key, salt, or signature missing from header"
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
const senderPubKey = (0, import_crypto17.createPublicKey)({
|
|
1284
|
+
key: Buffer.from(options.senderPublicKey, "base64"),
|
|
1285
|
+
format: "der",
|
|
1286
|
+
type: "spki"
|
|
1287
|
+
});
|
|
1288
|
+
const dataToVerify = Buffer.concat([
|
|
1289
|
+
Buffer.from(header.ephemeralPublicKey, "base64"),
|
|
1290
|
+
iv,
|
|
1291
|
+
authTag
|
|
1292
|
+
]);
|
|
1293
|
+
const signatureValid = (0, import_crypto17.verify)(
|
|
1294
|
+
null,
|
|
1295
|
+
dataToVerify,
|
|
1296
|
+
senderPubKey,
|
|
1297
|
+
Buffer.from(header.signature, "base64")
|
|
1298
|
+
);
|
|
1299
|
+
if (!signatureValid) {
|
|
1300
|
+
throw new Error("Invalid signature - message tampered or wrong sender");
|
|
1301
|
+
}
|
|
1302
|
+
const sharedSecretAuth = deriveAESKeyForDecryption(
|
|
1303
|
+
options.recipientPrivateKey,
|
|
1304
|
+
header.ephemeralPublicKey,
|
|
1305
|
+
Buffer.from(header.salt, "base64")
|
|
1306
|
+
);
|
|
1307
|
+
decipher = (0, import_crypto17.createDecipheriv)("aes-256-gcm", sharedSecretAuth, iv);
|
|
1308
|
+
decipher.setAuthTag(authTag);
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
const inputStream = (0, import_fs2.createReadStream)(inputPath, { start: dataOffset });
|
|
1312
|
+
const outputStream = (0, import_fs2.createWriteStream)(outputPath);
|
|
1313
|
+
await (0, import_promises3.pipeline)(inputStream, decipher, outputStream);
|
|
1314
|
+
console.log("\u2705 File decrypted successfully");
|
|
1315
|
+
}
|
|
1316
|
+
function decryptMessage(options, encryptedHex) {
|
|
1317
|
+
const buffer = Buffer.from(encryptedHex, "hex");
|
|
1318
|
+
let offset = 0;
|
|
1319
|
+
const version = buffer[offset];
|
|
1320
|
+
if (!version) throw new Error("Missing Version data, can't decrypt");
|
|
1321
|
+
validateVersion(version);
|
|
1322
|
+
offset += 1;
|
|
1323
|
+
const typeFlag = buffer[offset];
|
|
1324
|
+
const isString = typeFlag === 0;
|
|
1325
|
+
offset += 1;
|
|
1326
|
+
let decryptedData;
|
|
1327
|
+
let metadata = {};
|
|
1328
|
+
switch (options.type) {
|
|
1329
|
+
case "symmetric-password":
|
|
1330
|
+
if (!options.password) {
|
|
1331
|
+
throw new Error("Password required for symmetric decryption");
|
|
1332
|
+
}
|
|
1333
|
+
const salt = buffer.subarray(offset, offset + 16);
|
|
1334
|
+
offset += 16;
|
|
1335
|
+
const ivSymmetric = buffer.subarray(offset, offset + 12);
|
|
1336
|
+
offset += 12;
|
|
1337
|
+
const tagSymmetric = buffer.subarray(offset, offset + 16);
|
|
1338
|
+
offset += 16;
|
|
1339
|
+
const encryptedSymmetric = buffer.subarray(offset);
|
|
1340
|
+
const key = (0, import_crypto17.scryptSync)(options.password, salt, 32);
|
|
1341
|
+
const decipherSymmetric = (0, import_crypto17.createDecipheriv)(
|
|
1342
|
+
"aes-256-gcm",
|
|
1343
|
+
key,
|
|
1344
|
+
ivSymmetric
|
|
1345
|
+
);
|
|
1346
|
+
decipherSymmetric.setAuthTag(tagSymmetric);
|
|
1347
|
+
decryptedData = Buffer.concat([
|
|
1348
|
+
decipherSymmetric.update(encryptedSymmetric),
|
|
1349
|
+
decipherSymmetric.final()
|
|
1350
|
+
]).toString("utf8");
|
|
1351
|
+
break;
|
|
1352
|
+
case "openEnvelope":
|
|
1353
|
+
if (!options.recipientPrivateKey) {
|
|
1354
|
+
throw new Error("Recipient private key required for decryption");
|
|
1355
|
+
}
|
|
1356
|
+
const encryptedKeyLength = buffer.readUInt16BE(offset);
|
|
1357
|
+
offset += 2;
|
|
1358
|
+
const encryptedKey = buffer.subarray(offset, offset + encryptedKeyLength);
|
|
1359
|
+
offset += encryptedKeyLength;
|
|
1360
|
+
const ivRSA = buffer.subarray(offset, offset + 12);
|
|
1361
|
+
offset += 12;
|
|
1362
|
+
const tagRSA = buffer.subarray(offset, offset + 16);
|
|
1363
|
+
offset += 16;
|
|
1364
|
+
const encryptedRSA = buffer.subarray(offset);
|
|
1365
|
+
const recipientPrivKey = (0, import_crypto17.createPrivateKey)({
|
|
1366
|
+
key: Buffer.from(options.recipientPrivateKey, "base64"),
|
|
1367
|
+
format: "der",
|
|
1368
|
+
type: "pkcs8"
|
|
1369
|
+
});
|
|
1370
|
+
const aesKey = (0, import_crypto17.privateDecrypt)(
|
|
1371
|
+
{
|
|
1372
|
+
key: recipientPrivKey,
|
|
1373
|
+
// Use KeyObject instead of string
|
|
1374
|
+
padding: import_crypto17.constants.RSA_PKCS1_OAEP_PADDING,
|
|
1375
|
+
oaepHash: "sha256"
|
|
1376
|
+
},
|
|
1377
|
+
encryptedKey
|
|
1378
|
+
);
|
|
1379
|
+
const decipherRSA = (0, import_crypto17.createDecipheriv)("aes-256-gcm", aesKey, ivRSA);
|
|
1380
|
+
decipherRSA.setAuthTag(tagRSA);
|
|
1381
|
+
decryptedData = Buffer.concat([
|
|
1382
|
+
decipherRSA.update(encryptedRSA),
|
|
1383
|
+
decipherRSA.final()
|
|
1384
|
+
]).toString("utf8");
|
|
1385
|
+
break;
|
|
1386
|
+
case "secure-channel":
|
|
1387
|
+
if (!options.recipientPrivateKey) {
|
|
1388
|
+
throw new Error("Recipient private key required for secure channel");
|
|
1389
|
+
}
|
|
1390
|
+
const hasTimestamp = buffer[offset] === 1;
|
|
1391
|
+
offset += 1;
|
|
1392
|
+
const ephemeralKeyLength = buffer.readUInt16BE(offset);
|
|
1393
|
+
offset += 2;
|
|
1394
|
+
const ephemeralPublicKey = buffer.subarray(
|
|
1395
|
+
offset,
|
|
1396
|
+
offset + ephemeralKeyLength
|
|
1397
|
+
);
|
|
1398
|
+
offset += ephemeralKeyLength;
|
|
1399
|
+
const saltECDH = buffer.subarray(offset, offset + 16);
|
|
1400
|
+
offset += 16;
|
|
1401
|
+
const ivECDH = buffer.subarray(offset, offset + 12);
|
|
1402
|
+
offset += 12;
|
|
1403
|
+
const tagECDH = buffer.subarray(offset, offset + 16);
|
|
1404
|
+
offset += 16;
|
|
1405
|
+
const encryptedECDH = buffer.subarray(offset);
|
|
1406
|
+
const sharedSecret = deriveAESKeyForDecryption(
|
|
1407
|
+
options.recipientPrivateKey,
|
|
1408
|
+
ephemeralPublicKey.toString("base64"),
|
|
1409
|
+
saltECDH
|
|
1410
|
+
);
|
|
1411
|
+
const decipherECDH = (0, import_crypto17.createDecipheriv)(
|
|
1412
|
+
"aes-256-gcm",
|
|
1413
|
+
sharedSecret,
|
|
1414
|
+
ivECDH
|
|
1415
|
+
);
|
|
1416
|
+
decipherECDH.setAuthTag(tagECDH);
|
|
1417
|
+
let decryptedBuffer = Buffer.concat([
|
|
1418
|
+
decipherECDH.update(encryptedECDH),
|
|
1419
|
+
decipherECDH.final()
|
|
1420
|
+
]);
|
|
1421
|
+
if (hasTimestamp) {
|
|
1422
|
+
const decryptedBase64 = decryptedBuffer.toString("utf8");
|
|
1423
|
+
const fullBuffer = Buffer.from(decryptedBase64, "base64");
|
|
1424
|
+
const timestamp = Number(fullBuffer.readBigUInt64BE(0));
|
|
1425
|
+
metadata.timestamp = timestamp;
|
|
1426
|
+
if (options.validateTimestamp !== false || options.strictMode) {
|
|
1427
|
+
validateTimestamp(timestamp);
|
|
1428
|
+
}
|
|
1429
|
+
decryptedData = fullBuffer.subarray(8).toString("utf8");
|
|
1430
|
+
} else {
|
|
1431
|
+
decryptedData = decryptedBuffer.toString("utf8");
|
|
1432
|
+
}
|
|
1433
|
+
break;
|
|
1434
|
+
case "authenticated-channel":
|
|
1435
|
+
if (!options.recipientPrivateKey) {
|
|
1436
|
+
throw new Error(
|
|
1437
|
+
"Recipient private key required for authenticated channel"
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
if (!options.senderPublicKey) {
|
|
1441
|
+
throw new Error(
|
|
1442
|
+
"Sender public key required for signature verification"
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
const hasTimestampAuth = buffer[offset] === 1;
|
|
1446
|
+
offset += 1;
|
|
1447
|
+
const ephemeralKeyLengthAuth = buffer.readUInt16BE(offset);
|
|
1448
|
+
offset += 2;
|
|
1449
|
+
const ephemeralPublicKeyAuth = buffer.subarray(
|
|
1450
|
+
offset,
|
|
1451
|
+
offset + ephemeralKeyLengthAuth
|
|
1452
|
+
);
|
|
1453
|
+
offset += ephemeralKeyLengthAuth;
|
|
1454
|
+
const signatureLength = buffer.readUInt16BE(offset);
|
|
1455
|
+
offset += 2;
|
|
1456
|
+
const signature = buffer.subarray(offset, offset + signatureLength);
|
|
1457
|
+
offset += signatureLength;
|
|
1458
|
+
const saltAuth = buffer.subarray(offset, offset + 16);
|
|
1459
|
+
offset += 16;
|
|
1460
|
+
const ivAuth = buffer.subarray(offset, offset + 12);
|
|
1461
|
+
offset += 12;
|
|
1462
|
+
const tagAuth = buffer.subarray(offset, offset + 16);
|
|
1463
|
+
offset += 16;
|
|
1464
|
+
const encryptedAuth = buffer.subarray(offset);
|
|
1465
|
+
const senderPubKey = (0, import_crypto17.createPublicKey)({
|
|
1466
|
+
key: Buffer.from(options.senderPublicKey, "base64"),
|
|
1467
|
+
format: "der",
|
|
1468
|
+
type: "spki"
|
|
1469
|
+
});
|
|
1470
|
+
const dataToVerify = Buffer.concat([
|
|
1471
|
+
ephemeralPublicKeyAuth,
|
|
1472
|
+
ivAuth,
|
|
1473
|
+
tagAuth
|
|
1474
|
+
]);
|
|
1475
|
+
const signatureValid = (0, import_crypto17.verify)(
|
|
1476
|
+
null,
|
|
1477
|
+
dataToVerify,
|
|
1478
|
+
senderPubKey,
|
|
1479
|
+
signature
|
|
1480
|
+
);
|
|
1481
|
+
if (!signatureValid) {
|
|
1482
|
+
throw new Error("Invalid signature - message tampered or wrong sender");
|
|
1483
|
+
}
|
|
1484
|
+
metadata.authenticated = true;
|
|
1485
|
+
const sharedSecretAuth = deriveAESKeyForDecryption(
|
|
1486
|
+
options.recipientPrivateKey,
|
|
1487
|
+
ephemeralPublicKeyAuth.toString("base64"),
|
|
1488
|
+
saltAuth
|
|
1489
|
+
);
|
|
1490
|
+
const decipherAuth = (0, import_crypto17.createDecipheriv)(
|
|
1491
|
+
"aes-256-gcm",
|
|
1492
|
+
sharedSecretAuth,
|
|
1493
|
+
ivAuth
|
|
1494
|
+
);
|
|
1495
|
+
decipherAuth.setAuthTag(tagAuth);
|
|
1496
|
+
let decryptedBufferAuth = Buffer.concat([
|
|
1497
|
+
decipherAuth.update(encryptedAuth),
|
|
1498
|
+
decipherAuth.final()
|
|
1499
|
+
]);
|
|
1500
|
+
if (hasTimestampAuth) {
|
|
1501
|
+
const decryptedBase64Auth = decryptedBufferAuth.toString("utf8");
|
|
1502
|
+
const fullBufferAuth = Buffer.from(decryptedBase64Auth, "base64");
|
|
1503
|
+
const timestampAuth = Number(fullBufferAuth.readBigUInt64BE(0));
|
|
1504
|
+
metadata.timestamp = timestampAuth;
|
|
1505
|
+
if (options.validateTimestamp !== false || options.strictMode) {
|
|
1506
|
+
validateTimestamp(timestampAuth);
|
|
1507
|
+
}
|
|
1508
|
+
decryptedData = fullBufferAuth.subarray(8).toString("utf8");
|
|
1509
|
+
} else {
|
|
1510
|
+
decryptedData = decryptedBufferAuth.toString("utf8");
|
|
1511
|
+
}
|
|
1512
|
+
break;
|
|
1513
|
+
}
|
|
1514
|
+
const finalData = isString ? decryptedData : JSON.parse(decryptedData);
|
|
1515
|
+
return { data: finalData, metadata };
|
|
1516
|
+
}
|
|
1517
|
+
function deriveAESKeyForDecryption(recipientPrivateKeyStr, ephemeralPublicKeyStr, salt) {
|
|
1518
|
+
const recipientPrivateKey = (0, import_crypto17.createPrivateKey)({
|
|
1519
|
+
key: Buffer.from(recipientPrivateKeyStr, "base64"),
|
|
1520
|
+
format: "der",
|
|
1521
|
+
type: "pkcs8"
|
|
1522
|
+
});
|
|
1523
|
+
const ephemeralPublicKey = (0, import_crypto17.createPublicKey)({
|
|
1524
|
+
key: Buffer.from(ephemeralPublicKeyStr, "base64"),
|
|
1525
|
+
format: "der",
|
|
1526
|
+
type: "spki"
|
|
1527
|
+
});
|
|
1528
|
+
const sharedSecret = (0, import_crypto17.diffieHellman)({
|
|
1529
|
+
privateKey: recipientPrivateKey,
|
|
1530
|
+
publicKey: ephemeralPublicKey
|
|
1531
|
+
});
|
|
1532
|
+
const aesKey = Buffer.from(
|
|
1533
|
+
(0, import_crypto17.hkdfSync)("sha256", sharedSecret, salt, "secure-channel-aes-key", 32)
|
|
1534
|
+
);
|
|
1535
|
+
return aesKey;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// src/index.ts
|
|
1539
|
+
var LIBRARY_VERSION = VERSION;
|
|
1540
|
+
var MINIMUM_PASSWORD_LENGTH = MIN_PASSWORD_LENGTH;
|
|
1541
|
+
var MAX_MESSAGE_AGE = MESSAGE_MAX_AGE_MS;
|
|
1542
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1543
|
+
0 && (module.exports = {
|
|
1544
|
+
LIBRARY_VERSION,
|
|
1545
|
+
MAX_MESSAGE_AGE,
|
|
1546
|
+
MESSAGE_MAX_AGE_MS,
|
|
1547
|
+
MINIMUM_PASSWORD_LENGTH,
|
|
1548
|
+
MIN_PASSWORD_LENGTH,
|
|
1549
|
+
VERSION,
|
|
1550
|
+
decrypt,
|
|
1551
|
+
decryptFile,
|
|
1552
|
+
decryptMessage,
|
|
1553
|
+
deriveAESKeyForDecryption,
|
|
1554
|
+
deriveAESKeyForEncryption,
|
|
1555
|
+
encrypt,
|
|
1556
|
+
encryptFileStreaming,
|
|
1557
|
+
encryptMessage,
|
|
1558
|
+
hash,
|
|
1559
|
+
keys,
|
|
1560
|
+
otp,
|
|
1561
|
+
password,
|
|
1562
|
+
signature,
|
|
1563
|
+
uuid,
|
|
1564
|
+
validatePassword,
|
|
1565
|
+
validatePrivateKey,
|
|
1566
|
+
validatePublicKey,
|
|
1567
|
+
validateTimestamp,
|
|
1568
|
+
validateVersion
|
|
1569
|
+
});
|