@wishknish/knishio-client-ts 0.7.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/LICENSE +674 -0
- package/README.md +425 -0
- package/dist/index.cjs +9961 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.iife.js +32241 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.js +9853 -0
- package/dist/index.js.map +1 -0
- package/package.json +113 -0
- package/src/AuthToken.ts +214 -0
- package/src/KnishIOClient.ts +2020 -0
- package/src/constants.ts +398 -0
- package/src/core/Atom.ts +646 -0
- package/src/core/AtomMeta.ts +278 -0
- package/src/core/Meta.ts +428 -0
- package/src/core/Molecule.ts +825 -0
- package/src/core/PolicyMeta.ts +130 -0
- package/src/core/TokenUnit.ts +148 -0
- package/src/core/Wallet.ts +467 -0
- package/src/exception/AtomIndexException.ts +97 -0
- package/src/exception/AtomsMissingException.ts +109 -0
- package/src/exception/AuthorizationRejectedException.ts +63 -0
- package/src/exception/BalanceInsufficientException.ts +58 -0
- package/src/exception/BaseException.ts +275 -0
- package/src/exception/BatchIdException.ts +58 -0
- package/src/exception/CodeException.ts +119 -0
- package/src/exception/DecryptionKeyException.ts +65 -0
- package/src/exception/InvalidResponseException.ts +112 -0
- package/src/exception/MetaMissingException.ts +58 -0
- package/src/exception/MolecularHashMismatchException.ts +115 -0
- package/src/exception/MolecularHashMissingException.ts +58 -0
- package/src/exception/NegativeAmountException.ts +58 -0
- package/src/exception/PolicyInvalidException.ts +58 -0
- package/src/exception/SignatureMalformedException.ts +58 -0
- package/src/exception/SignatureMismatchException.ts +98 -0
- package/src/exception/StackableUnitAmountException.ts +65 -0
- package/src/exception/StackableUnitDecimalsException.ts +65 -0
- package/src/exception/TransferBalanceException.ts +98 -0
- package/src/exception/TransferMalformedException.ts +58 -0
- package/src/exception/TransferMismatchedException.ts +58 -0
- package/src/exception/TransferRemainderException.ts +58 -0
- package/src/exception/TransferToSelfException.ts +58 -0
- package/src/exception/TransferUnbalancedException.ts +58 -0
- package/src/exception/UnauthenticatedException.ts +147 -0
- package/src/exception/WalletCredentialException.ts +108 -0
- package/src/exception/WalletShadowException.ts +65 -0
- package/src/exception/WrongTokenTypeException.ts +58 -0
- package/src/exception/index.ts +272 -0
- package/src/index.ts +512 -0
- package/src/instance/rules/Callback.ts +257 -0
- package/src/instance/rules/Condition.ts +96 -0
- package/src/instance/rules/Meta.ts +163 -0
- package/src/instance/rules/Rule.ts +29 -0
- package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
- package/src/libraries/CheckMolecule.ts +581 -0
- package/src/libraries/Decimal.ts +94 -0
- package/src/libraries/Dot.ts +202 -0
- package/src/libraries/GraphQLClient.ts +276 -0
- package/src/libraries/Hex.ts +155 -0
- package/src/libraries/UrqlClientWrapper.ts +336 -0
- package/src/libraries/array.ts +91 -0
- package/src/libraries/crypto.ts +816 -0
- package/src/libraries/strings.ts +458 -0
- package/src/mutation/Mutation.ts +103 -0
- package/src/mutation/MutationActiveSession.ts +108 -0
- package/src/mutation/MutationClaimShadowWallet.ts +91 -0
- package/src/mutation/MutationCreateIdentifier.ts +86 -0
- package/src/mutation/MutationCreateMeta.ts +92 -0
- package/src/mutation/MutationCreateRule.ts +90 -0
- package/src/mutation/MutationCreateToken.ts +92 -0
- package/src/mutation/MutationCreateWallet.ts +78 -0
- package/src/mutation/MutationDepositBufferToken.ts +72 -0
- package/src/mutation/MutationLinkIdentifier.ts +87 -0
- package/src/mutation/MutationProposeMolecule.ts +147 -0
- package/src/mutation/MutationRequestAuthorization.ts +77 -0
- package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
- package/src/mutation/MutationRequestTokens.ts +98 -0
- package/src/mutation/MutationTransferTokens.ts +87 -0
- package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
- package/src/query/Query.ts +187 -0
- package/src/query/QueryActiveSession.ts +89 -0
- package/src/query/QueryAtom.ts +275 -0
- package/src/query/QueryBalance.ts +102 -0
- package/src/query/QueryBatch.ts +145 -0
- package/src/query/QueryBatchHistory.ts +86 -0
- package/src/query/QueryContinuId.ts +91 -0
- package/src/query/QueryMetaType.ts +177 -0
- package/src/query/QueryMetaTypeViaAtom.ts +199 -0
- package/src/query/QueryPolicy.ts +91 -0
- package/src/query/QueryToken.ts +90 -0
- package/src/query/QueryUserActivity.ts +153 -0
- package/src/query/QueryWalletBundle.ts +92 -0
- package/src/query/QueryWalletList.ts +106 -0
- package/src/response/EnhancedResponse.ts +345 -0
- package/src/response/Response.ts +254 -0
- package/src/response/ResponseActiveSession.ts +72 -0
- package/src/response/ResponseAtom.ts +132 -0
- package/src/response/ResponseAuthorizationGuest.ts +119 -0
- package/src/response/ResponseBalance.ts +153 -0
- package/src/response/ResponseClaimShadowWallet.ts +56 -0
- package/src/response/ResponseContinuId.ts +101 -0
- package/src/response/ResponseCreateIdentifier.ts +56 -0
- package/src/response/ResponseCreateMeta.ts +58 -0
- package/src/response/ResponseCreateRule.ts +56 -0
- package/src/response/ResponseCreateToken.ts +58 -0
- package/src/response/ResponseCreateWallet.ts +58 -0
- package/src/response/ResponseLinkIdentifier.ts +87 -0
- package/src/response/ResponseMetaBatch.ts +72 -0
- package/src/response/ResponseMetaType.ts +108 -0
- package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
- package/src/response/ResponsePolicy.ts +90 -0
- package/src/response/ResponseProposeMolecule.ts +155 -0
- package/src/response/ResponseQueryActiveSession.ts +105 -0
- package/src/response/ResponseQueryUserActivity.ts +89 -0
- package/src/response/ResponseRequestAuthorization.ts +100 -0
- package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
- package/src/response/ResponseRequestTokens.ts +58 -0
- package/src/response/ResponseTransferTokens.ts +72 -0
- package/src/response/ResponseWalletBundle.ts +95 -0
- package/src/response/ResponseWalletList.ts +165 -0
- package/src/schemas/index.ts +457 -0
- package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
- package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
- package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
- package/src/subscribe/Subscribe.ts +182 -0
- package/src/subscribe/WalletStatusSubscribe.ts +70 -0
- package/src/subscribe/index.ts +59 -0
- package/src/types/assertions.ts +722 -0
- package/src/types/client.ts +567 -0
- package/src/types/crypto.ts +541 -0
- package/src/types/graphql.ts +630 -0
- package/src/types/guards.ts +659 -0
- package/src/types/index.ts +614 -0
- package/src/types/response.ts +133 -0
- package/src/types/template-literals.ts +382 -0
- package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
- package/src/validation/ValidationService.ts +607 -0
- package/src/validation/schemas.ts +447 -0
- package/src/versions/HashAtom.ts +170 -0
- package/src/versions/Version4.ts +120 -0
- package/src/versions/index.ts +80 -0
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
/*
|
|
2
|
+
(
|
|
3
|
+
(/(
|
|
4
|
+
(//(
|
|
5
|
+
(///(
|
|
6
|
+
(/////(
|
|
7
|
+
(//////( )
|
|
8
|
+
(////////( (/)
|
|
9
|
+
(////////( (///)
|
|
10
|
+
(//////////( (////)
|
|
11
|
+
(//////////( (//////)
|
|
12
|
+
(////////////( (///////)
|
|
13
|
+
(/////////////( (/////////)
|
|
14
|
+
(//////////////( (///////////)
|
|
15
|
+
(///////////////( (/////////////)
|
|
16
|
+
(////////////////( (//////////////)
|
|
17
|
+
((((((((((((((((((( (((((((((((((((
|
|
18
|
+
((((((((((((((((((( ((((((((((((((
|
|
19
|
+
((((((((((((((((((( ((((((((((((((
|
|
20
|
+
(((((((((((((((((((( (((((((((((((
|
|
21
|
+
(((((((((((((((((((( ((((((((((((
|
|
22
|
+
((((((((((((((((((( ((((((((((((
|
|
23
|
+
((((((((((((((((((( ((((((((((
|
|
24
|
+
((((((((((((((((((/ (((((((((
|
|
25
|
+
(((((((((((((((((( ((((((((
|
|
26
|
+
((((((((((((((((( (((((((
|
|
27
|
+
(((((((((((((((((( (((((
|
|
28
|
+
################# ##
|
|
29
|
+
################ #
|
|
30
|
+
################# ##
|
|
31
|
+
%################ ###
|
|
32
|
+
###############( ####
|
|
33
|
+
############### ####
|
|
34
|
+
############### ######
|
|
35
|
+
%#############( (#######
|
|
36
|
+
%############# #########
|
|
37
|
+
############( ##########
|
|
38
|
+
########### #############
|
|
39
|
+
######### ##############
|
|
40
|
+
%######
|
|
41
|
+
|
|
42
|
+
Powered by Knish.IO: Connecting a Decentralized World
|
|
43
|
+
|
|
44
|
+
Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
|
|
45
|
+
|
|
46
|
+
License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Cryptographic library for KnishIO TypeScript SDK
|
|
51
|
+
* Ensures 100% compatibility with JavaScript SDK and cross-platform consistency
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
import JsSHA from 'jssha'
|
|
55
|
+
import { randomString } from './strings'
|
|
56
|
+
import type {
|
|
57
|
+
BundleHash,
|
|
58
|
+
Position,
|
|
59
|
+
SHAKE256Options,
|
|
60
|
+
SecretGenerationOptions,
|
|
61
|
+
BundleHashOptions,
|
|
62
|
+
PositionGenerationOptions
|
|
63
|
+
} from '@/types'
|
|
64
|
+
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// CONSTANTS - MUST MATCH JavaScript SDK EXACTLY
|
|
67
|
+
// =============================================================================
|
|
68
|
+
|
|
69
|
+
export const CRYPTO_CONSTANTS = {
|
|
70
|
+
SECRET_LENGTH: 2048,
|
|
71
|
+
BUNDLE_HASH_LENGTH: 64,
|
|
72
|
+
POSITION_LENGTH: 64,
|
|
73
|
+
ADDRESS_LENGTH: 64,
|
|
74
|
+
MOLECULAR_HASH_OUTPUT_LENGTH: 256,
|
|
75
|
+
SHAKE256_OUTPUT_LENGTH: 256,
|
|
76
|
+
KEY_FRAGMENT_SIZE: 128,
|
|
77
|
+
HASH_ITERATIONS: 16
|
|
78
|
+
} as const
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// SHAKE256 IMPLEMENTATION - CRITICAL FOR CROSS-SDK COMPATIBILITY
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* SHAKE256 hash function - MUST produce identical outputs to JavaScript SDK
|
|
86
|
+
* Test vectors from validation/common-config.json:
|
|
87
|
+
* - shake256("test", 256) === "b54ff7255705a71ee2925e4a3e30e41aed489a579d5595e0df13e32e1e4dd202"
|
|
88
|
+
* - shake256("KnishIO", 256) === "35e3c3f33aefb940baaf430855ccb441c24b7b0542f682b8543f4c9d3a077c6e"
|
|
89
|
+
*
|
|
90
|
+
* @param input - Input string to hash
|
|
91
|
+
* @param outputLength - Output length in BITS (256 bits = 64 hex chars)
|
|
92
|
+
*/
|
|
93
|
+
export function shake256(input: string, outputLength: number): string {
|
|
94
|
+
const sponge = new JsSHA('SHAKE256', 'TEXT')
|
|
95
|
+
sponge.update(input)
|
|
96
|
+
|
|
97
|
+
// CRITICAL FIX: outputLength parameter is in BITS (as per Implementation Guide)
|
|
98
|
+
// JsSHA outputLen expects bits, so pass outputLength directly
|
|
99
|
+
// 256 bits = 64 hex characters, 512 bits = 128 hex characters, etc.
|
|
100
|
+
const result = sponge.getHash('HEX', { outputLen: outputLength })
|
|
101
|
+
|
|
102
|
+
return result.toLowerCase()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Enhanced SHAKE256 with options
|
|
107
|
+
* @param options.input - Input string or Uint8Array to hash
|
|
108
|
+
* @param options.outputLength - Output length in BITS
|
|
109
|
+
* @param options.encoding - Output encoding format
|
|
110
|
+
*/
|
|
111
|
+
export function shake256Enhanced(options: SHAKE256Options): string {
|
|
112
|
+
const { input, outputLength, encoding = 'hex' } = options
|
|
113
|
+
|
|
114
|
+
const sponge = new JsSHA('SHAKE256', typeof input === 'string' ? 'TEXT' : 'UINT8ARRAY')
|
|
115
|
+
sponge.update(input)
|
|
116
|
+
|
|
117
|
+
// outputLength is in BITS consistently across all encodings
|
|
118
|
+
switch (encoding) {
|
|
119
|
+
case 'hex':
|
|
120
|
+
return sponge.getHash('HEX', { outputLen: outputLength }).toLowerCase()
|
|
121
|
+
case 'base64':
|
|
122
|
+
return sponge.getHash('B64', { outputLen: outputLength })
|
|
123
|
+
case 'bytes':
|
|
124
|
+
return sponge.getHash('UINT8ARRAY', { outputLen: outputLength }).toString()
|
|
125
|
+
default:
|
|
126
|
+
throw new Error(`Unsupported encoding: ${encoding}`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// SECRET GENERATION - MUST MATCH JavaScript SDK BEHAVIOR
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate secret from seed or random - MUST match JS SDK exactly
|
|
136
|
+
* If seed provided: uses SHAKE256 to generate deterministic secret
|
|
137
|
+
* If no seed: uses cryptographically secure random string
|
|
138
|
+
*/
|
|
139
|
+
export function generateSecret(seed: string | null = null, length = CRYPTO_CONSTANTS.SECRET_LENGTH): string {
|
|
140
|
+
if (seed) {
|
|
141
|
+
// Deterministic generation using SHAKE256 - MUST match JS SDK
|
|
142
|
+
const sponge = new JsSHA('SHAKE256', 'TEXT')
|
|
143
|
+
sponge.update(seed)
|
|
144
|
+
// Fix: outputLen is in BITS, so for 1024 hex chars output, we need length*2 bits (matching JS SDK)
|
|
145
|
+
return sponge.getHash('HEX', { outputLen: length * 2 }).toLowerCase()
|
|
146
|
+
} else {
|
|
147
|
+
// Cryptographically secure random generation
|
|
148
|
+
return randomString(length)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Enhanced secret generation with options
|
|
154
|
+
*/
|
|
155
|
+
export function generateSecretEnhanced(options: SecretGenerationOptions): string {
|
|
156
|
+
const {
|
|
157
|
+
seed = null,
|
|
158
|
+
length = CRYPTO_CONSTANTS.SECRET_LENGTH,
|
|
159
|
+
algorithm = 'SHAKE256'
|
|
160
|
+
} = options
|
|
161
|
+
|
|
162
|
+
if (algorithm !== 'SHAKE256') {
|
|
163
|
+
throw new Error('Only SHAKE256 algorithm is supported for cross-platform compatibility')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return generateSecret(seed, length)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// BUNDLE HASH GENERATION - MUST MATCH JavaScript SDK BEHAVIOR
|
|
171
|
+
// =============================================================================
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate bundle hash from secret - MUST produce identical results to JS SDK
|
|
175
|
+
* Uses SHAKE256 with 256-bit output (64 hex characters)
|
|
176
|
+
*/
|
|
177
|
+
export function generateBundleHash(secret: string, source: string | null = null): BundleHash {
|
|
178
|
+
if (!secret || secret.length === 0) {
|
|
179
|
+
throw new Error('Secret is required for bundle hash generation')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let input = secret
|
|
183
|
+
if (source) {
|
|
184
|
+
input = secret + source
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Fix: BUNDLE_HASH_LENGTH is 64 (hex chars), but shake256 expects bits
|
|
188
|
+
// So we multiply by 4: 64 hex chars * 4 = 256 bits (matching JS SDK)
|
|
189
|
+
const hash = shake256(input, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH * 4)
|
|
190
|
+
return hash as BundleHash
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Enhanced bundle hash generation with options
|
|
195
|
+
*/
|
|
196
|
+
export function generateBundleHashEnhanced(options: BundleHashOptions): BundleHash {
|
|
197
|
+
const { secret, source = null, algorithm = 'SHAKE256' } = options
|
|
198
|
+
|
|
199
|
+
if (algorithm !== 'SHAKE256') {
|
|
200
|
+
throw new Error('Only SHAKE256 algorithm is supported for cross-platform compatibility')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return generateBundleHash(secret, source)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// POSITION GENERATION - CRYPTOGRAPHICALLY SECURE RANDOM POSITIONS
|
|
208
|
+
// =============================================================================
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Generate cryptographically secure position
|
|
212
|
+
* Must be 64 hex characters (256 bits)
|
|
213
|
+
*/
|
|
214
|
+
export function generatePosition(): Position {
|
|
215
|
+
// Generate 32 random bytes (256 bits) and convert to hex
|
|
216
|
+
const position = randomString(CRYPTO_CONSTANTS.POSITION_LENGTH, '0123456789abcdef')
|
|
217
|
+
return position as Position
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Enhanced position generation with options
|
|
222
|
+
*/
|
|
223
|
+
export function generatePositionEnhanced(options: PositionGenerationOptions = {}): Position {
|
|
224
|
+
const {
|
|
225
|
+
entropy = null,
|
|
226
|
+
algorithm = 'SHAKE256',
|
|
227
|
+
length = CRYPTO_CONSTANTS.POSITION_LENGTH
|
|
228
|
+
} = options
|
|
229
|
+
|
|
230
|
+
if (entropy) {
|
|
231
|
+
// Use provided entropy with SHAKE256
|
|
232
|
+
const entropyString = Array.from(entropy).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
233
|
+
const hash = shake256(entropyString, length)
|
|
234
|
+
return hash as Position
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (length !== CRYPTO_CONSTANTS.POSITION_LENGTH) {
|
|
238
|
+
throw new Error(`Position length must be exactly ${CRYPTO_CONSTANTS.POSITION_LENGTH} characters`)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return generatePosition()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// =============================================================================
|
|
245
|
+
// BATCH ID GENERATION - DETERMINISTIC BATCH IDENTIFICATION
|
|
246
|
+
// =============================================================================
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Generate batch ID from molecular hash and index
|
|
250
|
+
* Uses SHAKE256 to create deterministic batch identifiers
|
|
251
|
+
*/
|
|
252
|
+
export function generateBatchId({
|
|
253
|
+
molecularHash = null,
|
|
254
|
+
index = null
|
|
255
|
+
}: {
|
|
256
|
+
molecularHash?: string | null
|
|
257
|
+
index?: number | null
|
|
258
|
+
} = {}): string {
|
|
259
|
+
let input = ''
|
|
260
|
+
|
|
261
|
+
if (molecularHash) {
|
|
262
|
+
input += molecularHash
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (index !== null) {
|
|
266
|
+
input += index.toString()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If no input provided, generate random batch ID
|
|
270
|
+
if (input === '') {
|
|
271
|
+
input = randomString(32)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Use SHAKE256 to generate 64-character batch ID
|
|
275
|
+
return shake256(input, 64)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// =============================================================================
|
|
279
|
+
// WALLET KEY GENERATION - MUST MATCH JavaScript SDK EXACTLY
|
|
280
|
+
// =============================================================================
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generate wallet key from secret, token, and position
|
|
284
|
+
* MUST match JS SDK algorithm exactly for cross-platform compatibility
|
|
285
|
+
*
|
|
286
|
+
* Algorithm from JS SDK Wallet.generateKey():
|
|
287
|
+
* 1. Convert secret to BigInt
|
|
288
|
+
* 2. Add position as BigInt
|
|
289
|
+
* 3. Convert result to hex string
|
|
290
|
+
* 4. Hash with SHAKE256 (8192 bits)
|
|
291
|
+
* 5. If token provided, append token and hash again
|
|
292
|
+
* 6. Final hash with SHAKE256 (8192 bits)
|
|
293
|
+
*/
|
|
294
|
+
export function generateWalletKey({
|
|
295
|
+
secret,
|
|
296
|
+
token,
|
|
297
|
+
position
|
|
298
|
+
}: {
|
|
299
|
+
secret: string
|
|
300
|
+
token: string | null
|
|
301
|
+
position: string
|
|
302
|
+
}): string {
|
|
303
|
+
if (!secret || !position) {
|
|
304
|
+
throw new Error('Secret and position are required for key generation')
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
// Step 1: Convert secret to BigInt (must match JS SDK)
|
|
309
|
+
const bigIntSecret = BigInt(`0x${secret}`)
|
|
310
|
+
|
|
311
|
+
// Step 2: Add position as BigInt
|
|
312
|
+
const indexedKey = bigIntSecret + BigInt(`0x${position}`)
|
|
313
|
+
|
|
314
|
+
// Step 3: Create intermediate key sponge
|
|
315
|
+
const intermediateKeySponge = new JsSHA('SHAKE256', 'TEXT')
|
|
316
|
+
|
|
317
|
+
// Step 4: Update with hex string of indexed key
|
|
318
|
+
intermediateKeySponge.update(indexedKey.toString(16))
|
|
319
|
+
|
|
320
|
+
// Step 5: If token provided, update the SAME sponge with token
|
|
321
|
+
if (token) {
|
|
322
|
+
intermediateKeySponge.update(token)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Step 6: Get intermediate hash (8192 bits = 2048 hex chars)
|
|
326
|
+
// Step 7: Create new sponge for private key
|
|
327
|
+
const privateKeySponge = new JsSHA('SHAKE256', 'TEXT')
|
|
328
|
+
privateKeySponge.update(intermediateKeySponge.getHash('HEX', { outputLen: 8192 }))
|
|
329
|
+
|
|
330
|
+
// Return final key (8192 bits = 2048 hex chars) - JS SDK doesn't call toLowerCase()
|
|
331
|
+
return privateKeySponge.getHash('HEX', { outputLen: 8192 })
|
|
332
|
+
|
|
333
|
+
} catch (error) {
|
|
334
|
+
throw new Error(`Key generation failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// =============================================================================
|
|
339
|
+
// WALLET ADDRESS GENERATION - MUST MATCH JavaScript SDK EXACTLY
|
|
340
|
+
// =============================================================================
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Generate wallet address from key
|
|
344
|
+
* MUST match JS SDK algorithm exactly for cross-platform compatibility
|
|
345
|
+
*
|
|
346
|
+
* Algorithm from JS SDK Wallet.generateAddress():
|
|
347
|
+
* 1. Split key into 16 fragments of 128 characters each
|
|
348
|
+
* 2. For each fragment, hash 16 times with SHAKE256
|
|
349
|
+
* 3. Combine all fragments and hash with SHAKE256
|
|
350
|
+
* 4. Final hash with SHAKE256 (256 bits = 64 hex chars)
|
|
351
|
+
*/
|
|
352
|
+
export function generateWalletAddress(key: string): string {
|
|
353
|
+
if (!key || key.length < 2048) {
|
|
354
|
+
throw new Error('Key must be at least 2048 characters for address generation')
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
// Step 1: Split into 16 fragments of 128 characters each
|
|
359
|
+
const keyFragments = []
|
|
360
|
+
for (let i = 0; i < key.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
|
|
361
|
+
keyFragments.push(key.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Step 2: Hash each fragment 16 times
|
|
365
|
+
const digestSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
366
|
+
|
|
367
|
+
for (const fragment of keyFragments) {
|
|
368
|
+
let workingFragment = fragment
|
|
369
|
+
|
|
370
|
+
// Hash the fragment 16 times
|
|
371
|
+
for (let fragmentCount = 1; fragmentCount <= CRYPTO_CONSTANTS.HASH_ITERATIONS; fragmentCount++) {
|
|
372
|
+
const workingSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
373
|
+
workingSponge.update(workingFragment)
|
|
374
|
+
workingFragment = workingSponge.getHash('HEX', { outputLen: 512 })
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Add processed fragment to digest
|
|
378
|
+
digestSponge.update(workingFragment)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Step 3: Get intermediate digest
|
|
382
|
+
const intermediateDigest = digestSponge.getHash('HEX', { outputLen: 8192 })
|
|
383
|
+
|
|
384
|
+
// Step 4: Final hash to create address
|
|
385
|
+
const outputSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
386
|
+
outputSponge.update(intermediateDigest)
|
|
387
|
+
|
|
388
|
+
return outputSponge.getHash('HEX', { outputLen: CRYPTO_CONSTANTS.ADDRESS_LENGTH * 4 })
|
|
389
|
+
|
|
390
|
+
} catch (error) {
|
|
391
|
+
throw new Error(`Address generation failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// =============================================================================
|
|
396
|
+
// VALIDATION FUNCTIONS - ENSURE DATA INTEGRITY
|
|
397
|
+
// =============================================================================
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Validate SHAKE256 hash format and length
|
|
401
|
+
*/
|
|
402
|
+
export function validateSHAKE256Hash(hash: string, expectedLength: number): boolean {
|
|
403
|
+
if (!hash || typeof hash !== 'string') return false
|
|
404
|
+
if (hash.length !== expectedLength) return false
|
|
405
|
+
return /^[0-9a-f]+$/i.test(hash)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Validate secret format and length
|
|
410
|
+
*/
|
|
411
|
+
export function validateSecret(secret: string): boolean {
|
|
412
|
+
if (!secret || typeof secret !== 'string') return false
|
|
413
|
+
if (secret.length !== CRYPTO_CONSTANTS.SECRET_LENGTH) return false
|
|
414
|
+
return /^[0-9a-f]+$/i.test(secret)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Validate bundle hash format
|
|
419
|
+
*/
|
|
420
|
+
export function validateBundleHash(bundleHash: string): boolean {
|
|
421
|
+
return validateSHAKE256Hash(bundleHash, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Validate position format
|
|
426
|
+
*/
|
|
427
|
+
export function validatePosition(position: string): boolean {
|
|
428
|
+
return validateSHAKE256Hash(position, CRYPTO_CONSTANTS.POSITION_LENGTH)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Validate wallet address format
|
|
433
|
+
*/
|
|
434
|
+
export function validateWalletAddress(address: string): boolean {
|
|
435
|
+
return validateSHAKE256Hash(address, CRYPTO_CONSTANTS.ADDRESS_LENGTH)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// =============================================================================
|
|
439
|
+
// COMPATIBILITY TESTING - VERIFY CROSS-PLATFORM COMPATIBILITY
|
|
440
|
+
// =============================================================================
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Test vector for SHAKE256 compatibility
|
|
444
|
+
* These values MUST match across all SDK implementations
|
|
445
|
+
*/
|
|
446
|
+
export const COMPATIBILITY_TEST_VECTORS = {
|
|
447
|
+
shake256: [
|
|
448
|
+
{
|
|
449
|
+
input: 'test',
|
|
450
|
+
outputLength: 256,
|
|
451
|
+
expected: 'b54ff7255705a71ee2925e4a3e30e41aed489a579d5595e0df13e32e1e4dd202'
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
input: 'KnishIO',
|
|
455
|
+
outputLength: 256,
|
|
456
|
+
expected: '35e3c3f33aefb940baaf430855ccb441c24b7b0542f682b8543f4c9d3a077c6e'
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
} as const
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Run compatibility tests to verify cross-platform consistency
|
|
463
|
+
*/
|
|
464
|
+
export function runCompatibilityTests(): { passed: boolean; results: Array<{ test: string; passed: boolean; expected: string; actual: string }> } {
|
|
465
|
+
const results = []
|
|
466
|
+
let allPassed = true
|
|
467
|
+
|
|
468
|
+
for (const testVector of COMPATIBILITY_TEST_VECTORS.shake256) {
|
|
469
|
+
const actual = shake256(testVector.input, testVector.outputLength)
|
|
470
|
+
const passed = actual === testVector.expected
|
|
471
|
+
|
|
472
|
+
results.push({
|
|
473
|
+
test: `shake256("${testVector.input}", ${testVector.outputLength})`,
|
|
474
|
+
passed,
|
|
475
|
+
expected: testVector.expected,
|
|
476
|
+
actual
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
if (!passed) {
|
|
480
|
+
allPassed = false
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return { passed: allPassed, results }
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// =============================================================================
|
|
488
|
+
// BASE17 CONVERSION SYSTEM - CRITICAL FOR MOLECULAR HASH PROCESSING
|
|
489
|
+
// =============================================================================
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Base conversion utility for molecular hash processing
|
|
493
|
+
* MUST match JavaScript SDK implementation exactly
|
|
494
|
+
*/
|
|
495
|
+
export function charsetBaseConvert(
|
|
496
|
+
input: string,
|
|
497
|
+
fromBase: number,
|
|
498
|
+
toBase: number,
|
|
499
|
+
fromCharset: string,
|
|
500
|
+
toCharset: string
|
|
501
|
+
): string {
|
|
502
|
+
if (input === '') return toCharset[0]
|
|
503
|
+
|
|
504
|
+
// Convert input from source base to decimal using BigInt for precision
|
|
505
|
+
let decimal = 0n
|
|
506
|
+
for (let i = 0; i < input.length; i++) {
|
|
507
|
+
const char = input[i]
|
|
508
|
+
const value = BigInt(fromCharset.indexOf(char))
|
|
509
|
+
if (value === -1n) {
|
|
510
|
+
throw new Error(`Invalid character '${char}' for source base ${fromBase}`)
|
|
511
|
+
}
|
|
512
|
+
decimal = decimal * BigInt(fromBase) + value
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Convert decimal to target base
|
|
516
|
+
if (decimal === 0n) return toCharset[0]
|
|
517
|
+
|
|
518
|
+
let result = ''
|
|
519
|
+
while (decimal > 0n) {
|
|
520
|
+
const remainder = decimal % BigInt(toBase)
|
|
521
|
+
result = toCharset[Number(remainder)] + result
|
|
522
|
+
decimal = decimal / BigInt(toBase)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return result
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Convert molecular hash from base16 to base17
|
|
530
|
+
* Critical for WOTS+ signature generation
|
|
531
|
+
*/
|
|
532
|
+
export function convertToBase17(hexHash: string): string {
|
|
533
|
+
const base16Charset = '0123456789abcdef'
|
|
534
|
+
const base17Charset = '0123456789abcdefg'
|
|
535
|
+
|
|
536
|
+
const base17Hash = charsetBaseConvert(hexHash, 16, 17, base16Charset, base17Charset)
|
|
537
|
+
|
|
538
|
+
// Pad to 64 characters as required by Implementation Guide
|
|
539
|
+
return base17Hash.padStart(64, '0')
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// =============================================================================
|
|
543
|
+
// MOLECULAR HASH ENUMERATION AND NORMALIZATION - FOR WOTS+ SIGNATURES
|
|
544
|
+
// =============================================================================
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Enumerate molecular hash for signature generation
|
|
548
|
+
* MUST match JavaScript SDK Molecule.enumerate() exactly
|
|
549
|
+
*/
|
|
550
|
+
export function enumerateMolecularHash(hash: string): number[] {
|
|
551
|
+
const mapped: Record<string, number> = {
|
|
552
|
+
'0': -8, '1': -7, '2': -6, '3': -5, '4': -4, '5': -3, '6': -2, '7': -1,
|
|
553
|
+
'8': 0, '9': 1, 'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const target: number[] = []
|
|
557
|
+
const hashList = hash.toLowerCase().split('')
|
|
558
|
+
|
|
559
|
+
for (let index = 0; index < hashList.length; index++) {
|
|
560
|
+
const symbol = hashList[index]
|
|
561
|
+
if (typeof mapped[symbol] !== 'undefined') {
|
|
562
|
+
target[index] = mapped[symbol]
|
|
563
|
+
} else {
|
|
564
|
+
throw new Error(`Invalid character '${symbol}' in molecular hash`)
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return target
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Normalize enumerated molecular hash for signature generation
|
|
573
|
+
* MUST match JavaScript SDK Molecule.normalize() exactly
|
|
574
|
+
*/
|
|
575
|
+
export function normalizeMolecularHash(mappedHashArray: number[]): number[] {
|
|
576
|
+
// Create a copy to avoid mutating the original
|
|
577
|
+
const normalized = [...mappedHashArray]
|
|
578
|
+
|
|
579
|
+
let total = normalized.reduce((sum, num) => sum + num, 0)
|
|
580
|
+
const totalCondition = total < 0
|
|
581
|
+
|
|
582
|
+
while (total !== 0) {
|
|
583
|
+
for (let index = 0; index < normalized.length; index++) {
|
|
584
|
+
const condition = totalCondition
|
|
585
|
+
? normalized[index] < 8
|
|
586
|
+
: normalized[index] > -8
|
|
587
|
+
|
|
588
|
+
if (condition) {
|
|
589
|
+
if (totalCondition) {
|
|
590
|
+
normalized[index]++
|
|
591
|
+
total++
|
|
592
|
+
} else {
|
|
593
|
+
normalized[index]--
|
|
594
|
+
total--
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (total === 0) break
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return normalized
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// =============================================================================
|
|
606
|
+
// WOTS+ ONE-TIME SIGNATURE IMPLEMENTATION - CRITICAL FOR TRANSACTION SIGNING
|
|
607
|
+
// =============================================================================
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Generate WOTS+ one-time signature for molecular hash
|
|
611
|
+
* MUST match JavaScript SDK implementation exactly for cross-platform compatibility
|
|
612
|
+
*/
|
|
613
|
+
export function generateOTSSignature(privateKey: string, molecularHash: string): string {
|
|
614
|
+
if (!privateKey || privateKey.length < 2048) {
|
|
615
|
+
throw new Error('Private key must be at least 2048 characters for OTS signature')
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (!molecularHash || molecularHash.length !== 64) {
|
|
619
|
+
throw new Error('Molecular hash must be exactly 64 characters')
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
try {
|
|
623
|
+
// Step 1: Molecular hash is already base17 from Atom.hashAtoms(), enumerate and normalize it
|
|
624
|
+
// Fix: Don't convert to base17 again - it's already base17 (matching JS SDK behavior)
|
|
625
|
+
const enumerated = enumerateMolecularHash(molecularHash)
|
|
626
|
+
const normalized = normalizeMolecularHash(enumerated)
|
|
627
|
+
|
|
628
|
+
// Step 2: Subdivide private key into 16 chunks of 128 characters each
|
|
629
|
+
const keyChunks: string[] = []
|
|
630
|
+
for (let i = 0; i < privateKey.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
|
|
631
|
+
keyChunks.push(privateKey.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Step 3: Generate signature fragments
|
|
635
|
+
let signatureFragments = ''
|
|
636
|
+
for (let index = 0; index < keyChunks.length; index++) {
|
|
637
|
+
let workingChunk = keyChunks[index]
|
|
638
|
+
|
|
639
|
+
// Hash (8 - normalized[index]) times for signature generation
|
|
640
|
+
const iterations = 8 - (normalized[index] || 0)
|
|
641
|
+
for (let j = 0; j < iterations; j++) {
|
|
642
|
+
const sponge = new JsSHA('SHAKE256', 'TEXT')
|
|
643
|
+
sponge.update(workingChunk)
|
|
644
|
+
workingChunk = sponge.getHash('HEX', { outputLen: 512 }) // 512 bits
|
|
645
|
+
}
|
|
646
|
+
signatureFragments += workingChunk
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return signatureFragments
|
|
650
|
+
} catch (error) {
|
|
651
|
+
throw new Error(`OTS signature generation failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Verify WOTS+ one-time signature against molecular hash and address
|
|
657
|
+
* MUST match JavaScript SDK implementation exactly
|
|
658
|
+
*/
|
|
659
|
+
export function verifyOTSSignature(
|
|
660
|
+
otsFragments: string,
|
|
661
|
+
molecularHash: string,
|
|
662
|
+
signingAddress: string
|
|
663
|
+
): boolean {
|
|
664
|
+
if (!otsFragments || !molecularHash || !signingAddress) {
|
|
665
|
+
return false
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
try {
|
|
669
|
+
// Step 1: Convert molecular hash to base17 and normalize
|
|
670
|
+
const base17Hash = convertToBase17(molecularHash)
|
|
671
|
+
const enumerated = enumerateMolecularHash(base17Hash)
|
|
672
|
+
const normalized = normalizeMolecularHash(enumerated)
|
|
673
|
+
|
|
674
|
+
// Step 2: Process OTS fragments (handle compression if needed)
|
|
675
|
+
let ots = otsFragments
|
|
676
|
+
if (ots.length !== 2048) {
|
|
677
|
+
// If not 2048 chars, it might be base64 compressed - for now, assume uncompressed
|
|
678
|
+
if (ots.length !== 2048) {
|
|
679
|
+
return false // Invalid signature length
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Step 3: Subdivide OTS into 16 segments of 128 characters each
|
|
684
|
+
const otsChunks: string[] = []
|
|
685
|
+
for (let i = 0; i < ots.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
|
|
686
|
+
otsChunks.push(ots.substring(i, i + CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE))
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Step 4: Process each chunk for verification
|
|
690
|
+
let keyFragments = ''
|
|
691
|
+
for (let index = 0; index < otsChunks.length; index++) {
|
|
692
|
+
let workingChunk = otsChunks[index]
|
|
693
|
+
|
|
694
|
+
// Hash (8 + normalized[index]) times for verification
|
|
695
|
+
const iterations = 8 + (normalized[index] || 0)
|
|
696
|
+
for (let j = 0; j < iterations; j++) {
|
|
697
|
+
const sponge = new JsSHA('SHAKE256', 'TEXT')
|
|
698
|
+
sponge.update(workingChunk)
|
|
699
|
+
workingChunk = sponge.getHash('HEX', { outputLen: 512 }) // 512 bits
|
|
700
|
+
}
|
|
701
|
+
keyFragments += workingChunk
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Step 5: Generate digest from reconstructed key fragments
|
|
705
|
+
const digestSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
706
|
+
digestSponge.update(keyFragments)
|
|
707
|
+
const digest = digestSponge.getHash('HEX', { outputLen: 8192 }) // 8192 bits
|
|
708
|
+
|
|
709
|
+
// Step 6: Generate address from digest
|
|
710
|
+
const addressSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
711
|
+
addressSponge.update(digest)
|
|
712
|
+
const reconstructedAddress = addressSponge.getHash('HEX', { outputLen: 256 }) // 256 bits
|
|
713
|
+
|
|
714
|
+
// Step 7: Compare with expected signing address
|
|
715
|
+
return reconstructedAddress.toLowerCase() === signingAddress.toLowerCase()
|
|
716
|
+
} catch (error) {
|
|
717
|
+
return false
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// =============================================================================
|
|
722
|
+
// ENHANCED VALIDATION FUNCTIONS - SUPPORT FOR NEW SIGNATURE SYSTEM
|
|
723
|
+
// =============================================================================
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Validate OTS signature format
|
|
727
|
+
*/
|
|
728
|
+
export function validateOTSSignature(signature: string): boolean {
|
|
729
|
+
if (!signature || typeof signature !== 'string') return false
|
|
730
|
+
|
|
731
|
+
// OTS signature should be 2048 characters (16 chunks * 128 chars each)
|
|
732
|
+
// or compressed format
|
|
733
|
+
return signature.length === 2048 || (signature.length > 0 && signature.length < 2048)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Validate molecular hash for base17 conversion
|
|
738
|
+
*/
|
|
739
|
+
export function validateMolecularHashForSignature(hash: string): boolean {
|
|
740
|
+
if (!hash || typeof hash !== 'string') return false
|
|
741
|
+
if (hash.length !== 64) return false
|
|
742
|
+
return /^[0-9a-f]+$/i.test(hash)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// =============================================================================
|
|
746
|
+
// UPDATED COMPATIBILITY TEST VECTORS - INCLUDING SIGNATURE TESTS
|
|
747
|
+
// =============================================================================
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Extended test vectors including signature generation
|
|
751
|
+
*/
|
|
752
|
+
export const EXTENDED_COMPATIBILITY_TEST_VECTORS = {
|
|
753
|
+
...COMPATIBILITY_TEST_VECTORS,
|
|
754
|
+
base17Conversion: [
|
|
755
|
+
{
|
|
756
|
+
input: '0000000000000000000000000000000000000000000000000000000000000000',
|
|
757
|
+
expected: '0000000000000000000000000000000000000000000000000000000000000000'
|
|
758
|
+
}
|
|
759
|
+
],
|
|
760
|
+
molecularEnumeration: [
|
|
761
|
+
{
|
|
762
|
+
input: '0000000000000000000000000000000000000000000000000000000000000000',
|
|
763
|
+
expected: [-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8]
|
|
764
|
+
}
|
|
765
|
+
]
|
|
766
|
+
} as const
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Run extended compatibility tests including signature system
|
|
770
|
+
*/
|
|
771
|
+
export function runExtendedCompatibilityTests(): {
|
|
772
|
+
passed: boolean
|
|
773
|
+
results: Array<{ test: string; passed: boolean; expected: any; actual: any }>
|
|
774
|
+
} {
|
|
775
|
+
const results = []
|
|
776
|
+
let allPassed = true
|
|
777
|
+
|
|
778
|
+
// Run original SHAKE256 tests
|
|
779
|
+
const originalResults = runCompatibilityTests()
|
|
780
|
+
results.push(...originalResults.results)
|
|
781
|
+
if (!originalResults.passed) allPassed = false
|
|
782
|
+
|
|
783
|
+
// Test base17 conversion
|
|
784
|
+
for (const testVector of EXTENDED_COMPATIBILITY_TEST_VECTORS.base17Conversion) {
|
|
785
|
+
const actual = convertToBase17(testVector.input)
|
|
786
|
+
const passed = actual === testVector.expected
|
|
787
|
+
|
|
788
|
+
results.push({
|
|
789
|
+
test: `convertToBase17("${testVector.input}")`,
|
|
790
|
+
passed,
|
|
791
|
+
expected: testVector.expected,
|
|
792
|
+
actual
|
|
793
|
+
})
|
|
794
|
+
|
|
795
|
+
if (!passed) allPassed = false
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Test molecular enumeration
|
|
799
|
+
for (const testVector of EXTENDED_COMPATIBILITY_TEST_VECTORS.molecularEnumeration) {
|
|
800
|
+
const actual = enumerateMolecularHash(testVector.input)
|
|
801
|
+
const passed = JSON.stringify(actual) === JSON.stringify(testVector.expected)
|
|
802
|
+
|
|
803
|
+
results.push({
|
|
804
|
+
test: `enumerateMolecularHash("${testVector.input}")`,
|
|
805
|
+
passed,
|
|
806
|
+
expected: testVector.expected,
|
|
807
|
+
actual
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
if (!passed) allPassed = false
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return { passed: allPassed, results }
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// All functions are already exported individually above
|