@ursalock/crypto 0.3.0 → 0.4.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/index.d.ts +54 -1
- package/dist/index.js +35 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -322,6 +322,59 @@ declare function computeHmac(data: Uint8Array, key: Uint8Array): Promise<string>
|
|
|
322
322
|
*/
|
|
323
323
|
declare function verifyHmac(data: Uint8Array, key: Uint8Array, expectedHmac: string): Promise<boolean>;
|
|
324
324
|
|
|
325
|
+
/**
|
|
326
|
+
* HKDF-SHA256 key derivation (RFC 5869)
|
|
327
|
+
*
|
|
328
|
+
* Uses Web Crypto's native HKDF implementation for deriving sub-keys
|
|
329
|
+
* from a master key. This is used to generate vault-specific keys for
|
|
330
|
+
* encryption, HMAC, and indexing from a single master key.
|
|
331
|
+
*
|
|
332
|
+
* Design rationale:
|
|
333
|
+
* - Key separation: Each purpose gets its own derived key
|
|
334
|
+
* - Context binding: Vault UID prevents key reuse across vaults
|
|
335
|
+
* - Versioned info strings: "ursalock:v1:..." allows future changes
|
|
336
|
+
* - Domain separation: Different info → different keys (collision-free)
|
|
337
|
+
*/
|
|
338
|
+
/** Vault-specific derived keys */
|
|
339
|
+
interface VaultKeys {
|
|
340
|
+
/** AES-256-GCM encryption key for vault documents */
|
|
341
|
+
encryptionKey: Uint8Array;
|
|
342
|
+
/** HMAC-SHA256 key for integrity verification */
|
|
343
|
+
hmacKey: Uint8Array;
|
|
344
|
+
/** Key for encrypted search indexes (future use) */
|
|
345
|
+
indexKey: Uint8Array;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Derive a sub-key using HKDF-SHA256 (RFC 5869)
|
|
349
|
+
*
|
|
350
|
+
* Uses Web Crypto's native HKDF implementation.
|
|
351
|
+
*
|
|
352
|
+
* @param ikm - Input keying material (master key)
|
|
353
|
+
* @param info - Context string (e.g., "ursalock:vault:abc123:encrypt") or raw bytes
|
|
354
|
+
* @param salt - Optional salt (default: empty buffer)
|
|
355
|
+
* @param length - Output key length in bytes (default: 32 for AES-256)
|
|
356
|
+
* @returns Derived key material
|
|
357
|
+
*/
|
|
358
|
+
declare function hkdf(ikm: Uint8Array, info: string | Uint8Array, salt?: Uint8Array, length?: number): Promise<Uint8Array>;
|
|
359
|
+
/**
|
|
360
|
+
* Derive a complete set of vault keys from a master key
|
|
361
|
+
*
|
|
362
|
+
* Uses HKDF with vault-specific context strings to derive:
|
|
363
|
+
* - encryptionKey: for AES-256-GCM document encryption
|
|
364
|
+
* - hmacKey: for integrity verification (Encrypt-then-MAC)
|
|
365
|
+
* - indexKey: for encrypted search indexes (future)
|
|
366
|
+
*
|
|
367
|
+
* Context format: "ursalock:v1:<purpose>:<vaultUid>"
|
|
368
|
+
* - Versioned to allow future changes
|
|
369
|
+
* - Vault UID prevents key reuse across vaults
|
|
370
|
+
* - Purpose string provides domain separation
|
|
371
|
+
*
|
|
372
|
+
* @param masterKey - Master key (from Argon2id or ZKC PRF)
|
|
373
|
+
* @param vaultUid - Vault unique identifier (used as context)
|
|
374
|
+
* @returns Set of derived vault keys
|
|
375
|
+
*/
|
|
376
|
+
declare function deriveVaultKeys(masterKey: Uint8Array, vaultUid: string): Promise<VaultKeys>;
|
|
377
|
+
|
|
325
378
|
/**
|
|
326
379
|
* JWK-based encryption using @z-base/cryptosuite
|
|
327
380
|
* For use with ZKCredentials PRF-derived keys
|
|
@@ -361,4 +414,4 @@ declare function encryptStringWithJwk(plaintext: string, cipherJwk: CipherJWK):
|
|
|
361
414
|
*/
|
|
362
415
|
declare function decryptStringWithJwk(encrypted: Uint8Array | JwkEncryptedPayload, cipherJwk: CipherJWK): Promise<string>;
|
|
363
416
|
|
|
364
|
-
export { DEFAULT_ARGON2_PARAMS, type DeriveKeyOptions, type DerivedKey, type EncryptedPayload, type ICryptoProvider, type IEncryptedPayload, type IKeyDerivationProvider, type JwkEncryptedPayload, LEGACY_ARGON2_PARAMS, type RecoveryKey, WebCryptoProvider, bytesToRecoveryKey, computeHmac, constantTimeEqual, decrypt, decryptString, decryptStringWithJwk, decryptWithJwk, deriveKey, encrypt, encryptString, encryptStringWithJwk, encryptWithJwk, formatRecoveryKey, generateRecoveryKey, getCryptoProvider, randomBytes, recoveryKeyToBytes, setCryptoProvider, validateRecoveryKey, verifyHmac };
|
|
417
|
+
export { DEFAULT_ARGON2_PARAMS, type DeriveKeyOptions, type DerivedKey, type EncryptedPayload, type ICryptoProvider, type IEncryptedPayload, type IKeyDerivationProvider, type JwkEncryptedPayload, LEGACY_ARGON2_PARAMS, type RecoveryKey, type VaultKeys, WebCryptoProvider, bytesToRecoveryKey, computeHmac, constantTimeEqual, decrypt, decryptString, decryptStringWithJwk, decryptWithJwk, deriveKey, deriveVaultKeys, encrypt, encryptString, encryptStringWithJwk, encryptWithJwk, formatRecoveryKey, generateRecoveryKey, getCryptoProvider, hkdf, randomBytes, recoveryKeyToBytes, setCryptoProvider, validateRecoveryKey, verifyHmac };
|
package/dist/index.js
CHANGED
|
@@ -271,6 +271,39 @@ function hexToBytes(hex) {
|
|
|
271
271
|
return bytes;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
// src/hkdf.ts
|
|
275
|
+
async function hkdf(ikm, info, salt, length = 32) {
|
|
276
|
+
const infoBytes = typeof info === "string" ? new TextEncoder().encode(info) : info;
|
|
277
|
+
const actualSalt = salt ?? new Uint8Array(0);
|
|
278
|
+
const baseKey = await crypto.subtle.importKey(
|
|
279
|
+
"raw",
|
|
280
|
+
ikm.buffer.slice(ikm.byteOffset, ikm.byteOffset + ikm.byteLength),
|
|
281
|
+
{ name: "HKDF" },
|
|
282
|
+
false,
|
|
283
|
+
["deriveBits"]
|
|
284
|
+
);
|
|
285
|
+
const derivedBits = await crypto.subtle.deriveBits(
|
|
286
|
+
{
|
|
287
|
+
name: "HKDF",
|
|
288
|
+
hash: "SHA-256",
|
|
289
|
+
salt: actualSalt.buffer.slice(actualSalt.byteOffset, actualSalt.byteOffset + actualSalt.byteLength),
|
|
290
|
+
info: infoBytes.buffer.slice(infoBytes.byteOffset, infoBytes.byteOffset + infoBytes.byteLength)
|
|
291
|
+
},
|
|
292
|
+
baseKey,
|
|
293
|
+
length * 8
|
|
294
|
+
// Convert bytes to bits
|
|
295
|
+
);
|
|
296
|
+
return new Uint8Array(derivedBits);
|
|
297
|
+
}
|
|
298
|
+
async function deriveVaultKeys(masterKey, vaultUid) {
|
|
299
|
+
const [encryptionKey, hmacKey, indexKey] = await Promise.all([
|
|
300
|
+
hkdf(masterKey, `ursalock:v1:encrypt:${vaultUid}`),
|
|
301
|
+
hkdf(masterKey, `ursalock:v1:hmac:${vaultUid}`),
|
|
302
|
+
hkdf(masterKey, `ursalock:v1:index:${vaultUid}`)
|
|
303
|
+
]);
|
|
304
|
+
return { encryptionKey, hmacKey, indexKey };
|
|
305
|
+
}
|
|
306
|
+
|
|
274
307
|
// src/jwk.ts
|
|
275
308
|
import { CipherCluster } from "@z-base/cryptosuite";
|
|
276
309
|
async function encryptWithJwk(plaintext, cipherJwk) {
|
|
@@ -326,6 +359,7 @@ export {
|
|
|
326
359
|
decryptStringWithJwk,
|
|
327
360
|
decryptWithJwk,
|
|
328
361
|
deriveKey,
|
|
362
|
+
deriveVaultKeys,
|
|
329
363
|
encrypt,
|
|
330
364
|
encryptString,
|
|
331
365
|
encryptStringWithJwk,
|
|
@@ -333,6 +367,7 @@ export {
|
|
|
333
367
|
formatRecoveryKey,
|
|
334
368
|
generateRecoveryKey,
|
|
335
369
|
getCryptoProvider,
|
|
370
|
+
hkdf,
|
|
336
371
|
randomBytes,
|
|
337
372
|
recoveryKeyToBytes,
|
|
338
373
|
setCryptoProvider,
|