neozip-blockchain 1.0.5 → 1.5.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/README.md +2 -1
- package/WHATS_NEW.md +26 -0
- package/dist/encryption/AccessMetadata.d.ts +24 -0
- package/dist/encryption/AccessMetadata.d.ts.map +1 -0
- package/dist/encryption/AccessMetadata.js +71 -0
- package/dist/encryption/AccessMetadata.js.map +1 -0
- package/dist/encryption/KeyWrapper.d.ts +40 -0
- package/dist/encryption/KeyWrapper.d.ts.map +1 -0
- package/dist/encryption/KeyWrapper.js +129 -0
- package/dist/encryption/KeyWrapper.js.map +1 -0
- package/dist/encryption/KeyWrapperX25519.d.ts +43 -0
- package/dist/encryption/KeyWrapperX25519.d.ts.map +1 -0
- package/dist/encryption/KeyWrapperX25519.js +136 -0
- package/dist/encryption/KeyWrapperX25519.js.map +1 -0
- package/dist/encryption/RecipientEncryption.d.ts +60 -0
- package/dist/encryption/RecipientEncryption.d.ts.map +1 -0
- package/dist/encryption/RecipientEncryption.js +237 -0
- package/dist/encryption/RecipientEncryption.js.map +1 -0
- package/dist/encryption/index.d.ts +6 -0
- package/dist/encryption/index.d.ts.map +1 -0
- package/dist/encryption/index.js +23 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/encryption/types.d.ts +60 -0
- package/dist/encryption/types.d.ts.map +1 -0
- package/dist/encryption/types.js +6 -0
- package/dist/encryption/types.js.map +1 -0
- package/dist/identity/ENSResolver.d.ts +30 -0
- package/dist/identity/ENSResolver.d.ts.map +1 -0
- package/dist/identity/ENSResolver.js +85 -0
- package/dist/identity/ENSResolver.js.map +1 -0
- package/dist/identity/IdentityResolverDispatch.d.ts +35 -0
- package/dist/identity/IdentityResolverDispatch.d.ts.map +1 -0
- package/dist/identity/IdentityResolverDispatch.js +55 -0
- package/dist/identity/IdentityResolverDispatch.js.map +1 -0
- package/dist/identity/LitPkpResolver.d.ts +17 -0
- package/dist/identity/LitPkpResolver.d.ts.map +1 -0
- package/dist/identity/LitPkpResolver.js +44 -0
- package/dist/identity/LitPkpResolver.js.map +1 -0
- package/dist/identity/index.d.ts +6 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +18 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/types.d.ts +38 -0
- package/dist/identity/types.d.ts.map +1 -0
- package/dist/identity/types.js +13 -0
- package/dist/identity/types.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/docs/CONTRACT_CODE_VERIFICATION_DESIGN.md +525 -0
- package/docs/ENCRYPTION.md +213 -0
- package/docs/MIGRATION.md +175 -0
- package/docs/RATE_LIMITING_DESIGN.md +290 -0
- package/docs/TOKEN_SERVICE_API_UPDATE_PLAN.md +712 -0
- package/docs/security-hardening-plan.html +398 -0
- package/docs/security-hardening-plan.md +316 -0
- package/package.json +24 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Blockchain functionality for NeoZip: **NeoZip Token Service timestamping** (stamp, upgrade, mint, verify), NFT minting, verification, and wallet management. Part of the [neozipkit monorepo](../../README.md).
|
|
4
4
|
|
|
5
|
-
> **Stable 1.
|
|
5
|
+
> **Stable 1.5:** This package **1.5.0** (identity-based encryption; pairs with `neozipkit@1.5.0`). Format profile: [NEOZIP_APPNOTE.md](../neozipkit/NEOZIP_APPNOTE.md). See [WHATS_NEW.md](WHATS_NEW.md).
|
|
6
6
|
|
|
7
7
|
See [WHATS_NEW.md](WHATS_NEW.md) for release notes.
|
|
8
8
|
|
|
@@ -16,6 +16,7 @@ This package provides the client-side API and utilities that work with the sibli
|
|
|
16
16
|
- **Wallet Management**: Browser and Node.js wallet integrations
|
|
17
17
|
- **Multi-Network Support**: Base Mainnet, Base Sepolia, and more
|
|
18
18
|
- **Examples**: Runnable scripts for stamp, upgrade, mint, verify, and token-create flows (see [`examples/README.md`](examples/README.md), `examples/`, and `package.json` `example:*` scripts)
|
|
19
|
+
- **Identity encryption**: Encrypt `.nzip` archives for ENS / address / Lit PKP recipients (`neozip-blockchain/encryption` + `neozip-blockchain/identity`). See [docs/ENCRYPTION.md](docs/ENCRYPTION.md).
|
|
19
20
|
|
|
20
21
|
## Installation
|
|
21
22
|
|
package/WHATS_NEW.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
Release notes for people who install and use **`neozip-blockchain`** from npm (alongside [`neozipkit`](https://www.npmjs.com/package/neozipkit)).
|
|
4
4
|
|
|
5
|
+
## 1.5.0 (2026-10-25)
|
|
6
|
+
|
|
7
|
+
Released with sibling **`neozipkit@1.5.0`** (Zip64, Extra Field `0x014F`, browser/buffer zstd, `forceZip64`).
|
|
8
|
+
|
|
9
|
+
### Identity-based (recipient) encryption
|
|
10
|
+
|
|
11
|
+
Moved from the former private `@neowareinc/neozipkit-pro` package into this library:
|
|
12
|
+
|
|
13
|
+
- **`neozip-blockchain/encryption`** — ECIES key wrapping (secp256k1 / X25519 + AES-256-GCM), `META-INF/ACCESS.NZIP`, `encryptForRecipients` / `decryptAsRecipient`
|
|
14
|
+
- **`neozip-blockchain/identity`** — ENS / address / Lit PKP resolvers for recipient public keys
|
|
15
|
+
|
|
16
|
+
Inner ZIP entry encryption still uses **neozipkit** NeoEncrypt (`neo-aes256`) by default. See [docs/ENCRYPTION.md](docs/ENCRYPTION.md).
|
|
17
|
+
|
|
18
|
+
HKDF domain-separation strings are unchanged (`neozipkit-pro/ecies/v1`, …) so existing recipient-encrypted archives remain decryptable.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install neozipkit@^1.5.0 neozip-blockchain@^1.5.0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { encryptForRecipients } from 'neozip-blockchain/encryption';
|
|
26
|
+
import { ENSResolver } from 'neozip-blockchain/identity';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
5
31
|
## 1.0.5 (2026-08-14)
|
|
6
32
|
|
|
7
33
|
### Token Service network profiles (Base Sepolia + Base Mainnet)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize and parse META-INF/ACCESS.NZIP — the JSON envelope that stores
|
|
3
|
+
* ECIES-wrapped AES passwords and recipient identity information.
|
|
4
|
+
*/
|
|
5
|
+
import { type ResolvedIdentity } from '../identity/types';
|
|
6
|
+
import type { AccessControlMetadata, WrappedKeyResult } from './types';
|
|
7
|
+
/** Well-known path inside a .nzip archive for recipient access control. */
|
|
8
|
+
export declare const ACCESS_NZIP_PATH = "META-INF/ACCESS.NZIP";
|
|
9
|
+
/**
|
|
10
|
+
* Build an AccessControlMetadata object from resolved identities and their
|
|
11
|
+
* individually wrapped keys.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildAccessMetadata(recipients: Array<{
|
|
14
|
+
resolved: ResolvedIdentity;
|
|
15
|
+
wrapped: WrappedKeyResult;
|
|
16
|
+
}>, scheme?: AccessControlMetadata['scheme'], innerZipEncryption?: 'neo-aes256' | 'aes256'): AccessControlMetadata;
|
|
17
|
+
/** Serialize metadata to a UTF-8 buffer suitable for writing as a ZIP entry. */
|
|
18
|
+
export declare function serializeAccessMetadata(metadata: AccessControlMetadata): Buffer;
|
|
19
|
+
/**
|
|
20
|
+
* Parse a raw buffer (extracted from META-INF/ACCESS.NZIP) back into typed metadata.
|
|
21
|
+
* Throws on malformed JSON or missing required fields.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseAccessMetadata(raw: Buffer | string): AccessControlMetadata;
|
|
24
|
+
//# sourceMappingURL=AccessMetadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessMetadata.d.ts","sourceRoot":"","sources":["../../src/encryption/AccessMetadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EACV,qBAAqB,EAErB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAEjB,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC,EAC5E,MAAM,GAAE,qBAAqB,CAAC,QAAQ,CAAgB,EACtD,kBAAkB,GAAE,YAAY,GAAG,QAAuB,GACzD,qBAAqB,CAgCvB;AAED,gFAAgF;AAChF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,qBAAqB,CAsB/E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Serialize and parse META-INF/ACCESS.NZIP — the JSON envelope that stores
|
|
4
|
+
* ECIES-wrapped AES passwords and recipient identity information.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ACCESS_NZIP_PATH = void 0;
|
|
8
|
+
exports.buildAccessMetadata = buildAccessMetadata;
|
|
9
|
+
exports.serializeAccessMetadata = serializeAccessMetadata;
|
|
10
|
+
exports.parseAccessMetadata = parseAccessMetadata;
|
|
11
|
+
const version_1 = require("../version");
|
|
12
|
+
const types_1 = require("../identity/types");
|
|
13
|
+
/** Well-known path inside a .nzip archive for recipient access control. */
|
|
14
|
+
exports.ACCESS_NZIP_PATH = 'META-INF/ACCESS.NZIP';
|
|
15
|
+
/**
|
|
16
|
+
* Build an AccessControlMetadata object from resolved identities and their
|
|
17
|
+
* individually wrapped keys.
|
|
18
|
+
*/
|
|
19
|
+
function buildAccessMetadata(recipients, scheme = 'ens-hybrid', innerZipEncryption = 'neo-aes256') {
|
|
20
|
+
const encryptionInner = innerZipEncryption === 'aes256'
|
|
21
|
+
? {
|
|
22
|
+
method: 'aes-256-winzip',
|
|
23
|
+
note: 'AES key derived via PBKDF2 from wrapped password (neozipkit AE-1)',
|
|
24
|
+
}
|
|
25
|
+
: {
|
|
26
|
+
method: 'neo-aes256',
|
|
27
|
+
note: 'NeoEncrypt (NEO extra 0x024E); AES-256 via PBKDF2 from wrapped password (neozipkit)',
|
|
28
|
+
};
|
|
29
|
+
const wrappedRecipients = recipients.map(({ resolved, wrapped }) => ({
|
|
30
|
+
identity: resolved.identity,
|
|
31
|
+
identityType: resolved.identityType,
|
|
32
|
+
resolvedAddress: resolved.address,
|
|
33
|
+
publicKeyHex: resolved.publicKeyHex,
|
|
34
|
+
wrappedKey: wrapped.ciphertext,
|
|
35
|
+
ephemeralPublicKey: wrapped.ephemeralPublicKey,
|
|
36
|
+
keyAlgorithm: wrapped.keyAlgorithm,
|
|
37
|
+
}));
|
|
38
|
+
return {
|
|
39
|
+
version: '1.0',
|
|
40
|
+
scheme,
|
|
41
|
+
recipients: wrappedRecipients,
|
|
42
|
+
encryption: encryptionInner,
|
|
43
|
+
created: new Date().toISOString(),
|
|
44
|
+
proVersion: version_1.VERSION,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Serialize metadata to a UTF-8 buffer suitable for writing as a ZIP entry. */
|
|
48
|
+
function serializeAccessMetadata(metadata) {
|
|
49
|
+
return Buffer.from(JSON.stringify(metadata, null, 2), 'utf8');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse a raw buffer (extracted from META-INF/ACCESS.NZIP) back into typed metadata.
|
|
53
|
+
* Throws on malformed JSON or missing required fields.
|
|
54
|
+
*/
|
|
55
|
+
function parseAccessMetadata(raw) {
|
|
56
|
+
const text = typeof raw === 'string' ? raw : raw.toString('utf8');
|
|
57
|
+
const parsed = JSON.parse(text);
|
|
58
|
+
if (!parsed.version || !parsed.scheme || !Array.isArray(parsed.recipients)) {
|
|
59
|
+
throw new Error('Invalid ACCESS.NZIP: missing version, scheme, or recipients');
|
|
60
|
+
}
|
|
61
|
+
for (const r of parsed.recipients) {
|
|
62
|
+
if (!r.identityType || !(0, types_1.isIdentityType)(r.identityType)) {
|
|
63
|
+
throw new Error(`Invalid ACCESS.NZIP recipient "${r.identity ?? '?'}": unknown or missing identityType`);
|
|
64
|
+
}
|
|
65
|
+
if (!r.wrappedKey || !r.ephemeralPublicKey || !r.keyAlgorithm) {
|
|
66
|
+
throw new Error(`Invalid ACCESS.NZIP recipient "${r.identity}": missing wrappedKey, ephemeralPublicKey, or keyAlgorithm`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=AccessMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessMetadata.js","sourceRoot":"","sources":["../../src/encryption/AccessMetadata.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAiBH,kDAoCC;AAGD,0DAIC;AAMD,kDAsBC;AAtFD,wCAAqC;AACrC,6CAA0E;AAO1E,2EAA2E;AAC9D,QAAA,gBAAgB,GAAG,sBAAsB,CAAC;AAEvD;;;GAGG;AACH,SAAgB,mBAAmB,CACjC,UAA4E,EAC5E,SAA0C,YAAY,EACtD,qBAA8C,YAAY;IAE1D,MAAM,eAAe,GACnB,kBAAkB,KAAK,QAAQ;QAC7B,CAAC,CAAC;YACE,MAAM,EAAE,gBAAyB;YACjC,IAAI,EAAE,mEAAmE;SAC1E;QACH,CAAC,CAAC;YACE,MAAM,EAAE,YAAqB;YAC7B,IAAI,EAAE,qFAAqF;SAC5F,CAAC;IAER,MAAM,iBAAiB,GAAuB,UAAU,CAAC,GAAG,CAC1D,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,eAAe,EAAE,QAAQ,CAAC,OAAO;QACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,CAAC,CACH,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,KAAK;QACd,MAAM;QACN,UAAU,EAAE,iBAAiB;QAC7B,UAAU,EAAE,eAAe;QAC3B,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACjC,UAAU,EAAE,iBAAO;KACpB,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,SAAgB,uBAAuB,CACrC,QAA+B;IAE/B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,GAAoB;IACtD,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA0B,CAAC;IAEzD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,IAAA,sBAAc,EAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CACb,kCAAkC,CAAC,CAAC,QAAQ,IAAI,GAAG,oCAAoC,CACxF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CACb,kCAAkC,CAAC,CAAC,QAAQ,4DAA4D,CACzG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECIES key wrapping using secp256k1.
|
|
3
|
+
*
|
|
4
|
+
* Encrypt:
|
|
5
|
+
* 1. Generate ephemeral secp256k1 key pair
|
|
6
|
+
* 2. ECDH(ephemeral private, recipient public) → shared secret
|
|
7
|
+
* 3. HKDF-SHA256(shared secret) → 32-byte wrapping key
|
|
8
|
+
* 4. AES-256-GCM encrypt plaintext with wrapping key
|
|
9
|
+
*
|
|
10
|
+
* Decrypt:
|
|
11
|
+
* 1. ECDH(recipient private, ephemeral public) → same shared secret
|
|
12
|
+
* 2. HKDF-SHA256(shared secret) → same wrapping key
|
|
13
|
+
* 3. AES-256-GCM decrypt ciphertext
|
|
14
|
+
*/
|
|
15
|
+
import type { WrappedKeyResult } from './types';
|
|
16
|
+
export declare const KEY_ALGORITHM_SECP256K1 = "ecies-secp256k1-aes256gcm";
|
|
17
|
+
/** @deprecated Use KEY_ALGORITHM_SECP256K1 */
|
|
18
|
+
export declare const KEY_ALGORITHM = "ecies-secp256k1-aes256gcm";
|
|
19
|
+
/**
|
|
20
|
+
* Wrap (encrypt) plaintext so only the holder of `recipientPubKeyHex` can unwrap it.
|
|
21
|
+
*
|
|
22
|
+
* @param recipientPubKeyHex Uncompressed secp256k1 public key ("04…", 130 hex chars).
|
|
23
|
+
* @param plaintext The secret to protect (typically a hex-encoded AES password).
|
|
24
|
+
*/
|
|
25
|
+
export declare function wrapKey(recipientPubKeyHex: string, plaintext: Buffer): WrappedKeyResult;
|
|
26
|
+
/**
|
|
27
|
+
* Unwrap (decrypt) ciphertext using the recipient's private key.
|
|
28
|
+
*
|
|
29
|
+
* @param recipientPrivKeyHex Recipient's secp256k1 private key (hex, 64 chars).
|
|
30
|
+
* @param ciphertextB64 Base64 string produced by `wrapKey`.
|
|
31
|
+
* @param ephemeralPubKeyHex Ephemeral public key from `wrapKey` result.
|
|
32
|
+
* @returns The original plaintext buffer.
|
|
33
|
+
*/
|
|
34
|
+
export declare function unwrapKey(recipientPrivKeyHex: string, ciphertextB64: string, ephemeralPubKeyHex: string): Buffer;
|
|
35
|
+
/**
|
|
36
|
+
* Derive the public key (uncompressed, hex) from a private key. Useful for
|
|
37
|
+
* matching a recipient entry in ACCESS.NZIP without knowing the identity string.
|
|
38
|
+
*/
|
|
39
|
+
export declare function publicKeyFromPrivate(privKeyHex: string): string;
|
|
40
|
+
//# sourceMappingURL=KeyWrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyWrapper.d.ts","sourceRoot":"","sources":["../../src/encryption/KeyWrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AACnE,8CAA8C;AAC9C,eAAO,MAAM,aAAa,8BAA0B,CAAC;AAYrD;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAsBlB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAgBR;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ECIES key wrapping using secp256k1.
|
|
4
|
+
*
|
|
5
|
+
* Encrypt:
|
|
6
|
+
* 1. Generate ephemeral secp256k1 key pair
|
|
7
|
+
* 2. ECDH(ephemeral private, recipient public) → shared secret
|
|
8
|
+
* 3. HKDF-SHA256(shared secret) → 32-byte wrapping key
|
|
9
|
+
* 4. AES-256-GCM encrypt plaintext with wrapping key
|
|
10
|
+
*
|
|
11
|
+
* Decrypt:
|
|
12
|
+
* 1. ECDH(recipient private, ephemeral public) → same shared secret
|
|
13
|
+
* 2. HKDF-SHA256(shared secret) → same wrapping key
|
|
14
|
+
* 3. AES-256-GCM decrypt ciphertext
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.KEY_ALGORITHM = exports.KEY_ALGORITHM_SECP256K1 = void 0;
|
|
51
|
+
exports.wrapKey = wrapKey;
|
|
52
|
+
exports.unwrapKey = unwrapKey;
|
|
53
|
+
exports.publicKeyFromPrivate = publicKeyFromPrivate;
|
|
54
|
+
const crypto = __importStar(require("crypto"));
|
|
55
|
+
const secp256k1_1 = require("@noble/curves/secp256k1");
|
|
56
|
+
const hkdf_1 = require("@noble/hashes/hkdf");
|
|
57
|
+
const sha256_1 = require("@noble/hashes/sha256");
|
|
58
|
+
exports.KEY_ALGORITHM_SECP256K1 = 'ecies-secp256k1-aes256gcm';
|
|
59
|
+
/** @deprecated Use KEY_ALGORITHM_SECP256K1 */
|
|
60
|
+
exports.KEY_ALGORITHM = exports.KEY_ALGORITHM_SECP256K1;
|
|
61
|
+
const AES_KEY_BYTES = 32;
|
|
62
|
+
const GCM_IV_BYTES = 12;
|
|
63
|
+
const GCM_AUTH_TAG_BYTES = 16;
|
|
64
|
+
const HKDF_INFO = Buffer.from('neozipkit-pro/ecies/v1', 'utf8');
|
|
65
|
+
function deriveWrappingKey(sharedSecret) {
|
|
66
|
+
const ikm = sharedSecret.slice(1); // drop the 0x04 prefix byte
|
|
67
|
+
const derived = (0, hkdf_1.hkdf)(sha256_1.sha256, ikm, undefined, HKDF_INFO, AES_KEY_BYTES);
|
|
68
|
+
return Buffer.from(derived);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Wrap (encrypt) plaintext so only the holder of `recipientPubKeyHex` can unwrap it.
|
|
72
|
+
*
|
|
73
|
+
* @param recipientPubKeyHex Uncompressed secp256k1 public key ("04…", 130 hex chars).
|
|
74
|
+
* @param plaintext The secret to protect (typically a hex-encoded AES password).
|
|
75
|
+
*/
|
|
76
|
+
function wrapKey(recipientPubKeyHex, plaintext) {
|
|
77
|
+
const recipientPub = hexToBytes(recipientPubKeyHex);
|
|
78
|
+
const ephPriv = secp256k1_1.secp256k1.utils.randomPrivateKey();
|
|
79
|
+
const ephPub = secp256k1_1.secp256k1.getPublicKey(ephPriv, false); // uncompressed
|
|
80
|
+
const shared = secp256k1_1.secp256k1.getSharedSecret(ephPriv, recipientPub, false);
|
|
81
|
+
const wrappingKey = deriveWrappingKey(shared);
|
|
82
|
+
const iv = crypto.randomBytes(GCM_IV_BYTES);
|
|
83
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', wrappingKey, iv);
|
|
84
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
85
|
+
const tag = cipher.getAuthTag();
|
|
86
|
+
// ciphertext layout: iv (12) || authTag (16) || encrypted data
|
|
87
|
+
const ciphertextBuf = Buffer.concat([iv, tag, encrypted]);
|
|
88
|
+
return {
|
|
89
|
+
ciphertext: ciphertextBuf.toString('base64'),
|
|
90
|
+
ephemeralPublicKey: Buffer.from(ephPub).toString('hex'),
|
|
91
|
+
keyAlgorithm: exports.KEY_ALGORITHM,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Unwrap (decrypt) ciphertext using the recipient's private key.
|
|
96
|
+
*
|
|
97
|
+
* @param recipientPrivKeyHex Recipient's secp256k1 private key (hex, 64 chars).
|
|
98
|
+
* @param ciphertextB64 Base64 string produced by `wrapKey`.
|
|
99
|
+
* @param ephemeralPubKeyHex Ephemeral public key from `wrapKey` result.
|
|
100
|
+
* @returns The original plaintext buffer.
|
|
101
|
+
*/
|
|
102
|
+
function unwrapKey(recipientPrivKeyHex, ciphertextB64, ephemeralPubKeyHex) {
|
|
103
|
+
const recipientPriv = hexToBytes(recipientPrivKeyHex);
|
|
104
|
+
const ephPub = hexToBytes(ephemeralPubKeyHex);
|
|
105
|
+
const shared = secp256k1_1.secp256k1.getSharedSecret(recipientPriv, ephPub, false);
|
|
106
|
+
const wrappingKey = deriveWrappingKey(shared);
|
|
107
|
+
const ciphertextBuf = Buffer.from(ciphertextB64, 'base64');
|
|
108
|
+
const iv = ciphertextBuf.subarray(0, GCM_IV_BYTES);
|
|
109
|
+
const tag = ciphertextBuf.subarray(GCM_IV_BYTES, GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
110
|
+
const encrypted = ciphertextBuf.subarray(GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
111
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', wrappingKey, iv);
|
|
112
|
+
decipher.setAuthTag(tag);
|
|
113
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
114
|
+
return decrypted;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Derive the public key (uncompressed, hex) from a private key. Useful for
|
|
118
|
+
* matching a recipient entry in ACCESS.NZIP without knowing the identity string.
|
|
119
|
+
*/
|
|
120
|
+
function publicKeyFromPrivate(privKeyHex) {
|
|
121
|
+
const priv = hexToBytes(privKeyHex);
|
|
122
|
+
const pub = secp256k1_1.secp256k1.getPublicKey(priv, false);
|
|
123
|
+
return Buffer.from(pub).toString('hex');
|
|
124
|
+
}
|
|
125
|
+
function hexToBytes(hex) {
|
|
126
|
+
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
127
|
+
return Uint8Array.from(Buffer.from(clean, 'hex'));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=KeyWrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyWrapper.js","sourceRoot":"","sources":["../../src/encryption/KeyWrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECIES key wrapping using X25519 (Curve25519 Diffie-Hellman).
|
|
3
|
+
*
|
|
4
|
+
* X25519 is purpose-built for key agreement / secure messaging and is used by
|
|
5
|
+
* Signal, WireGuard, TLS 1.3, and libsodium. The ZipStamp server provisions
|
|
6
|
+
* X25519 key pairs for each user, so no additional key infrastructure is needed.
|
|
7
|
+
*
|
|
8
|
+
* Encrypt:
|
|
9
|
+
* 1. Generate ephemeral X25519 key pair
|
|
10
|
+
* 2. X25519(ephemeral private, recipient public) → 32-byte shared secret
|
|
11
|
+
* 3. HKDF-SHA256(shared secret) → 32-byte wrapping key
|
|
12
|
+
* 4. AES-256-GCM encrypt plaintext with wrapping key
|
|
13
|
+
*
|
|
14
|
+
* Decrypt:
|
|
15
|
+
* 1. X25519(recipient private, ephemeral public) → same shared secret
|
|
16
|
+
* 2. HKDF-SHA256(shared secret) → same wrapping key
|
|
17
|
+
* 3. AES-256-GCM decrypt ciphertext
|
|
18
|
+
*/
|
|
19
|
+
import type { WrappedKeyResult } from './types';
|
|
20
|
+
export declare const KEY_ALGORITHM_X25519 = "ecies-x25519-aes256gcm";
|
|
21
|
+
/**
|
|
22
|
+
* Wrap (encrypt) plaintext so only the holder of `recipientPubKeyHex` can unwrap it.
|
|
23
|
+
*
|
|
24
|
+
* @param recipientPubKeyHex Raw X25519 public key, 64 hex chars (32 bytes).
|
|
25
|
+
* Obtained from ZipStamp GET /crypto/public → x25519PublicKey.
|
|
26
|
+
* @param plaintext The secret to protect (typically a hex-encoded AES password).
|
|
27
|
+
*/
|
|
28
|
+
export declare function wrapKeyX25519(recipientPubKeyHex: string, plaintext: Buffer): WrappedKeyResult;
|
|
29
|
+
/**
|
|
30
|
+
* Unwrap (decrypt) ciphertext using the recipient's X25519 private key.
|
|
31
|
+
*
|
|
32
|
+
* @param recipientPrivKeyHex Recipient's X25519 private key (hex, 64 chars / 32 bytes).
|
|
33
|
+
* @param ciphertextB64 Base64 string produced by `wrapKeyX25519`.
|
|
34
|
+
* @param ephemeralPubKeyHex Ephemeral public key from `wrapKeyX25519` result (64 hex chars).
|
|
35
|
+
* @returns The original plaintext buffer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function unwrapKeyX25519(recipientPrivKeyHex: string, ciphertextB64: string, ephemeralPubKeyHex: string): Buffer;
|
|
38
|
+
/**
|
|
39
|
+
* Derive the X25519 public key (hex, 64 chars) from a private key.
|
|
40
|
+
* Useful for matching a recipient entry in ACCESS.NZIP.
|
|
41
|
+
*/
|
|
42
|
+
export declare function publicKeyFromPrivateX25519(privKeyHex: string): string;
|
|
43
|
+
//# sourceMappingURL=KeyWrapperX25519.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyWrapperX25519.d.ts","sourceRoot":"","sources":["../../src/encryption/KeyWrapperX25519.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAW7D;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAyBlB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAsBR;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ECIES key wrapping using X25519 (Curve25519 Diffie-Hellman).
|
|
4
|
+
*
|
|
5
|
+
* X25519 is purpose-built for key agreement / secure messaging and is used by
|
|
6
|
+
* Signal, WireGuard, TLS 1.3, and libsodium. The ZipStamp server provisions
|
|
7
|
+
* X25519 key pairs for each user, so no additional key infrastructure is needed.
|
|
8
|
+
*
|
|
9
|
+
* Encrypt:
|
|
10
|
+
* 1. Generate ephemeral X25519 key pair
|
|
11
|
+
* 2. X25519(ephemeral private, recipient public) → 32-byte shared secret
|
|
12
|
+
* 3. HKDF-SHA256(shared secret) → 32-byte wrapping key
|
|
13
|
+
* 4. AES-256-GCM encrypt plaintext with wrapping key
|
|
14
|
+
*
|
|
15
|
+
* Decrypt:
|
|
16
|
+
* 1. X25519(recipient private, ephemeral public) → same shared secret
|
|
17
|
+
* 2. HKDF-SHA256(shared secret) → same wrapping key
|
|
18
|
+
* 3. AES-256-GCM decrypt ciphertext
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.KEY_ALGORITHM_X25519 = void 0;
|
|
55
|
+
exports.wrapKeyX25519 = wrapKeyX25519;
|
|
56
|
+
exports.unwrapKeyX25519 = unwrapKeyX25519;
|
|
57
|
+
exports.publicKeyFromPrivateX25519 = publicKeyFromPrivateX25519;
|
|
58
|
+
const crypto = __importStar(require("crypto"));
|
|
59
|
+
const ed25519_1 = require("@noble/curves/ed25519");
|
|
60
|
+
const hkdf_1 = require("@noble/hashes/hkdf");
|
|
61
|
+
const sha256_1 = require("@noble/hashes/sha256");
|
|
62
|
+
exports.KEY_ALGORITHM_X25519 = 'ecies-x25519-aes256gcm';
|
|
63
|
+
const AES_KEY_BYTES = 32;
|
|
64
|
+
const GCM_IV_BYTES = 12;
|
|
65
|
+
const GCM_AUTH_TAG_BYTES = 16;
|
|
66
|
+
const HKDF_INFO = Buffer.from('neozipkit-pro/ecies-x25519/v1', 'utf8');
|
|
67
|
+
function deriveWrappingKey(sharedSecret) {
|
|
68
|
+
const derived = (0, hkdf_1.hkdf)(sha256_1.sha256, sharedSecret, undefined, HKDF_INFO, AES_KEY_BYTES);
|
|
69
|
+
return Buffer.from(derived);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Wrap (encrypt) plaintext so only the holder of `recipientPubKeyHex` can unwrap it.
|
|
73
|
+
*
|
|
74
|
+
* @param recipientPubKeyHex Raw X25519 public key, 64 hex chars (32 bytes).
|
|
75
|
+
* Obtained from ZipStamp GET /crypto/public → x25519PublicKey.
|
|
76
|
+
* @param plaintext The secret to protect (typically a hex-encoded AES password).
|
|
77
|
+
*/
|
|
78
|
+
function wrapKeyX25519(recipientPubKeyHex, plaintext) {
|
|
79
|
+
const clean = recipientPubKeyHex.startsWith('0x')
|
|
80
|
+
? recipientPubKeyHex.slice(2)
|
|
81
|
+
: recipientPubKeyHex;
|
|
82
|
+
const recipientPub = Uint8Array.from(Buffer.from(clean, 'hex'));
|
|
83
|
+
const ephPriv = ed25519_1.x25519.utils.randomPrivateKey();
|
|
84
|
+
const ephPub = ed25519_1.x25519.getPublicKey(ephPriv);
|
|
85
|
+
const shared = ed25519_1.x25519.getSharedSecret(ephPriv, recipientPub);
|
|
86
|
+
const wrappingKey = deriveWrappingKey(shared);
|
|
87
|
+
const iv = crypto.randomBytes(GCM_IV_BYTES);
|
|
88
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', wrappingKey, iv);
|
|
89
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
90
|
+
const tag = cipher.getAuthTag();
|
|
91
|
+
// ciphertext layout: iv (12) || authTag (16) || encrypted data
|
|
92
|
+
const ciphertextBuf = Buffer.concat([iv, tag, encrypted]);
|
|
93
|
+
return {
|
|
94
|
+
ciphertext: ciphertextBuf.toString('base64'),
|
|
95
|
+
ephemeralPublicKey: Buffer.from(ephPub).toString('hex'),
|
|
96
|
+
keyAlgorithm: exports.KEY_ALGORITHM_X25519,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Unwrap (decrypt) ciphertext using the recipient's X25519 private key.
|
|
101
|
+
*
|
|
102
|
+
* @param recipientPrivKeyHex Recipient's X25519 private key (hex, 64 chars / 32 bytes).
|
|
103
|
+
* @param ciphertextB64 Base64 string produced by `wrapKeyX25519`.
|
|
104
|
+
* @param ephemeralPubKeyHex Ephemeral public key from `wrapKeyX25519` result (64 hex chars).
|
|
105
|
+
* @returns The original plaintext buffer.
|
|
106
|
+
*/
|
|
107
|
+
function unwrapKeyX25519(recipientPrivKeyHex, ciphertextB64, ephemeralPubKeyHex) {
|
|
108
|
+
const cleanPriv = recipientPrivKeyHex.startsWith('0x')
|
|
109
|
+
? recipientPrivKeyHex.slice(2)
|
|
110
|
+
: recipientPrivKeyHex;
|
|
111
|
+
const cleanEph = ephemeralPubKeyHex.startsWith('0x')
|
|
112
|
+
? ephemeralPubKeyHex.slice(2)
|
|
113
|
+
: ephemeralPubKeyHex;
|
|
114
|
+
const recipientPriv = Uint8Array.from(Buffer.from(cleanPriv, 'hex'));
|
|
115
|
+
const ephPub = Uint8Array.from(Buffer.from(cleanEph, 'hex'));
|
|
116
|
+
const shared = ed25519_1.x25519.getSharedSecret(recipientPriv, ephPub);
|
|
117
|
+
const wrappingKey = deriveWrappingKey(shared);
|
|
118
|
+
const ciphertextBuf = Buffer.from(ciphertextB64, 'base64');
|
|
119
|
+
const iv = ciphertextBuf.subarray(0, GCM_IV_BYTES);
|
|
120
|
+
const tag = ciphertextBuf.subarray(GCM_IV_BYTES, GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
121
|
+
const encrypted = ciphertextBuf.subarray(GCM_IV_BYTES + GCM_AUTH_TAG_BYTES);
|
|
122
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', wrappingKey, iv);
|
|
123
|
+
decipher.setAuthTag(tag);
|
|
124
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Derive the X25519 public key (hex, 64 chars) from a private key.
|
|
128
|
+
* Useful for matching a recipient entry in ACCESS.NZIP.
|
|
129
|
+
*/
|
|
130
|
+
function publicKeyFromPrivateX25519(privKeyHex) {
|
|
131
|
+
const clean = privKeyHex.startsWith('0x') ? privKeyHex.slice(2) : privKeyHex;
|
|
132
|
+
const priv = Uint8Array.from(Buffer.from(clean, 'hex'));
|
|
133
|
+
const pub = ed25519_1.x25519.getPublicKey(priv);
|
|
134
|
+
return Buffer.from(pub).toString('hex');
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=KeyWrapperX25519.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyWrapperX25519.js","sourceRoot":"","sources":["../../src/encryption/KeyWrapperX25519.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level orchestrator for identity-based (recipient) encryption.
|
|
3
|
+
*
|
|
4
|
+
* Encrypt workflow:
|
|
5
|
+
* 1. Generate a random AES password (32 random bytes → hex)
|
|
6
|
+
* 2. Create a NeoEncrypt (neo-aes256) encrypted .nzip via neozipkit by default
|
|
7
|
+
* 3. For each recipient: ECIES-wrap the password with their public key
|
|
8
|
+
* 4. Append META-INF/ACCESS.NZIP (STORED, unencrypted) with the wrapped keys
|
|
9
|
+
*
|
|
10
|
+
* Decrypt workflow:
|
|
11
|
+
* 1. Load the .nzip and extract META-INF/ACCESS.NZIP
|
|
12
|
+
* 2. Match the recipient by public key derived from the private key
|
|
13
|
+
* 3. ECIES-unwrap → recover the AES password
|
|
14
|
+
* 4. Set the password on the zipkit instance for normal extraction
|
|
15
|
+
*/
|
|
16
|
+
import type { ResolvedIdentity } from '../identity/types';
|
|
17
|
+
import type { RecipientEncryptionFileInput, RecipientEncryptionOptions } from './types';
|
|
18
|
+
interface ZipkitNodeLike {
|
|
19
|
+
initializeZipFile(outputPath: string): Promise<any>;
|
|
20
|
+
prepareEntryFromFile(filePath: string, entryName?: string): Promise<any>;
|
|
21
|
+
writeZipEntry(writer: any, entry: any, filePath: string, options?: any): Promise<void>;
|
|
22
|
+
writeCentralDirectory(writer: any, entries: any[], options?: any): Promise<number>;
|
|
23
|
+
writeEndOfCentralDirectory(writer: any, count: number, size: number, offset: number): Promise<void>;
|
|
24
|
+
finalizeZipFile(writer: any): Promise<void>;
|
|
25
|
+
getDirectory(): any[];
|
|
26
|
+
loadZipFile(filePath: string): Promise<any[]>;
|
|
27
|
+
getZipEntry(filename: string): any | null;
|
|
28
|
+
extractToBuffer(entry: any, options?: any): Promise<Buffer>;
|
|
29
|
+
/** neozipkit ZipkitNode: close read handle opened by loadZipFile */
|
|
30
|
+
closeFile?: () => Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create an encrypted .nzip file that can only be opened by the listed
|
|
34
|
+
* recipients (via their secp256k1 public keys). Entry data uses **NeoEncrypt**
|
|
35
|
+
* (`neo-aes256`, NEO extra `0x024E` in neozipkit) by default; set
|
|
36
|
+
* `zipEncryptionMethod: 'aes256'` in options if you need WinZip AES.
|
|
37
|
+
*
|
|
38
|
+
* @param zipkit A fresh ZipkitNode instance (will be mutated).
|
|
39
|
+
* @param files Paths only (`string[]`) or `{ path, entryName? }[]` — forwarded to neozipkit
|
|
40
|
+
* `prepareEntryFromFile` / `writeZipEntry` (compress then encrypt).
|
|
41
|
+
* @param outputPath Where to write the .nzip.
|
|
42
|
+
* @param recipients Pre-resolved identities with public keys.
|
|
43
|
+
* @param options Compression and inner ZIP encryption options forwarded to neozipkit.
|
|
44
|
+
*/
|
|
45
|
+
export declare function encryptForRecipients(zipkit: ZipkitNodeLike, files: string[] | RecipientEncryptionFileInput[], outputPath: string, recipients: ResolvedIdentity[], options?: RecipientEncryptionOptions): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Recover the AES password from a recipient-encrypted .nzip and configure the
|
|
48
|
+
* zipkit instance for extraction.
|
|
49
|
+
*
|
|
50
|
+
* After this call succeeds, use the returned zipkit normally:
|
|
51
|
+
* `const buf = await zipkit.extractToBuffer(entry);`
|
|
52
|
+
*
|
|
53
|
+
* @param zipkit A ZipkitNode instance.
|
|
54
|
+
* @param archivePath Path to the .nzip file.
|
|
55
|
+
* @param recipientPrivKey Recipient's secp256k1 private key (hex).
|
|
56
|
+
* @returns The same zipkit instance, now configured with the recovered password.
|
|
57
|
+
*/
|
|
58
|
+
export declare function decryptAsRecipient(zipkit: ZipkitNodeLike, archivePath: string, recipientPrivKey: string): Promise<ZipkitNodeLike>;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=RecipientEncryption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecipientEncryption.d.ts","sourceRoot":"","sources":["../../src/encryption/RecipientEncryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmBH,OAAO,KAAK,EAAE,gBAAgB,EAAkC,MAAM,mBAAmB,CAAC;AAC1F,OAAO,KAAK,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAwCxF,UAAU,cAAc;IACtB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACpD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACzE,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,qBAAqB,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnF,0BAA0B,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpG,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,YAAY,IAAI,GAAG,EAAE,CAAC;IACtB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;IAC1C,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,EAAE,GAAG,4BAA4B,EAAE,EAChD,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,gBAAgB,EAAE,EAC9B,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,IAAI,CAAC,CAkFf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,cAAc,CAAC,CA6DzB"}
|