neozipkit 0.1.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/README.md +400 -0
- package/contracts/Lock.sol +34 -0
- package/contracts/bytecode.txt +1 -0
- package/contracts/compile-contract.js +168 -0
- package/contracts/deploy/deploy-both-contracts.js +286 -0
- package/contracts/deploy/deploy-enhanced-contract.js +197 -0
- package/contracts/deploy/deploy-private-contract.js +190 -0
- package/contracts/deploy/deploy-public-clean.js +1063 -0
- package/contracts/deploy/deploy-public-contract.js +1107 -0
- package/contracts/deployment.config.example.js +25 -0
- package/contracts/remix-compile.js +222 -0
- package/contracts/setup-deployment.js +51 -0
- package/contracts/src/NZIP-NFT-Private.sol +350 -0
- package/contracts/src/NZIP-NFT.sol +265 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-6field.sol +260 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-Enhanced.sol +289 -0
- package/dist/browser/CertificateGeneratorBrowser.d.ts +75 -0
- package/dist/browser/CertificateGeneratorBrowser.js +433 -0
- package/dist/browser/TokenVerifierBrowser.d.ts +76 -0
- package/dist/browser/TokenVerifierBrowser.js +299 -0
- package/dist/browser/ZipKitBrowser.d.ts +18 -0
- package/dist/browser/ZipKitBrowser.js +250 -0
- package/dist/browser/ZipKitStampBrowser.d.ts +2 -0
- package/dist/browser/ZipKitStampBrowser.js +12 -0
- package/dist/browser-index.d.ts +21 -0
- package/dist/browser-index.js +58 -0
- package/dist/browser.d.ts +21 -0
- package/dist/browser.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +76 -0
- package/dist/server/blockchain/CertificateToken.d.ts +48 -0
- package/dist/server/blockchain/CertificateToken.js +386 -0
- package/dist/server/blockchain/NFTMinter.d.ts +41 -0
- package/dist/server/blockchain/NFTMinter.js +323 -0
- package/dist/server/blockchain/TokenQuerier.d.ts +31 -0
- package/dist/server/blockchain/TokenQuerier.js +259 -0
- package/dist/server/blockchain/TokenVerifier.d.ts +20 -0
- package/dist/server/blockchain/TokenVerifier.js +395 -0
- package/dist/server/blockchain/WalletAnalyzer.d.ts +81 -0
- package/dist/server/blockchain/WalletAnalyzer.js +356 -0
- package/dist/server/blockchain/WalletFormatter.d.ts +39 -0
- package/dist/server/blockchain/WalletFormatter.js +196 -0
- package/dist/server/blockchain/WalletManager.d.ts +46 -0
- package/dist/server/blockchain/WalletManager.js +294 -0
- package/dist/server/blockchain/ZipMetadata.d.ts +27 -0
- package/dist/server/blockchain/ZipMetadata.js +131 -0
- package/dist/server/blockchain/ZipTokenProcessor.d.ts +44 -0
- package/dist/server/blockchain/ZipTokenProcessor.js +204 -0
- package/dist/server/blockchain/constants.d.ts +102 -0
- package/dist/server/blockchain/constants.js +968 -0
- package/dist/server/blockchain/index.d.ts +9 -0
- package/dist/server/blockchain/index.js +44 -0
- package/dist/server/blockchain/test-certificate.d.ts +8 -0
- package/dist/server/blockchain/test-certificate.js +100 -0
- package/dist/server/blockchain/types.d.ts +69 -0
- package/dist/server/blockchain/types.js +51 -0
- package/dist/server/platform/server/ZipKitServer.d.ts +105 -0
- package/dist/server/platform/server/ZipKitServer.js +437 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.d.ts +91 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.js +271 -0
- package/dist/shared/ZipEntry.d.ts +131 -0
- package/dist/shared/ZipEntry.js +686 -0
- package/dist/shared/ZipKit.d.ts +249 -0
- package/dist/shared/ZipKit.js +789 -0
- package/dist/shared/components/CertificateOTS.d.ts +15 -0
- package/dist/shared/components/CertificateOTS.js +116 -0
- package/dist/shared/components/FileSys.d.ts +2 -0
- package/dist/shared/components/FileSys.js +113 -0
- package/dist/shared/components/HashAccumulator.d.ts +55 -0
- package/dist/shared/components/HashAccumulator.js +100 -0
- package/dist/shared/components/Support.d.ts +63 -0
- package/dist/shared/components/Support.js +70 -0
- package/dist/shared/components/Util.d.ts +21 -0
- package/dist/shared/components/Util.js +115 -0
- package/dist/shared/components/ZipCrypto.d.ts +3 -0
- package/dist/shared/components/ZipCrypto.js +78 -0
- package/dist/shared/components/merkleTree.d.ts +66 -0
- package/dist/shared/components/merkleTree.js +136 -0
- package/dist/shared/constants/Errors.d.ts +53 -0
- package/dist/shared/constants/Errors.js +69 -0
- package/dist/shared/constants/Headers.d.ts +167 -0
- package/dist/shared/constants/Headers.js +191 -0
- package/dist/shared/types/index.d.ts +21 -0
- package/dist/shared/types/index.js +3 -0
- package/dist/shared/types.d.ts +33 -0
- package/dist/shared/types.js +8 -0
- package/dist/shared/version.d.ts +4 -0
- package/dist/shared/version.js +7 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.js +3 -0
- package/examples/unzip-token.ts +488 -0
- package/examples/zip-token.ts +604 -0
- package/package.json +117 -0
- package/src/browser/CertificateGeneratorBrowser.ts +498 -0
- package/src/browser/TokenVerifierBrowser.ts +344 -0
- package/src/browser/ZipKitBrowser.ts +242 -0
- package/src/browser/ZipKitStampBrowser.ts +9 -0
- package/src/browser-index.ts +36 -0
- package/src/index.ts +48 -0
- package/src/server/blockchain/CertificateToken.ts +444 -0
- package/src/server/blockchain/NFTMinter.ts +357 -0
- package/src/server/blockchain/TokenQuerier.ts +287 -0
- package/src/server/blockchain/TokenVerifier.ts +453 -0
- package/src/server/blockchain/WalletAnalyzer.ts +397 -0
- package/src/server/blockchain/WalletFormatter.ts +206 -0
- package/src/server/blockchain/WalletManager.ts +294 -0
- package/src/server/blockchain/ZipMetadata.ts +117 -0
- package/src/server/blockchain/ZipTokenProcessor.ts +234 -0
- package/src/server/blockchain/constants.ts +969 -0
- package/src/server/blockchain/index.ts +42 -0
- package/src/server/blockchain/test-certificate.ts +137 -0
- package/src/server/blockchain/types.ts +134 -0
- package/src/server/platform/server/ZipKitServer.ts +459 -0
- package/src/server/platform/servercomponents/ZipKitOTS.ts +313 -0
- package/src/server/platform/servercomponents/opentimestamps.d.ts +23 -0
- package/src/shared/ZipEntry.ts +767 -0
- package/src/shared/ZipKit.ts +899 -0
- package/src/shared/components/CertificateOTS.ts +132 -0
- package/src/shared/components/FileSys.ts +87 -0
- package/src/shared/components/HashAccumulator.ts +113 -0
- package/src/shared/components/Support.ts +77 -0
- package/src/shared/components/Util.ts +113 -0
- package/src/shared/components/ZipCrypto.ts +91 -0
- package/src/shared/components/merkleTree.ts +162 -0
- package/src/shared/constants/Errors.ts +82 -0
- package/src/shared/constants/Headers.ts +203 -0
- package/src/shared/types/index.ts +25 -0
- package/src/shared/types/modules.d.ts +22 -0
- package/src/shared/types/opentimestamps.d.ts +23 -0
- package/src/shared/types.d.ts +8 -0
- package/src/shared/types.ts +43 -0
- package/src/shared/version.ts +4 -0
- package/src/types/index.ts +25 -0
- package/src/types/modules.d.ts +22 -0
- package/src/types/opentimestamps.d.ts +23 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// CertificateOTS.ts
|
|
3
|
+
// Copyright (c) 2025 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import ZipEntry from '../ZipEntry';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
TIMESTAMP_SUBMITTED,
|
|
12
|
+
TIMESTAMP_METADATA,
|
|
13
|
+
} from '../constants/Headers';
|
|
14
|
+
|
|
15
|
+
// Add this at the top of the file
|
|
16
|
+
import type { AttestationValue } from '../types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generates a certificate HTML document based on timestamp information
|
|
20
|
+
*
|
|
21
|
+
* @param attestStr - Attestation message
|
|
22
|
+
* @param zipFile - Name of the zip file
|
|
23
|
+
* @param hashDigest - Hash digest of the zip file
|
|
24
|
+
* @param needsUpdateMsg - Message indicating if update is needed
|
|
25
|
+
* @param attestationValues - Array of attestation values
|
|
26
|
+
* @param zipEntries - Array of ZipEntry objects
|
|
27
|
+
* @param narrow - Whether to use narrow layout
|
|
28
|
+
* @returns Generated HTML certificate as string
|
|
29
|
+
*/
|
|
30
|
+
export default function CertificateTemplate(
|
|
31
|
+
attestStr: string | null,
|
|
32
|
+
zipFile: string,
|
|
33
|
+
hashDigest: string,
|
|
34
|
+
needsUpdateMsg: string,
|
|
35
|
+
attestationValues: AttestationValue[],
|
|
36
|
+
zipEntries: ZipEntry[],
|
|
37
|
+
narrow?: boolean
|
|
38
|
+
): string {
|
|
39
|
+
// Load the HTML template
|
|
40
|
+
let templatePath;
|
|
41
|
+
try {
|
|
42
|
+
// Try to find the template from the package root
|
|
43
|
+
templatePath = path.resolve(process.cwd(), 'assets/templates/certificate.html');
|
|
44
|
+
if (!fs.existsSync(templatePath)) {
|
|
45
|
+
// If not found in current working directory, try relative to this file
|
|
46
|
+
templatePath = path.resolve(__dirname, '../../assets/templates/certificate.html');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(templatePath)) {
|
|
50
|
+
throw new Error('Certificate template not found');
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error resolving template path:', error);
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Read the template file
|
|
58
|
+
let htmlTemplate;
|
|
59
|
+
try {
|
|
60
|
+
htmlTemplate = fs.readFileSync(templatePath, 'utf8');
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('Error reading template file:', error);
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const updateWarning = needsUpdateMsg || '';
|
|
67
|
+
|
|
68
|
+
// Generate attestation HTML
|
|
69
|
+
let attestHTML = '';
|
|
70
|
+
if (attestationValues && attestationValues.length > 0) {
|
|
71
|
+
attestationValues.forEach((attest) => {
|
|
72
|
+
attestHTML += `
|
|
73
|
+
<div class="key-value-pair">
|
|
74
|
+
<div class="key">Block Height:</div>
|
|
75
|
+
<div class="value">${attest['blockHeight']}</div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="key-value-pair">
|
|
78
|
+
<div class="key">Transaction:</div>
|
|
79
|
+
<div class="value">${attest['transactionId']}</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="key-value-pair">
|
|
82
|
+
<div class="key">Merkle Root:</div>
|
|
83
|
+
<div class="value">${attest['merkleRoot']}</div>
|
|
84
|
+
</div>
|
|
85
|
+
<br/>
|
|
86
|
+
`;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Generate files table HTML
|
|
91
|
+
let stampEntry = null;
|
|
92
|
+
let files = [];
|
|
93
|
+
for (const entry of zipEntries) {
|
|
94
|
+
if (entry.filename === TIMESTAMP_METADATA) {
|
|
95
|
+
stampEntry = entry;
|
|
96
|
+
} else if (entry.filename === TIMESTAMP_SUBMITTED) {
|
|
97
|
+
if (stampEntry == null)
|
|
98
|
+
stampEntry = entry;
|
|
99
|
+
} else if (entry.sha256) {
|
|
100
|
+
let file = {
|
|
101
|
+
name: entry.filename,
|
|
102
|
+
size: entry.uncompressedSize.toString(),
|
|
103
|
+
date: entry.toFormattedUTCDateString(),
|
|
104
|
+
hash: entry.sha256,
|
|
105
|
+
}
|
|
106
|
+
files.push(file);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let filesTableHTML = '';
|
|
111
|
+
files.forEach((file) => {
|
|
112
|
+
filesTableHTML += `
|
|
113
|
+
<tr>
|
|
114
|
+
<td class="table-data1">${file.name}</td>
|
|
115
|
+
<td class="table-data">${file.size}</td>
|
|
116
|
+
<td class="table-data">${file.date}</td>
|
|
117
|
+
<td class="table-data">${file.hash}</td>
|
|
118
|
+
</tr>
|
|
119
|
+
`;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Replace placeholders in the template
|
|
123
|
+
let html = htmlTemplate
|
|
124
|
+
.replace(/{{ATTEST_STR}}/g, attestStr || '')
|
|
125
|
+
.replace(/{{UPDATE_WARNING}}/g, needsUpdateMsg ? `<div class="warning">${needsUpdateMsg}</div>` : '')
|
|
126
|
+
.replace(/{{ZIP_FILE}}/g, zipFile)
|
|
127
|
+
.replace(/{{HASH_DIGEST}}/g, hashDigest)
|
|
128
|
+
.replace(/{{ATTESTATION_HTML}}/g, attestHTML)
|
|
129
|
+
.replace(/{{FILES_TABLE_HTML}}/g, filesTableHTML);
|
|
130
|
+
|
|
131
|
+
return html;
|
|
132
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// FileSys.ts
|
|
3
|
+
// Copyright (c) 2024 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as pth from 'path';
|
|
8
|
+
|
|
9
|
+
import Errors from '../constants/Errors';
|
|
10
|
+
|
|
11
|
+
// Output a buffer to a file
|
|
12
|
+
export function outputToFile(
|
|
13
|
+
fileName: string,
|
|
14
|
+
buffer: Buffer,
|
|
15
|
+
dirPath: string,
|
|
16
|
+
overwrite: boolean,
|
|
17
|
+
never: boolean,
|
|
18
|
+
fileAttr: number)
|
|
19
|
+
{
|
|
20
|
+
// get Unix file permissions
|
|
21
|
+
const _fattr = fileAttr ? (((fileAttr >>> 0) | 0) >> 16) & 0xfff : 0;
|
|
22
|
+
|
|
23
|
+
const _path = dirPath ? dirPath + pth.sep + fileName : fileName;
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(_path)) {
|
|
26
|
+
console.log('File exists: ', _path);
|
|
27
|
+
if (never)
|
|
28
|
+
return false;
|
|
29
|
+
if (!overwrite) {
|
|
30
|
+
console.log('Overwrite file: ', _path);
|
|
31
|
+
return false; // cannot overwrite
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const stat = fs.statSync(_path);
|
|
35
|
+
if (stat.isDirectory())
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check if we need to create a new folder
|
|
40
|
+
const _folder = pth.dirname(_path);
|
|
41
|
+
if (!fs.existsSync(_folder)) {
|
|
42
|
+
makeDir(_folder);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var fd;
|
|
46
|
+
try {
|
|
47
|
+
fd = fs.openSync(_path, "w", 438); // 0666
|
|
48
|
+
} catch (e) {
|
|
49
|
+
fs.chmodSync(_path, 438);
|
|
50
|
+
fd = fs.openSync(_path, "w", 438);
|
|
51
|
+
}
|
|
52
|
+
if (fd) {
|
|
53
|
+
try {
|
|
54
|
+
fs.writeSync(fd, buffer, 0, buffer.length, 0);
|
|
55
|
+
} finally {
|
|
56
|
+
fs.closeSync(fd);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (_fattr)
|
|
61
|
+
fs.chmodSync(_path, _fattr);
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Make directories tree for a given path
|
|
67
|
+
export function makeDir(folder: string) {
|
|
68
|
+
console.log('mkDir: ', folder);
|
|
69
|
+
|
|
70
|
+
// Sync - make directories tree
|
|
71
|
+
function mkdirSync(fpath: string) {
|
|
72
|
+
let resolvedPath = fpath.split(pth.sep)[0];
|
|
73
|
+
fpath.split(pth.sep).forEach(function (name) {
|
|
74
|
+
if (!name || name.endsWith(":")) return;
|
|
75
|
+
resolvedPath += pth.sep + name;
|
|
76
|
+
var stat;
|
|
77
|
+
try {
|
|
78
|
+
stat = fs.statSync(resolvedPath);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
fs.mkdirSync(resolvedPath);
|
|
81
|
+
}
|
|
82
|
+
if (stat && stat.isFile()) throw Errors.FILE_IN_THE_WAY.replace("%s", resolvedPath);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
mkdirSync(folder);
|
|
87
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// HashAccumulator.ts
|
|
3
|
+
// ======================================
|
|
4
|
+
|
|
5
|
+
import { MerkleTree } from './merkleTree';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Class for accumulating and combining SHA-256 hashes using both XOR and Merkle tree methods
|
|
9
|
+
*
|
|
10
|
+
* Usage example:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const accumulator = new HashAccumulator();
|
|
13
|
+
* accumulator.addHash('a1b2c3d4');
|
|
14
|
+
* accumulator.addHash('12345678');
|
|
15
|
+
* console.log(accumulator.xorHash()); // Outputs the accumulated XOR result
|
|
16
|
+
* console.log(accumulator.merkleRoot()); // Outputs the accumulated Merkle root
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export default class HashAccumulator {
|
|
20
|
+
private hashes: Buffer[]; // Array of hash buffers for Merkle tree
|
|
21
|
+
private xorResult: Buffer; // Running XOR of all hashes
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new HashAccumulator instance
|
|
25
|
+
* Initializes empty hash array and zero-filled XOR buffer
|
|
26
|
+
*/
|
|
27
|
+
constructor() {
|
|
28
|
+
this.hashes = [];
|
|
29
|
+
this.xorResult = Buffer.alloc(32, 0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Adds a hash to both the XOR accumulation and hash array
|
|
34
|
+
* @param hash - Hash value as hex string or Buffer
|
|
35
|
+
* The hash must be 32 bytes (SHA-256)
|
|
36
|
+
*/
|
|
37
|
+
addHash(hash: string | Buffer) {
|
|
38
|
+
// Convert hash to Buffer if it's a string
|
|
39
|
+
const hashBuffer = typeof hash === 'string'
|
|
40
|
+
? Buffer.from(hash, 'hex')
|
|
41
|
+
: hash;
|
|
42
|
+
|
|
43
|
+
// Add to array for Merkle tree
|
|
44
|
+
this.hashes.push(hashBuffer);
|
|
45
|
+
|
|
46
|
+
// XOR accumulation
|
|
47
|
+
for (let i = 0; i < 32; i++) {
|
|
48
|
+
this.xorResult[i] ^= hashBuffer[i];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Gets the accumulated XOR of all added hashes
|
|
54
|
+
* @returns Hex string of XOR result
|
|
55
|
+
*/
|
|
56
|
+
xorHash(): string {
|
|
57
|
+
return this.xorResult.toString('hex');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a Merkle tree from accumulated hashes and returns the root
|
|
62
|
+
* @returns Hex string of Merkle root, or null if no hashes added
|
|
63
|
+
*/
|
|
64
|
+
merkleRoot(): string | null {
|
|
65
|
+
let merkleRoot = null;
|
|
66
|
+
if (this.hashes.length > 0) {
|
|
67
|
+
const tree = new MerkleTree(this.hashes, {
|
|
68
|
+
hashLeaves: false, // Leaves are already hashed
|
|
69
|
+
sortLeaves: true, // Sort leaves for consistent results
|
|
70
|
+
sortPairs: true, // Sort pairs for consistent results
|
|
71
|
+
duplicateOdd: true // Duplicate odd leaf for balanced tree
|
|
72
|
+
});
|
|
73
|
+
merkleRoot = tree.getRoot().toString('hex');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return merkleRoot;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets the number of hashes accumulated
|
|
81
|
+
* @returns Number of accumulated hashes
|
|
82
|
+
*/
|
|
83
|
+
leafCount(): number {
|
|
84
|
+
return this.hashes.length;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resets the accumulator to initial state
|
|
89
|
+
* Clears hash array and zeroes XOR buffer
|
|
90
|
+
*/
|
|
91
|
+
clear() {
|
|
92
|
+
this.hashes = [];
|
|
93
|
+
this.xorResult = Buffer.alloc(32, 0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Combines multiple hash results into a single Merkle root
|
|
98
|
+
* @param results - Array of objects containing merkleRoot properties
|
|
99
|
+
* @returns Combined Merkle root as hex string, or null if no valid roots
|
|
100
|
+
*/
|
|
101
|
+
static combineResults(results: { merkleRoot?: string }[]) {
|
|
102
|
+
const accumulator = new HashAccumulator();
|
|
103
|
+
|
|
104
|
+
// Combine all hashes
|
|
105
|
+
for (const result of results) {
|
|
106
|
+
if (result.merkleRoot) {
|
|
107
|
+
accumulator.addHash(result.merkleRoot);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return accumulator.merkleRoot();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// Support.ts
|
|
3
|
+
// A utility module for detecting platform capabilities and features
|
|
4
|
+
// Copyright (c) 2024 NeoWare, Inc. All rights reserved.
|
|
5
|
+
// ======================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Platform and environment feature detection utility
|
|
9
|
+
* Provides boolean flags indicating support for various features needed by NeoZip
|
|
10
|
+
*
|
|
11
|
+
* @namespace Support
|
|
12
|
+
* @property {boolean} base64 - Always true, indicates base64 encoding support
|
|
13
|
+
* @property {boolean} array - Always true, indicates Array support
|
|
14
|
+
* @property {boolean} string - Always true, indicates String support
|
|
15
|
+
* @property {boolean} isNode - True if running in Node.js environment
|
|
16
|
+
* @property {boolean} buffer - True if Node.js Buffer is available
|
|
17
|
+
* @property {boolean} uint8array - True if Uint8Array is supported
|
|
18
|
+
* @property {boolean} arrayBuffer - True if both ArrayBuffer and Uint8Array are supported
|
|
19
|
+
* @property {boolean} blob - True if Blob creation with ArrayBuffer is supported
|
|
20
|
+
* @property {boolean} streams - True if ReadableStream and WritableStream are supported
|
|
21
|
+
* @property {boolean} fileReader - True if FileReader is supported
|
|
22
|
+
*/
|
|
23
|
+
const Support = {
|
|
24
|
+
/** Base64 encoding support (always true) */
|
|
25
|
+
base64: true,
|
|
26
|
+
/** Array support (always true) */
|
|
27
|
+
array: true,
|
|
28
|
+
/** String support (always true) */
|
|
29
|
+
string: true,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Detects if code is running in Node.js environment
|
|
33
|
+
* True if process.versions.node exists, undefined in browser
|
|
34
|
+
*/
|
|
35
|
+
isNode: true,
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detects if Node.js Buffer is available
|
|
39
|
+
* True in Node.js, undefined in browser
|
|
40
|
+
*/
|
|
41
|
+
buffer: true,
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Detects support for Uint8Array typed array
|
|
45
|
+
* Used for binary data handling
|
|
46
|
+
*/
|
|
47
|
+
uint8array: true,
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Detects support for both ArrayBuffer and Uint8Array
|
|
51
|
+
* Required for binary data operations
|
|
52
|
+
*/
|
|
53
|
+
arrayBuffer: typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined",
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Detects support for Blob with ArrayBuffer
|
|
57
|
+
* Tests by attempting to create an empty ZIP Blob
|
|
58
|
+
* @returns {boolean} True if Blob creation succeeds
|
|
59
|
+
*/
|
|
60
|
+
blob: true,
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detects support for Streams API
|
|
64
|
+
* Checks for ReadableStream/WritableStream in browser
|
|
65
|
+
* or 'stream' module in Node.js
|
|
66
|
+
* @returns {boolean} True if streams are supported in current environment
|
|
67
|
+
*/
|
|
68
|
+
streams: true,
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Detects support for FileReader
|
|
72
|
+
* @returns {boolean} True if FileReader is supported
|
|
73
|
+
*/
|
|
74
|
+
fileReader: false
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default Support;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// Util.ts
|
|
3
|
+
// Copyright (c) 2024 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
// Utility functions
|
|
6
|
+
|
|
7
|
+
import Support from "./Support";
|
|
8
|
+
import Errors from "../constants/Errors";
|
|
9
|
+
|
|
10
|
+
export const DATATYPE = {
|
|
11
|
+
STRING: "string",
|
|
12
|
+
BUFFER: "buffer",
|
|
13
|
+
ARRAY: "array",
|
|
14
|
+
ARRAYBUFFER: "arraybuffer",
|
|
15
|
+
U8ARRAY: "uint8array",
|
|
16
|
+
BLOB: "blob",
|
|
17
|
+
STREAM: "stream",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Generate the CRC32 table
|
|
21
|
+
const crcTable = new Uint32Array(256).map((t, crc) => {
|
|
22
|
+
for (let j = 0; j < 8; j++) {
|
|
23
|
+
if (0 !== (crc & 1)) {
|
|
24
|
+
crc = (crc >>> 1) ^ 0xedb88320;
|
|
25
|
+
} else {
|
|
26
|
+
crc >>>= 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return crc >>> 0;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// crc32 byte single update (actually same function is part of utils.crc32 function :) )
|
|
33
|
+
export const crc32update = (pCrc32: number, bval: number) => {
|
|
34
|
+
return crcTable[(pCrc32 ^ bval) & 0xff] ^ (pCrc32 >>> 8);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Return the CRC32 of a buffer
|
|
38
|
+
export function crc32(buf: Buffer): number {
|
|
39
|
+
if (typeof buf === "string") {
|
|
40
|
+
buf = Buffer.from(buf, "utf8");
|
|
41
|
+
}
|
|
42
|
+
// Generate crcTable
|
|
43
|
+
// if (!crcTable.length) genCRCTable();
|
|
44
|
+
|
|
45
|
+
let len = buf.length;
|
|
46
|
+
let crc = ~0;
|
|
47
|
+
for (let off = 0; off < len; )
|
|
48
|
+
crc = crcTable[(crc ^ buf[off++]) & 0xff] ^ (crc >>> 8);
|
|
49
|
+
|
|
50
|
+
// xor and cast as uint32 number
|
|
51
|
+
return ~crc >>> 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Parse a 64-bit unsigned integer from a buffer
|
|
55
|
+
export function readBigUInt64LE(buffer: Buffer, index: number): number {
|
|
56
|
+
var buf = Buffer.from(buffer.subarray(index, index + 8));
|
|
57
|
+
buf.swap64();
|
|
58
|
+
|
|
59
|
+
return parseInt(`0x${buf.toString("hex")}`);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// C-style uInt32 Multiply (discards higher bits, when JS multiply discards lower bits)
|
|
63
|
+
export const uMul = (a: number, b: number) => Math.imul(a, b) >>> 0;
|
|
64
|
+
|
|
65
|
+
interface StreamLike {
|
|
66
|
+
on: Function;
|
|
67
|
+
pause: Function;
|
|
68
|
+
resume: Function;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function isStream(obj: unknown): obj is StreamLike {
|
|
72
|
+
if (!obj || typeof obj !== 'object') return false;
|
|
73
|
+
const streamLike = obj as StreamLike;
|
|
74
|
+
return typeof streamLike.on === "function" &&
|
|
75
|
+
typeof streamLike.pause === "function" &&
|
|
76
|
+
typeof streamLike.resume === "function";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Return the type of the input.
|
|
80
|
+
// The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
|
|
81
|
+
// If the type is not supported, throw an exception.
|
|
82
|
+
export function getTypeOf(input: string|Buffer|Blob|Array<any>|ArrayBuffer|Uint8Array): string {
|
|
83
|
+
if (typeof input === "string") {
|
|
84
|
+
if (!Support.string)
|
|
85
|
+
throw new Error(Errors.DATATYPE_STRING_UNSUPPORTED);
|
|
86
|
+
return DATATYPE.STRING;
|
|
87
|
+
}
|
|
88
|
+
else if (Object.prototype.toString.call(input) === "[object Array]") {
|
|
89
|
+
if (!Support.array)
|
|
90
|
+
throw new Error(Errors.DATATYPE_ARRAY_UNSUPPORTED);
|
|
91
|
+
return DATATYPE.ARRAY;
|
|
92
|
+
}
|
|
93
|
+
else if (Buffer.isBuffer(input)) {
|
|
94
|
+
if (!Support.buffer)
|
|
95
|
+
throw new Error(Errors.DATATYPE_BUFFER_UNSUPPORTED);
|
|
96
|
+
return DATATYPE.BUFFER;
|
|
97
|
+
} else if (input instanceof Blob) {
|
|
98
|
+
if (!Support.blob)
|
|
99
|
+
throw new Error(Errors.DATATYPE_BLOB_UNSUPPORTED);
|
|
100
|
+
return DATATYPE.BLOB;
|
|
101
|
+
} else if (input instanceof Uint8Array) {
|
|
102
|
+
if (!Support.uint8array)
|
|
103
|
+
throw new Error(Errors.DATATYPE_U8ARRAY_UNSUPPORTED);
|
|
104
|
+
return DATATYPE.U8ARRAY;
|
|
105
|
+
}
|
|
106
|
+
else if (input instanceof ArrayBuffer) {
|
|
107
|
+
if (!Support.arrayBuffer)
|
|
108
|
+
throw new Error(Errors.DATATYPE_ARRAYBUFFER_UNSUPPORTED);
|
|
109
|
+
return DATATYPE.ARRAYBUFFER;
|
|
110
|
+
} else { // Add an else block to handle unsupported types
|
|
111
|
+
throw new Error(Errors.DATATYPE_UNSUPPORTED);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// ZipCrypto.ts
|
|
3
|
+
// Copyright (c) 2024 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
// Zip Cryptography Routines
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
import { createHash, randomFillSync } from 'crypto';
|
|
10
|
+
|
|
11
|
+
import { crc32update, uMul } from './Util';
|
|
12
|
+
import { ENCRYPT_HDR_SIZE } from '../constants/Headers';
|
|
13
|
+
|
|
14
|
+
// Return the SHA256 hash of a buffer
|
|
15
|
+
export function sha256(data: Buffer): string {
|
|
16
|
+
const hash = createHash('sha256');
|
|
17
|
+
|
|
18
|
+
return hash
|
|
19
|
+
.update(data)
|
|
20
|
+
.digest('hex');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class InitKeys {
|
|
24
|
+
private keys: Uint32Array;
|
|
25
|
+
|
|
26
|
+
constructor(pw: string | Buffer) {
|
|
27
|
+
this.keys = new Uint32Array([0x12345678, 0x23456789, 0x34567890]);
|
|
28
|
+
const pass = typeof pw === 'string' ? Buffer.from(pw) : pw;
|
|
29
|
+
for (let i = 0; i < pass.length; i++) {
|
|
30
|
+
this.updateKeys(pass[i]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
updateKeys(byteValue: number): number {
|
|
35
|
+
this.keys[0] = crc32update(this.keys[0], byteValue);
|
|
36
|
+
this.keys[1] = ((this.keys[1] + (this.keys[0] & 0xff)) * 0x08088405 + 1) >>> 0;
|
|
37
|
+
this.keys[2] = crc32update(this.keys[2], this.keys[1] >>> 24);
|
|
38
|
+
return byteValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
next(): number {
|
|
42
|
+
const k = (this.keys[2] | 2) >>> 0; // key
|
|
43
|
+
return (uMul(k, k ^ 1) >> 8) & 0xff; // decode
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Return encrypting function, result and pos is here so we dont have to merge buffers later
|
|
48
|
+
function make_encrypter(pwd: string | Buffer): Function {
|
|
49
|
+
const keys = new InitKeys(pwd);
|
|
50
|
+
|
|
51
|
+
return function (data: Buffer, result: Buffer, pos: number = 0): Buffer {
|
|
52
|
+
// result - we create new Buffer for results
|
|
53
|
+
if (!result) result = Buffer.alloc(data.length);
|
|
54
|
+
// process input data
|
|
55
|
+
for (let c of data) {
|
|
56
|
+
const k = keys.next(); // save key byte
|
|
57
|
+
result[pos++] = c ^ k; // save val
|
|
58
|
+
keys.updateKeys(c); // update keys with decoded byte
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Encrypt data with password and include a checkByte verification byte at the end
|
|
65
|
+
export function encrypter(data: Buffer | null, checkByte: number, pwd: string): Buffer | null {
|
|
66
|
+
if (!data) return null;
|
|
67
|
+
|
|
68
|
+
const _encrypter = make_encrypter(pwd);
|
|
69
|
+
|
|
70
|
+
const salt = randomFillSync(Buffer.alloc(ENCRYPT_HDR_SIZE));
|
|
71
|
+
salt[ENCRYPT_HDR_SIZE - 1] = checkByte;
|
|
72
|
+
|
|
73
|
+
// create encryption header
|
|
74
|
+
const result = Buffer.alloc(data.length + ENCRYPT_HDR_SIZE);
|
|
75
|
+
_encrypter(salt, result);
|
|
76
|
+
|
|
77
|
+
// encode content
|
|
78
|
+
return _encrypter(data, result, ENCRYPT_HDR_SIZE);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Return the decrypter function
|
|
82
|
+
export function decrypter(pwd: string | Buffer): Function {
|
|
83
|
+
const keys = new InitKeys(pwd);
|
|
84
|
+
|
|
85
|
+
return function (data: Buffer): Buffer {
|
|
86
|
+
for (let i=0; i<data.length; i++) {
|
|
87
|
+
data[i] = keys.updateKeys(data[i] ^ keys.next());
|
|
88
|
+
}
|
|
89
|
+
return data;
|
|
90
|
+
};
|
|
91
|
+
}
|