@capsara/sdk 1.0.1
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/LICENSE +74 -0
- package/README.md +230 -0
- package/dist/builder/capsa-builder.d.ts +167 -0
- package/dist/builder/capsa-builder.d.ts.map +1 -0
- package/dist/builder/capsa-builder.js +489 -0
- package/dist/builder/capsa-builder.js.map +1 -0
- package/dist/client/capsara-client.d.ts +96 -0
- package/dist/client/capsara-client.d.ts.map +1 -0
- package/dist/client/capsara-client.js +266 -0
- package/dist/client/capsara-client.js.map +1 -0
- package/dist/errors/account-error.d.ts +73 -0
- package/dist/errors/account-error.d.ts.map +1 -0
- package/dist/errors/account-error.js +155 -0
- package/dist/errors/account-error.js.map +1 -0
- package/dist/errors/audit-error.d.ts +34 -0
- package/dist/errors/audit-error.d.ts.map +1 -0
- package/dist/errors/audit-error.js +93 -0
- package/dist/errors/audit-error.js.map +1 -0
- package/dist/errors/auth-error.d.ts +38 -0
- package/dist/errors/auth-error.d.ts.map +1 -0
- package/dist/errors/auth-error.js +87 -0
- package/dist/errors/auth-error.js.map +1 -0
- package/dist/errors/capsa-error.d.ts +64 -0
- package/dist/errors/capsa-error.d.ts.map +1 -0
- package/dist/errors/capsa-error.js +172 -0
- package/dist/errors/capsa-error.js.map +1 -0
- package/dist/errors/capsara-error.d.ts +52 -0
- package/dist/errors/capsara-error.d.ts.map +1 -0
- package/dist/errors/capsara-error.js +83 -0
- package/dist/errors/capsara-error.js.map +1 -0
- package/dist/errors/index.d.ts +8 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +7 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/capsa-cache.d.ts +49 -0
- package/dist/internal/capsa-cache.d.ts.map +1 -0
- package/dist/internal/capsa-cache.js +118 -0
- package/dist/internal/capsa-cache.js.map +1 -0
- package/dist/internal/config/http-client.d.ts +37 -0
- package/dist/internal/config/http-client.d.ts.map +1 -0
- package/dist/internal/config/http-client.js +63 -0
- package/dist/internal/config/http-client.js.map +1 -0
- package/dist/internal/config/retry-interceptor.d.ts +18 -0
- package/dist/internal/config/retry-interceptor.d.ts.map +1 -0
- package/dist/internal/config/retry-interceptor.js +103 -0
- package/dist/internal/config/retry-interceptor.js.map +1 -0
- package/dist/internal/crypto/compression.d.ts +15 -0
- package/dist/internal/crypto/compression.d.ts.map +1 -0
- package/dist/internal/crypto/compression.js +34 -0
- package/dist/internal/crypto/compression.js.map +1 -0
- package/dist/internal/crypto/key-generator.d.ts +23 -0
- package/dist/internal/crypto/key-generator.d.ts.map +1 -0
- package/dist/internal/crypto/key-generator.js +65 -0
- package/dist/internal/crypto/key-generator.js.map +1 -0
- package/dist/internal/crypto/primitives.d.ts +67 -0
- package/dist/internal/crypto/primitives.d.ts.map +1 -0
- package/dist/internal/crypto/primitives.js +230 -0
- package/dist/internal/crypto/primitives.js.map +1 -0
- package/dist/internal/crypto/signatures.d.ts +30 -0
- package/dist/internal/crypto/signatures.d.ts.map +1 -0
- package/dist/internal/crypto/signatures.js +153 -0
- package/dist/internal/crypto/signatures.js.map +1 -0
- package/dist/internal/decryptor/capsa-decryptor.d.ts +89 -0
- package/dist/internal/decryptor/capsa-decryptor.d.ts.map +1 -0
- package/dist/internal/decryptor/capsa-decryptor.js +263 -0
- package/dist/internal/decryptor/capsa-decryptor.js.map +1 -0
- package/dist/internal/http-factory.d.ts +78 -0
- package/dist/internal/http-factory.d.ts.map +1 -0
- package/dist/internal/http-factory.js +201 -0
- package/dist/internal/http-factory.js.map +1 -0
- package/dist/internal/index.d.ts +5 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +5 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/internal/retry-executor.d.ts +74 -0
- package/dist/internal/retry-executor.d.ts.map +1 -0
- package/dist/internal/retry-executor.js +204 -0
- package/dist/internal/retry-executor.js.map +1 -0
- package/dist/internal/services/account-service.d.ts +56 -0
- package/dist/internal/services/account-service.d.ts.map +1 -0
- package/dist/internal/services/account-service.js +114 -0
- package/dist/internal/services/account-service.js.map +1 -0
- package/dist/internal/services/audit-service.d.ts +25 -0
- package/dist/internal/services/audit-service.d.ts.map +1 -0
- package/dist/internal/services/audit-service.js +43 -0
- package/dist/internal/services/audit-service.js.map +1 -0
- package/dist/internal/services/auth-service.d.ts +44 -0
- package/dist/internal/services/auth-service.d.ts.map +1 -0
- package/dist/internal/services/auth-service.js +170 -0
- package/dist/internal/services/auth-service.js.map +1 -0
- package/dist/internal/services/capsa-service.d.ts +40 -0
- package/dist/internal/services/capsa-service.d.ts.map +1 -0
- package/dist/internal/services/capsa-service.js +82 -0
- package/dist/internal/services/capsa-service.js.map +1 -0
- package/dist/internal/services/download-service.d.ts +62 -0
- package/dist/internal/services/download-service.d.ts.map +1 -0
- package/dist/internal/services/download-service.js +114 -0
- package/dist/internal/services/download-service.js.map +1 -0
- package/dist/internal/services/key-service.d.ts +28 -0
- package/dist/internal/services/key-service.d.ts.map +1 -0
- package/dist/internal/services/key-service.js +45 -0
- package/dist/internal/services/key-service.js.map +1 -0
- package/dist/internal/services/limits-service.d.ts +30 -0
- package/dist/internal/services/limits-service.d.ts.map +1 -0
- package/dist/internal/services/limits-service.js +73 -0
- package/dist/internal/services/limits-service.js.map +1 -0
- package/dist/internal/services/upload-service.d.ts +61 -0
- package/dist/internal/services/upload-service.d.ts.map +1 -0
- package/dist/internal/services/upload-service.js +258 -0
- package/dist/internal/services/upload-service.js.map +1 -0
- package/dist/internal/types.d.ts +74 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +3 -0
- package/dist/internal/types.js.map +1 -0
- package/dist/internal/upload/multipart-builder.d.ts +57 -0
- package/dist/internal/upload/multipart-builder.d.ts.map +1 -0
- package/dist/internal/upload/multipart-builder.js +139 -0
- package/dist/internal/upload/multipart-builder.js.map +1 -0
- package/dist/internal/utils/id-generator.d.ts +8 -0
- package/dist/internal/utils/id-generator.d.ts.map +1 -0
- package/dist/internal/utils/id-generator.js +20 -0
- package/dist/internal/utils/id-generator.js.map +1 -0
- package/dist/internal/utils/mimetype-lookup.d.ts +8 -0
- package/dist/internal/utils/mimetype-lookup.d.ts.map +1 -0
- package/dist/internal/utils/mimetype-lookup.js +118 -0
- package/dist/internal/utils/mimetype-lookup.js.map +1 -0
- package/dist/internal/version.d.ts +20 -0
- package/dist/internal/version.d.ts.map +1 -0
- package/dist/internal/version.js +25 -0
- package/dist/internal/version.js.map +1 -0
- package/dist/types/index.d.ts +143 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +20 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/** AES-256-GCM, RSA-4096-OAEP-SHA256, and SHA-256 cryptographic primitives. */
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
/**
|
|
4
|
+
* @throws Error if key size is below minBits or cannot be determined
|
|
5
|
+
*/
|
|
6
|
+
function validateRSAKeySize(keyObject, minBits = 4096) {
|
|
7
|
+
const keyDetails = keyObject.asymmetricKeyDetails;
|
|
8
|
+
if (!keyDetails) {
|
|
9
|
+
throw new Error('Unable to extract key details');
|
|
10
|
+
}
|
|
11
|
+
const modulusLength = keyDetails.modulusLength;
|
|
12
|
+
if (!modulusLength || modulusLength < minBits) {
|
|
13
|
+
throw new Error(`RSA key size too small: expected at least ${minBits} bits, got ${modulusLength || 'unknown'} bits`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Generate a 256-bit AES master key. */
|
|
17
|
+
export function generateMasterKey() {
|
|
18
|
+
return crypto.randomBytes(32); // 256 bits
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate a 96-bit initialization vector for AES-GCM.
|
|
22
|
+
* Used for optional fields (subject, body, metadata) that need separate IVs.
|
|
23
|
+
* encryptAES() generates its own IV automatically for file content.
|
|
24
|
+
*/
|
|
25
|
+
export function generateIV() {
|
|
26
|
+
const iv = crypto.randomBytes(12); // 96 bits for GCM
|
|
27
|
+
return iv.toString('base64url');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Encrypt data using AES-256-GCM.
|
|
31
|
+
* @param key - 256-bit AES key (32 bytes)
|
|
32
|
+
* @returns Encrypted data with IV and authentication tag, all base64url-encoded
|
|
33
|
+
* @throws Error if key is not 32 bytes
|
|
34
|
+
*/
|
|
35
|
+
export function encryptAES(data, key) {
|
|
36
|
+
if (key.length !== 32) {
|
|
37
|
+
throw new Error(`Invalid key length: expected 32 bytes (AES-256), got ${key.length} bytes`);
|
|
38
|
+
}
|
|
39
|
+
const iv = crypto.randomBytes(12); // 96 bits for GCM
|
|
40
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
41
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
42
|
+
const authTag = cipher.getAuthTag();
|
|
43
|
+
return {
|
|
44
|
+
encryptedData: encrypted.toString('base64url'),
|
|
45
|
+
iv: iv.toString('base64url'),
|
|
46
|
+
authTag: authTag.toString('base64url'),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decrypt data using AES-256-GCM.
|
|
51
|
+
* @param encryptedData - Base64url-encoded ciphertext
|
|
52
|
+
* @param key - 256-bit AES key (32 bytes)
|
|
53
|
+
* @param iv - Base64url-encoded 12-byte IV
|
|
54
|
+
* @param authTag - Base64url-encoded 16-byte authentication tag
|
|
55
|
+
* @throws Error if key length is invalid, authentication fails, or decryption fails
|
|
56
|
+
*/
|
|
57
|
+
export function decryptAES(encryptedData, key, iv, authTag) {
|
|
58
|
+
if (key.length !== 32) {
|
|
59
|
+
throw new Error(`Invalid key length: expected 32 bytes (AES-256), got ${key.length} bytes`);
|
|
60
|
+
}
|
|
61
|
+
let ivBuffer;
|
|
62
|
+
try {
|
|
63
|
+
ivBuffer = Buffer.from(iv, 'base64url');
|
|
64
|
+
if (ivBuffer.length !== 12) {
|
|
65
|
+
throw new Error('Invalid IV length');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new Error('Invalid IV: must be 12-byte base64url-encoded value');
|
|
70
|
+
}
|
|
71
|
+
let authTagBuffer;
|
|
72
|
+
try {
|
|
73
|
+
authTagBuffer = Buffer.from(authTag, 'base64url');
|
|
74
|
+
if (authTagBuffer.length !== 16) {
|
|
75
|
+
throw new Error('Invalid auth tag length');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new Error('Invalid auth tag: must be 16-byte base64url-encoded value');
|
|
80
|
+
}
|
|
81
|
+
let encryptedBuffer;
|
|
82
|
+
try {
|
|
83
|
+
encryptedBuffer = Buffer.from(encryptedData, 'base64url');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error('Invalid encrypted data: must be base64url-encoded');
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, ivBuffer);
|
|
90
|
+
decipher.setAuthTag(authTagBuffer);
|
|
91
|
+
return Buffer.concat([
|
|
92
|
+
decipher.update(encryptedBuffer),
|
|
93
|
+
decipher.final(),
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Generic error message to avoid leaking information
|
|
98
|
+
throw new Error('AES-GCM decryption failed: authentication failed or corrupted ciphertext');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Encrypt master key for a party using their RSA-4096 public key.
|
|
103
|
+
* @param masterKey - 32-byte AES master key
|
|
104
|
+
* @param publicKeyPEM - Party's RSA public key in PEM format
|
|
105
|
+
* @returns Base64url-encoded encrypted master key
|
|
106
|
+
* @throws Error if masterKey is not 32 bytes or publicKeyPEM is invalid
|
|
107
|
+
*/
|
|
108
|
+
export function encryptMasterKeyForParty(masterKey, publicKeyPEM) {
|
|
109
|
+
if (masterKey.length !== 32) {
|
|
110
|
+
throw new Error(`Invalid master key length: expected 32 bytes, got ${masterKey.length} bytes`);
|
|
111
|
+
}
|
|
112
|
+
if (!publicKeyPEM || typeof publicKeyPEM !== 'string') {
|
|
113
|
+
throw new Error('publicKeyPEM must be a non-empty string');
|
|
114
|
+
}
|
|
115
|
+
if (!publicKeyPEM.includes('BEGIN PUBLIC KEY') && !publicKeyPEM.includes('BEGIN RSA PUBLIC KEY')) {
|
|
116
|
+
throw new Error('publicKeyPEM must be in PEM format');
|
|
117
|
+
}
|
|
118
|
+
let publicKeyObject;
|
|
119
|
+
try {
|
|
120
|
+
publicKeyObject = crypto.createPublicKey({
|
|
121
|
+
key: publicKeyPEM,
|
|
122
|
+
format: 'pem',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
127
|
+
throw new Error(`Invalid public key PEM: ${message}`);
|
|
128
|
+
}
|
|
129
|
+
validateRSAKeySize(publicKeyObject, 4096);
|
|
130
|
+
const encrypted = crypto.publicEncrypt({
|
|
131
|
+
key: publicKeyObject,
|
|
132
|
+
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
133
|
+
oaepHash: 'sha256',
|
|
134
|
+
}, masterKey);
|
|
135
|
+
return encrypted.toString('base64url');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Decrypt master key using party's RSA-4096 private key.
|
|
139
|
+
* @param encryptedKey - Base64url-encoded encrypted master key
|
|
140
|
+
* @param privateKeyPEM - Party's RSA private key in PEM format
|
|
141
|
+
* @returns Decrypted master key (32 bytes)
|
|
142
|
+
* @throws Error if privateKeyPEM is invalid or decryption fails
|
|
143
|
+
*/
|
|
144
|
+
export function decryptMasterKey(encryptedKey, privateKeyPEM) {
|
|
145
|
+
if (!privateKeyPEM || typeof privateKeyPEM !== 'string') {
|
|
146
|
+
throw new Error('privateKeyPEM must be a non-empty string');
|
|
147
|
+
}
|
|
148
|
+
if (!privateKeyPEM.includes('BEGIN PRIVATE KEY') && !privateKeyPEM.includes('BEGIN RSA PRIVATE KEY')) {
|
|
149
|
+
throw new Error('privateKeyPEM must be in PEM format');
|
|
150
|
+
}
|
|
151
|
+
let privateKeyObject;
|
|
152
|
+
try {
|
|
153
|
+
privateKeyObject = crypto.createPrivateKey({
|
|
154
|
+
key: privateKeyPEM,
|
|
155
|
+
format: 'pem',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
160
|
+
throw new Error(`Invalid private key PEM: ${message}`);
|
|
161
|
+
}
|
|
162
|
+
validateRSAKeySize(privateKeyObject, 4096);
|
|
163
|
+
try {
|
|
164
|
+
const encryptedBuffer = Buffer.from(encryptedKey, 'base64url');
|
|
165
|
+
return crypto.privateDecrypt({
|
|
166
|
+
key: privateKeyObject,
|
|
167
|
+
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
168
|
+
oaepHash: 'sha256',
|
|
169
|
+
}, encryptedBuffer);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Generic error to prevent padding oracle attacks via OpenSSL error detail leakage
|
|
173
|
+
throw new Error('RSA-OAEP decryption failed');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Encrypt data using AES-256-GCM, returning raw Buffers instead of base64url.
|
|
178
|
+
* Avoids base64 round-trip overhead for large file content.
|
|
179
|
+
* @param key - 256-bit AES key (32 bytes)
|
|
180
|
+
* @throws Error if key is not 32 bytes
|
|
181
|
+
*/
|
|
182
|
+
export function encryptAESRaw(data, key) {
|
|
183
|
+
if (key.length !== 32) {
|
|
184
|
+
throw new Error(`Invalid key length: expected 32 bytes (AES-256), got ${key.length} bytes`);
|
|
185
|
+
}
|
|
186
|
+
const iv = crypto.randomBytes(12); // 96 bits for GCM
|
|
187
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
188
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
189
|
+
const authTag = cipher.getAuthTag();
|
|
190
|
+
return { encryptedData: encrypted, iv, authTag };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Decrypt data using AES-256-GCM with raw Buffer inputs.
|
|
194
|
+
* Avoids base64 round-trip overhead for large file content.
|
|
195
|
+
* @param key - 256-bit AES key (32 bytes)
|
|
196
|
+
* @param iv - 12-byte initialization vector
|
|
197
|
+
* @param authTag - 16-byte authentication tag
|
|
198
|
+
* @throws Error if authentication fails or decryption fails
|
|
199
|
+
*/
|
|
200
|
+
export function decryptAESRaw(encryptedData, key, iv, authTag) {
|
|
201
|
+
if (key.length !== 32) {
|
|
202
|
+
throw new Error(`Invalid key length: expected 32 bytes (AES-256), got ${key.length} bytes`);
|
|
203
|
+
}
|
|
204
|
+
if (iv.length !== 12) {
|
|
205
|
+
throw new Error('Invalid IV: must be 12 bytes');
|
|
206
|
+
}
|
|
207
|
+
if (authTag.length !== 16) {
|
|
208
|
+
throw new Error('Invalid auth tag: must be 16 bytes');
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
212
|
+
decipher.setAuthTag(authTag);
|
|
213
|
+
return Buffer.concat([
|
|
214
|
+
decipher.update(encryptedData),
|
|
215
|
+
decipher.final(),
|
|
216
|
+
]);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
throw new Error('AES-GCM decryption failed: authentication failed or corrupted ciphertext');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Compute SHA-256 hash, returned as lowercase hex. */
|
|
223
|
+
export function computeHash(data) {
|
|
224
|
+
return crypto.createHash('sha256').update(data).digest('hex').toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
/** Generate a cryptographically secure random ID, returned as base64url. */
|
|
227
|
+
export function generateSecureId(length = 16) {
|
|
228
|
+
return crypto.randomBytes(length).toString('base64url');
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=primitives.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.js","sourceRoot":"","sources":["../../../src/internal/crypto/primitives.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** JWS RS256 (RSA-SHA256) signature creation and verification for capsas. */
|
|
2
|
+
import type { CapsaSignature, EncryptedFile } from '../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build canonical string for capsa signature.
|
|
5
|
+
* Format: packageId|version|totalSize|algorithm|hashes|(file)iv[s]|filenameIV[s]|structuredIV|subjectIV|bodyIV
|
|
6
|
+
* createdAt excluded because the server sets it when the capsa is stored.
|
|
7
|
+
* @throws Error if validation fails
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildCanonicalString(params: {
|
|
10
|
+
packageId: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
totalSize: number;
|
|
13
|
+
algorithm: string;
|
|
14
|
+
files: EncryptedFile[];
|
|
15
|
+
structuredIV?: string;
|
|
16
|
+
subjectIV?: string;
|
|
17
|
+
bodyIV?: string;
|
|
18
|
+
}): string;
|
|
19
|
+
/**
|
|
20
|
+
* Create JWS capsa signature using RSA-SHA256.
|
|
21
|
+
* @throws Error if key is invalid or signing fails
|
|
22
|
+
*/
|
|
23
|
+
export declare function createCapsaSignature(canonicalString: string, privateKeyPEM: string): CapsaSignature;
|
|
24
|
+
/**
|
|
25
|
+
* Verify capsa signature.
|
|
26
|
+
* @returns True if signature is valid, false if it doesn't match
|
|
27
|
+
* @throws Error if inputs are invalid or key errors occur
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifyCapsaSignature(signature: CapsaSignature, canonicalString: string, publicKeyPEM: string): boolean;
|
|
30
|
+
//# sourceMappingURL=signatures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signatures.d.ts","sourceRoot":"","sources":["../../../src/internal/crypto/signatures.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAG7E,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAc1E;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAgDT;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,GACpB,cAAc,CA8ChB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,cAAc,EACzB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CA2DT"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** JWS RS256 (RSA-SHA256) signature creation and verification for capsas. */
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
function validateEncryptedFile(file, index) {
|
|
4
|
+
if (!file.hash || typeof file.hash !== 'string') {
|
|
5
|
+
throw new Error(`File at index ${index} missing required field: hash`);
|
|
6
|
+
}
|
|
7
|
+
if (!file.iv || typeof file.iv !== 'string') {
|
|
8
|
+
throw new Error(`File at index ${index} missing required field: iv`);
|
|
9
|
+
}
|
|
10
|
+
if (!file.filenameIV || typeof file.filenameIV !== 'string') {
|
|
11
|
+
throw new Error(`File at index ${index} missing required field: filenameIV`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build canonical string for capsa signature.
|
|
16
|
+
* Format: packageId|version|totalSize|algorithm|hashes|(file)iv[s]|filenameIV[s]|structuredIV|subjectIV|bodyIV
|
|
17
|
+
* createdAt excluded because the server sets it when the capsa is stored.
|
|
18
|
+
* @throws Error if validation fails
|
|
19
|
+
*/
|
|
20
|
+
export function buildCanonicalString(params) {
|
|
21
|
+
if (!params.packageId || typeof params.packageId !== 'string') {
|
|
22
|
+
throw new Error('Invalid canonicalString: packageId must be a non-empty string');
|
|
23
|
+
}
|
|
24
|
+
if (typeof params.totalSize !== 'number' || params.totalSize < 0) {
|
|
25
|
+
throw new Error('Invalid canonicalString: totalSize must be a non-negative number');
|
|
26
|
+
}
|
|
27
|
+
if (!params.algorithm || typeof params.algorithm !== 'string') {
|
|
28
|
+
throw new Error('Invalid canonicalString: algorithm must be a non-empty string');
|
|
29
|
+
}
|
|
30
|
+
if (!Array.isArray(params.files)) {
|
|
31
|
+
throw new Error('Invalid canonicalString: files must be an array');
|
|
32
|
+
}
|
|
33
|
+
params.files.forEach((file, index) => validateEncryptedFile(file, index));
|
|
34
|
+
const version = params.version || '1.0.0';
|
|
35
|
+
const canonicalParts = [
|
|
36
|
+
params.packageId,
|
|
37
|
+
version,
|
|
38
|
+
String(params.totalSize), // Use String() instead of .toString()
|
|
39
|
+
params.algorithm,
|
|
40
|
+
];
|
|
41
|
+
// Preserve file order - DO NOT SORT (for deterministic signatures)
|
|
42
|
+
if (params.files.length > 0) {
|
|
43
|
+
const hashes = params.files.map((f) => f.hash);
|
|
44
|
+
const ivs = params.files.map((f) => f.iv);
|
|
45
|
+
const filenameIVs = params.files.map((f) => f.filenameIV);
|
|
46
|
+
canonicalParts.push(...hashes);
|
|
47
|
+
canonicalParts.push(...ivs);
|
|
48
|
+
canonicalParts.push(...filenameIVs);
|
|
49
|
+
}
|
|
50
|
+
// Skip empty/undefined optional IVs
|
|
51
|
+
if (params.structuredIV) {
|
|
52
|
+
canonicalParts.push(params.structuredIV);
|
|
53
|
+
}
|
|
54
|
+
if (params.subjectIV) {
|
|
55
|
+
canonicalParts.push(params.subjectIV);
|
|
56
|
+
}
|
|
57
|
+
if (params.bodyIV) {
|
|
58
|
+
canonicalParts.push(params.bodyIV);
|
|
59
|
+
}
|
|
60
|
+
return canonicalParts.join('|');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create JWS capsa signature using RSA-SHA256.
|
|
64
|
+
* @throws Error if key is invalid or signing fails
|
|
65
|
+
*/
|
|
66
|
+
export function createCapsaSignature(canonicalString, privateKeyPEM) {
|
|
67
|
+
if (!canonicalString || typeof canonicalString !== 'string') {
|
|
68
|
+
throw new Error('canonicalString must be a non-empty string');
|
|
69
|
+
}
|
|
70
|
+
if (!privateKeyPEM || typeof privateKeyPEM !== 'string') {
|
|
71
|
+
throw new Error('privateKeyPEM must be a non-empty string');
|
|
72
|
+
}
|
|
73
|
+
const joseHeader = {
|
|
74
|
+
alg: 'RS256',
|
|
75
|
+
typ: 'JWT',
|
|
76
|
+
};
|
|
77
|
+
const protectedHeader = Buffer.from(JSON.stringify(joseHeader)).toString('base64url');
|
|
78
|
+
const payload = Buffer.from(canonicalString, 'utf-8').toString('base64url');
|
|
79
|
+
const signingInput = `${protectedHeader}.${payload}`;
|
|
80
|
+
let privateKeyObject;
|
|
81
|
+
try {
|
|
82
|
+
privateKeyObject = crypto.createPrivateKey({
|
|
83
|
+
key: privateKeyPEM,
|
|
84
|
+
format: 'pem',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error(`Invalid private key: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
89
|
+
}
|
|
90
|
+
const signatureBuffer = crypto.sign('sha256', Buffer.from(signingInput, 'utf-8'), privateKeyObject);
|
|
91
|
+
const signature = signatureBuffer.toString('base64url');
|
|
92
|
+
return {
|
|
93
|
+
algorithm: 'RS256',
|
|
94
|
+
protected: protectedHeader,
|
|
95
|
+
payload,
|
|
96
|
+
signature,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Verify capsa signature.
|
|
101
|
+
* @returns True if signature is valid, false if it doesn't match
|
|
102
|
+
* @throws Error if inputs are invalid or key errors occur
|
|
103
|
+
*/
|
|
104
|
+
export function verifyCapsaSignature(signature, canonicalString, publicKeyPEM) {
|
|
105
|
+
if (!signature || typeof signature !== 'object') {
|
|
106
|
+
throw new Error('signature must be a CapsaSignature object');
|
|
107
|
+
}
|
|
108
|
+
if (!signature.payload || typeof signature.payload !== 'string') {
|
|
109
|
+
throw new Error('signature.payload must be a non-empty string');
|
|
110
|
+
}
|
|
111
|
+
if (!signature.protected || typeof signature.protected !== 'string') {
|
|
112
|
+
throw new Error('signature.protected must be a non-empty string');
|
|
113
|
+
}
|
|
114
|
+
if (!signature.signature || typeof signature.signature !== 'string') {
|
|
115
|
+
throw new Error('signature.signature must be a non-empty string');
|
|
116
|
+
}
|
|
117
|
+
if (!canonicalString || typeof canonicalString !== 'string') {
|
|
118
|
+
throw new Error('canonicalString must be a non-empty string');
|
|
119
|
+
}
|
|
120
|
+
if (!publicKeyPEM || typeof publicKeyPEM !== 'string') {
|
|
121
|
+
throw new Error('publicKeyPEM must be a non-empty string');
|
|
122
|
+
}
|
|
123
|
+
// Constant-time comparison to prevent timing attacks
|
|
124
|
+
const expectedPayload = Buffer.from(canonicalString, 'utf-8').toString('base64url');
|
|
125
|
+
const expectedPayloadBuffer = Buffer.from(expectedPayload, 'utf-8');
|
|
126
|
+
const actualPayloadBuffer = Buffer.from(signature.payload, 'utf-8');
|
|
127
|
+
// Check lengths first (length comparison is not timing-sensitive for this use case)
|
|
128
|
+
if (expectedPayloadBuffer.length !== actualPayloadBuffer.length) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
// Constant-time comparison to prevent timing side-channel attacks
|
|
132
|
+
if (!crypto.timingSafeEqual(expectedPayloadBuffer, actualPayloadBuffer)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const signingInput = `${signature.protected}.${signature.payload}`;
|
|
136
|
+
let publicKeyObject;
|
|
137
|
+
try {
|
|
138
|
+
publicKeyObject = crypto.createPublicKey({
|
|
139
|
+
key: publicKeyPEM,
|
|
140
|
+
format: 'pem',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw new Error(`Invalid public key: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return crypto.verify('sha256', Buffer.from(signingInput, 'utf-8'), publicKeyObject, Buffer.from(signature.signature, 'base64url'));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new Error('Signature verification failed');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=signatures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../../src/internal/crypto/signatures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Capsa decryption utilities for client-side decryption of API responses. */
|
|
2
|
+
import type { Capsa, KeychainEntry, EncryptedFile, CapsaMetadata } from '../../types/index.js';
|
|
3
|
+
export interface DecryptedCapsa {
|
|
4
|
+
id: string;
|
|
5
|
+
creator: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
status: 'active' | 'expired';
|
|
9
|
+
subject?: string;
|
|
10
|
+
body?: string;
|
|
11
|
+
structured?: Record<string, unknown>;
|
|
12
|
+
files: EncryptedFile[];
|
|
13
|
+
accessControl: {
|
|
14
|
+
expiresAt?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Cached AES-256 master key, avoids repeated RSA-4096 decryption per file.
|
|
18
|
+
* Non-enumerable to prevent accidental serialization (JSON.stringify, spread, logging).
|
|
19
|
+
* Scoped to this capsa only; does not compromise other capsas or the party's private key.
|
|
20
|
+
* Call clearMasterKey() when done with file operations.
|
|
21
|
+
*/
|
|
22
|
+
_masterKey: Buffer;
|
|
23
|
+
/** Securely clears the master key from memory. */
|
|
24
|
+
clearMasterKey(): void;
|
|
25
|
+
keychain: {
|
|
26
|
+
algorithm: string;
|
|
27
|
+
keys: KeychainEntry[];
|
|
28
|
+
};
|
|
29
|
+
signature: {
|
|
30
|
+
algorithm: string;
|
|
31
|
+
protected: string;
|
|
32
|
+
payload: string;
|
|
33
|
+
signature: string;
|
|
34
|
+
};
|
|
35
|
+
metadata?: CapsaMetadata;
|
|
36
|
+
stats: {
|
|
37
|
+
totalSize: number;
|
|
38
|
+
fileCount: number;
|
|
39
|
+
lastAccessedAt?: string;
|
|
40
|
+
};
|
|
41
|
+
_encrypted: Capsa;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decrypt capsa using party's private key
|
|
45
|
+
*
|
|
46
|
+
* SECURITY FEATURES:
|
|
47
|
+
* - Verifies capsa signature before decryption
|
|
48
|
+
* - Requires auth tags for all AES-GCM decryption
|
|
49
|
+
* - Validates keychain entry exists
|
|
50
|
+
*
|
|
51
|
+
* NOTE: partyId is optional. If not provided, uses the first keychain entry
|
|
52
|
+
* (which is the authenticated party's key when retrieved from API).
|
|
53
|
+
*
|
|
54
|
+
* @param capsa - Encrypted capsa from API
|
|
55
|
+
* @param privateKey - Party's RSA private key in PEM format
|
|
56
|
+
* @param partyId - Party ID (optional - auto-detected from keychain if omitted)
|
|
57
|
+
* @param creatorPublicKey - Creator's RSA public key in PEM format (for signature verification)
|
|
58
|
+
* @param verifySignature - Whether to verify signature (default: true, set false to skip)
|
|
59
|
+
* @returns Decrypted capsa data
|
|
60
|
+
* @throws Error if signature invalid, party not in keychain, or decryption fails
|
|
61
|
+
*/
|
|
62
|
+
export declare function decryptCapsa(capsa: Capsa, privateKey: string, partyId?: string, creatorPublicKey?: string, verifySignature?: boolean): DecryptedCapsa;
|
|
63
|
+
/**
|
|
64
|
+
* Decrypt a file from a capsa
|
|
65
|
+
*
|
|
66
|
+
* SECURITY: Requires non-empty authTag for AES-GCM integrity verification
|
|
67
|
+
*
|
|
68
|
+
* @param encryptedFileData - Encrypted file data (base64url)
|
|
69
|
+
* @param masterKey - Decrypted master key
|
|
70
|
+
* @param iv - Initialization vector for file
|
|
71
|
+
* @param authTag - Authentication tag for file (REQUIRED)
|
|
72
|
+
* @returns Decrypted file buffer
|
|
73
|
+
* @throws Error if authTag is missing or decryption fails
|
|
74
|
+
*/
|
|
75
|
+
export declare function decryptFile(encryptedFileData: string, masterKey: Buffer, iv: string, authTag: string, compressed?: boolean): Promise<Buffer>;
|
|
76
|
+
/**
|
|
77
|
+
* Decrypt filename from capsa
|
|
78
|
+
*
|
|
79
|
+
* SECURITY: Requires non-empty authTag for AES-GCM integrity verification
|
|
80
|
+
*
|
|
81
|
+
* @param encryptedFilename - Encrypted filename (base64url)
|
|
82
|
+
* @param masterKey - Decrypted master key
|
|
83
|
+
* @param iv - Initialization vector for filename
|
|
84
|
+
* @param authTag - Authentication tag for filename (REQUIRED)
|
|
85
|
+
* @returns Decrypted filename
|
|
86
|
+
* @throws Error if authTag is missing or decryption fails
|
|
87
|
+
*/
|
|
88
|
+
export declare function decryptFilename(encryptedFilename: string, masterKey: Buffer, iv: string, authTag: string): string;
|
|
89
|
+
//# sourceMappingURL=capsa-decryptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capsa-decryptor.d.ts","sourceRoot":"","sources":["../../../src/internal/decryptor/capsa-decryptor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAM9E,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE/F,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAG7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGrC,KAAK,EAAE,aAAa,EAAE,CAAC;IAGvB,aAAa,EAAE;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,kDAAkD;IAClD,cAAc,IAAI,IAAI,CAAC;IAGvB,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,aAAa,EAAE,CAAC;KACvB,CAAC;IAGF,SAAS,EAAE;QACT,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAGF,QAAQ,CAAC,EAAE,aAAa,CAAC;IAGzB,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAGF,UAAU,EAAE,KAAK,CAAC;CACnB;AAoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,UAAO,GACrB,cAAc,CAwPhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,OAAO,GACnB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,GACd,MAAM,CAeR"}
|