@wishknish/knishio-client-js 0.5.2 → 0.6.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 +207 -274
- package/dist/client.iife.js +533 -0
- package/package.json +37 -79
- package/src/.babelrc +0 -22
- package/src/Atom.js +171 -132
- package/src/AtomMeta.js +76 -50
- package/src/AuthToken.js +38 -47
- package/src/KnishIOClient.js +934 -987
- package/src/Meta.js +15 -17
- package/src/Molecule.js +423 -494
- package/src/PolicyMeta.js +32 -41
- package/src/TokenUnit.js +30 -32
- package/src/Wallet.js +275 -265
- package/src/exception/AtomIndexException.js +4 -8
- package/src/exception/AtomsMissingException.js +4 -6
- package/src/exception/AuthorizationRejectedException.js +4 -5
- package/src/exception/BalanceInsufficientException.js +4 -8
- package/src/exception/BaseException.js +6 -8
- package/src/exception/BatchIdException.js +5 -7
- package/src/exception/CodeException.js +4 -8
- package/src/{libraries/ApolloLink/HttpLink.js → exception/DecryptionKeyException.js} +12 -15
- package/src/exception/InvalidResponseException.js +4 -5
- package/src/exception/MetaMissingException.js +4 -6
- package/src/exception/MolecularHashMismatchException.js +4 -6
- package/src/exception/MolecularHashMissingException.js +4 -5
- package/src/exception/NegativeAmountException.js +4 -5
- package/src/exception/PolicyInvalidException.js +4 -4
- package/src/exception/SignatureMalformedException.js +4 -5
- package/src/exception/SignatureMismatchException.js +4 -5
- package/src/exception/StackableUnitAmountException.js +4 -5
- package/src/exception/StackableUnitDecimalsException.js +4 -5
- package/src/exception/TransferBalanceException.js +4 -5
- package/src/exception/TransferMalformedException.js +4 -5
- package/src/exception/TransferMismatchedException.js +4 -5
- package/src/exception/TransferRemainderException.js +4 -5
- package/src/exception/TransferToSelfException.js +4 -5
- package/src/exception/TransferUnbalancedException.js +4 -5
- package/src/exception/UnauthenticatedException.js +4 -5
- package/src/{libraries/ApolloLink/AuthLink.js → exception/WalletCredentialException.js} +12 -40
- package/src/exception/WalletShadowException.js +4 -5
- package/src/exception/WrongTokenTypeException.js +4 -5
- package/src/exception/index.js +26 -26
- package/src/index.js +8 -10
- package/src/instance/Rules/Callback.js +91 -93
- package/src/instance/Rules/Condition.js +21 -23
- package/src/instance/Rules/Meta.js +13 -14
- package/src/instance/Rules/Rule.js +39 -43
- package/src/instance/Rules/exception/RuleArgumentException.js +4 -4
- package/src/libraries/CheckMolecule.js +253 -232
- package/src/libraries/Decimal.js +13 -17
- package/src/libraries/Dot.js +74 -48
- package/src/libraries/Hex.js +49 -54
- package/src/libraries/array.js +50 -41
- package/src/libraries/crypto.js +20 -27
- package/src/libraries/strings.js +58 -91
- package/src/libraries/urql/UrqlClientWrapper.js +166 -0
- package/src/mutation/Mutation.js +44 -25
- package/src/mutation/MutationActiveSession.js +12 -12
- package/src/mutation/MutationClaimShadowWallet.js +15 -17
- package/src/mutation/MutationCreateIdentifier.js +11 -12
- package/src/mutation/MutationCreateMeta.js +11 -12
- package/src/mutation/MutationCreateRule.js +11 -12
- package/src/mutation/MutationCreateToken.js +18 -13
- package/src/mutation/MutationCreateWallet.js +9 -11
- package/src/mutation/MutationDepositBufferToken.js +7 -9
- package/src/mutation/MutationLinkIdentifier.js +12 -14
- package/src/mutation/MutationProposeMolecule.js +24 -25
- package/src/mutation/MutationRequestAuthorization.js +9 -10
- package/src/mutation/MutationRequestAuthorizationGuest.js +12 -15
- package/src/mutation/MutationRequestTokens.js +11 -14
- package/src/mutation/MutationTransferTokens.js +11 -14
- package/src/mutation/MutationWithdrawBufferToken.js +7 -10
- package/src/query/Query.js +62 -36
- package/src/query/QueryActiveSession.js +11 -13
- package/src/query/QueryAtom.js +75 -76
- package/src/query/QueryBalance.js +11 -12
- package/src/query/QueryBatch.js +17 -14
- package/src/query/QueryBatchHistory.js +16 -13
- package/src/query/QueryContinuId.js +13 -10
- package/src/query/QueryMetaType.js +45 -57
- package/src/query/QueryMetaTypeViaAtom.js +49 -57
- package/src/query/QueryPolicy.js +11 -12
- package/src/query/QueryToken.js +11 -13
- package/src/query/QueryUserActivity.js +11 -13
- package/src/query/QueryWalletBundle.js +15 -47
- package/src/query/QueryWalletList.js +15 -16
- package/src/response/Response.js +29 -34
- package/src/response/ResponseActiveSession.js +6 -6
- package/src/response/ResponseAtom.js +29 -30
- package/src/response/ResponseAuthorizationGuest.js +17 -18
- package/src/response/ResponseBalance.js +12 -13
- package/src/response/ResponseClaimShadowWallet.js +1 -1
- package/src/response/ResponseContinuId.js +21 -22
- package/src/response/ResponseCreateIdentifier.js +1 -1
- package/src/response/ResponseCreateMeta.js +1 -1
- package/src/response/ResponseCreateRule.js +1 -1
- package/src/response/ResponseCreateToken.js +1 -1
- package/src/response/ResponseCreateWallet.js +1 -1
- package/src/response/ResponseLinkIdentifier.js +9 -10
- package/src/response/ResponseMetaBatch.js +6 -8
- package/src/response/ResponseMetaType.js +19 -20
- package/src/response/ResponseMetaTypeViaAtom.js +19 -19
- package/src/response/ResponsePolicy.js +14 -15
- package/src/response/ResponseProposeMolecule.js +27 -30
- package/src/response/ResponseQueryActiveSession.js +20 -23
- package/src/response/ResponseQueryUserActivity.js +11 -12
- package/src/response/ResponseRequestAuthorization.js +11 -16
- package/src/response/ResponseRequestAuthorizationGuest.js +18 -21
- package/src/response/ResponseRequestTokens.js +1 -1
- package/src/response/ResponseTransferTokens.js +8 -9
- package/src/response/ResponseWalletBundle.js +16 -17
- package/src/response/ResponseWalletList.js +44 -47
- package/src/subscribe/ActiveSessionSubscribe.js +5 -6
- package/src/subscribe/ActiveWalletSubscribe.js +5 -6
- package/src/subscribe/CreateMoleculeSubscribe.js +5 -5
- package/src/subscribe/Subscribe.js +26 -26
- package/src/subscribe/WalletStatusSubscribe.js +5 -6
- package/src/versions/HashAtom.js +78 -0
- package/src/versions/Version4.js +34 -0
- package/src/versions/index.js +5 -0
- package/dist/client.umd.js +0 -453
- package/src/httpClient/ApolloClient.js +0 -245
- package/src/libraries/ApolloLink/CipherLink.js +0 -117
- package/src/libraries/ApolloLink/Client.js +0 -231
- package/src/libraries/ApolloLink/PusherLink.js +0 -234
- package/src/libraries/ApolloLink/handler.js +0 -106
- package/src/libraries/Base58.js +0 -71
- package/src/libraries/Base64.js +0 -40
- package/src/libraries/BaseX.js +0 -91
- package/src/libraries/Soda.js +0 -93
- package/src/query/QueryMetaInstance.js +0 -99
- package/src/test/Test.js +0 -670
- package/src/test/TestTokenUnit.js +0 -340
package/src/Wallet.js
CHANGED
|
@@ -45,119 +45,125 @@ Please visit https://github.com/WishKnish/KnishIO-Client-JS for information.
|
|
|
45
45
|
|
|
46
46
|
License: https://github.com/WishKnish/KnishIO-Client-JS/blob/master/LICENSE
|
|
47
47
|
*/
|
|
48
|
-
import
|
|
49
|
-
import bigInt from 'big-integer/BigInteger';
|
|
48
|
+
import JsSHA from 'jssha'
|
|
50
49
|
import {
|
|
51
50
|
chunkSubstr,
|
|
52
51
|
isHex,
|
|
53
52
|
randomString
|
|
54
|
-
} from './libraries/strings'
|
|
53
|
+
} from './libraries/strings'
|
|
55
54
|
import {
|
|
56
55
|
generateBatchId,
|
|
57
|
-
generateBundleHash
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
import TokenUnit from './TokenUnit'
|
|
61
|
-
import
|
|
56
|
+
generateBundleHash,
|
|
57
|
+
generateSecret
|
|
58
|
+
} from './libraries/crypto'
|
|
59
|
+
import TokenUnit from './TokenUnit'
|
|
60
|
+
import WalletCredentialException from './exception/WalletCredentialException'
|
|
61
|
+
import { ml_kem768 as MlKEM768 } from '@noble/post-quantum/ml-kem'
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Wallet class represents the set of public and private
|
|
65
65
|
* keys to sign Molecules
|
|
66
66
|
*/
|
|
67
67
|
export default class Wallet {
|
|
68
|
-
|
|
69
68
|
/**
|
|
70
69
|
* Class constructor
|
|
71
70
|
*
|
|
72
71
|
* @param {string|null} secret - typically a 2048-character biometric hash
|
|
72
|
+
* @param {string|null} bundle - 64-character hexadecimal user identifier
|
|
73
73
|
* @param {string} token - slug for the token this wallet is intended for
|
|
74
|
+
* @param {string|null} address - hexadecimal public key for the signature of this wallet
|
|
74
75
|
* @param {string|null} position - hexadecimal string used to salt the secret and produce one-time signatures
|
|
75
76
|
* @param {string|null} batchId
|
|
76
77
|
* @param {string|null} characters
|
|
77
78
|
*/
|
|
78
|
-
constructor (
|
|
79
|
+
constructor ({
|
|
79
80
|
secret = null,
|
|
81
|
+
bundle = null,
|
|
80
82
|
token = 'USER',
|
|
83
|
+
address = null,
|
|
81
84
|
position = null,
|
|
82
85
|
batchId = null,
|
|
83
86
|
characters = null
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
88
|
-
this.molecules = {};
|
|
87
|
+
}) {
|
|
88
|
+
this.token = token
|
|
89
|
+
this.balance = 0
|
|
90
|
+
this.molecules = {}
|
|
89
91
|
|
|
90
92
|
// Empty values
|
|
91
|
-
this.key = null
|
|
92
|
-
this.
|
|
93
|
-
this.
|
|
94
|
-
this.
|
|
95
|
-
this.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.
|
|
99
|
-
this.
|
|
100
|
-
this.
|
|
101
|
-
this.characters = characters
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
93
|
+
this.key = null
|
|
94
|
+
this.privkey = null
|
|
95
|
+
this.pubkey = null
|
|
96
|
+
this.tokenUnits = []
|
|
97
|
+
this.tradeRates = {}
|
|
98
|
+
|
|
99
|
+
this.address = address
|
|
100
|
+
this.position = position
|
|
101
|
+
this.bundle = bundle
|
|
102
|
+
this.batchId = batchId
|
|
103
|
+
this.characters = characters
|
|
104
|
+
|
|
105
|
+
if (secret) {
|
|
105
106
|
// Set bundle from the secret
|
|
106
|
-
this.bundle = generateBundleHash(
|
|
107
|
+
this.bundle = this.bundle || generateBundleHash(secret, 'Wallet::constructor')
|
|
107
108
|
|
|
108
109
|
// Generate a position for non-shadow wallet if not initialized
|
|
109
|
-
this.position = this.position || Wallet.generatePosition()
|
|
110
|
+
this.position = this.position || Wallet.generatePosition()
|
|
110
111
|
|
|
111
112
|
// Key & address initialization
|
|
112
|
-
this.key = Wallet.generateKey(
|
|
113
|
+
this.key = Wallet.generateKey({
|
|
113
114
|
secret,
|
|
114
115
|
token: this.token,
|
|
115
116
|
position: this.position
|
|
116
|
-
}
|
|
117
|
-
this.address = Wallet.generateAddress(
|
|
118
|
-
|
|
119
|
-
// Soda object initialization
|
|
120
|
-
this.soda = new Soda( characters );
|
|
121
|
-
|
|
122
|
-
// Private & pubkey initialization
|
|
123
|
-
this.privkey = this.soda.generatePrivateKey( this.key );
|
|
124
|
-
this.pubkey = this.soda.generatePublicKey( this.privkey );
|
|
117
|
+
})
|
|
118
|
+
this.address = this.address || Wallet.generateAddress(this.key)
|
|
125
119
|
|
|
126
120
|
// Set characters
|
|
127
|
-
this.characters = this.characters || 'BASE64'
|
|
121
|
+
this.characters = this.characters || 'BASE64'
|
|
122
|
+
|
|
123
|
+
// Initialize ML-KEM keys
|
|
124
|
+
this.initializeMLKEM()
|
|
128
125
|
}
|
|
129
126
|
}
|
|
130
127
|
|
|
131
128
|
/**
|
|
132
129
|
* Creates a new Wallet instance
|
|
133
130
|
*
|
|
134
|
-
* @param {string}
|
|
131
|
+
* @param {string|null} secret
|
|
132
|
+
* @param {string|null} bundle
|
|
135
133
|
* @param {string} token
|
|
136
134
|
* @param {string|null} batchId
|
|
137
135
|
* @param {string|null} characters
|
|
138
136
|
* @return {Wallet}
|
|
139
137
|
*/
|
|
140
|
-
static create (
|
|
141
|
-
|
|
138
|
+
static create ({
|
|
139
|
+
secret = null,
|
|
140
|
+
bundle = null,
|
|
142
141
|
token,
|
|
143
142
|
batchId = null,
|
|
144
143
|
characters = null
|
|
145
|
-
}
|
|
144
|
+
}) {
|
|
145
|
+
let position = null
|
|
146
|
+
|
|
147
|
+
// No credentials parameters provided?
|
|
148
|
+
if (!secret && !bundle) {
|
|
149
|
+
throw new WalletCredentialException()
|
|
150
|
+
}
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
// Secret, but no bundle?
|
|
153
|
+
if (secret && !bundle) {
|
|
154
|
+
position = Wallet.generatePosition()
|
|
155
|
+
bundle = generateBundleHash(secret, 'Wallet::create')
|
|
156
|
+
}
|
|
150
157
|
|
|
151
158
|
// Wallet initialization
|
|
152
|
-
|
|
159
|
+
return new Wallet({
|
|
153
160
|
secret,
|
|
161
|
+
bundle,
|
|
154
162
|
token,
|
|
155
163
|
position,
|
|
156
164
|
batchId,
|
|
157
165
|
characters
|
|
158
|
-
}
|
|
159
|
-
wallet.bundle = bundle;
|
|
160
|
-
return wallet;
|
|
166
|
+
})
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
/**
|
|
@@ -166,13 +172,12 @@ export default class Wallet {
|
|
|
166
172
|
* @param {string} maybeBundleHash
|
|
167
173
|
* @return {boolean}
|
|
168
174
|
*/
|
|
169
|
-
static isBundleHash (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return false;
|
|
175
|
+
static isBundleHash (maybeBundleHash) {
|
|
176
|
+
if (typeof maybeBundleHash !== 'string') {
|
|
177
|
+
return false
|
|
173
178
|
}
|
|
174
179
|
|
|
175
|
-
return maybeBundleHash.length === 64 && isHex(
|
|
180
|
+
return maybeBundleHash.length === 64 && isHex(maybeBundleHash)
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
/**
|
|
@@ -181,278 +186,283 @@ export default class Wallet {
|
|
|
181
186
|
* @param unitsData
|
|
182
187
|
* @return {[]}
|
|
183
188
|
*/
|
|
184
|
-
static getTokenUnits (
|
|
185
|
-
|
|
186
|
-
unitsData.forEach(
|
|
187
|
-
result.push(
|
|
188
|
-
}
|
|
189
|
-
return result
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
*
|
|
194
|
-
* @returns {*[]}
|
|
195
|
-
*/
|
|
196
|
-
getTokenUnitsData () {
|
|
197
|
-
const result = [];
|
|
198
|
-
this.tokenUnits.forEach( tokenUnit => {
|
|
199
|
-
result.push( tokenUnit.toData() );
|
|
200
|
-
} );
|
|
201
|
-
return result;
|
|
189
|
+
static getTokenUnits (unitsData) {
|
|
190
|
+
const result = []
|
|
191
|
+
unitsData.forEach(unitData => {
|
|
192
|
+
result.push(TokenUnit.createFromDB(unitData))
|
|
193
|
+
})
|
|
194
|
+
return result
|
|
202
195
|
}
|
|
203
196
|
|
|
204
|
-
|
|
205
197
|
/**
|
|
206
|
-
*
|
|
198
|
+
* Generates a private key for the given parameters
|
|
207
199
|
*
|
|
208
|
-
* @param {
|
|
209
|
-
* @param
|
|
210
|
-
* @param
|
|
200
|
+
* @param {string} secret
|
|
201
|
+
* @param {string} token
|
|
202
|
+
* @param {string} position
|
|
203
|
+
* @return {string}
|
|
211
204
|
*/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if ( units.length === 0 ) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
205
|
+
static generateKey ({
|
|
206
|
+
secret,
|
|
207
|
+
token,
|
|
208
|
+
position
|
|
209
|
+
}) {
|
|
210
|
+
// Converting secret to bigInt
|
|
211
|
+
const bigIntSecret = BigInt(`0x${ secret }`)
|
|
222
212
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
let remainderTokenUnits = [];
|
|
226
|
-
this.tokenUnits.forEach( tokenUnit => {
|
|
227
|
-
if ( units.includes( tokenUnit.id ) ) {
|
|
228
|
-
recipientTokenUnits.push( tokenUnit );
|
|
229
|
-
} else {
|
|
230
|
-
remainderTokenUnits.push( tokenUnit );
|
|
231
|
-
}
|
|
232
|
-
} );
|
|
213
|
+
// Adding new position to the user secret to produce the indexed key
|
|
214
|
+
const indexedKey = bigIntSecret + BigInt(`0x${ position }`)
|
|
233
215
|
|
|
216
|
+
// Hashing the indexed key to produce the intermediate key
|
|
217
|
+
const intermediateKeySponge = new JsSHA('SHAKE256', 'TEXT')
|
|
234
218
|
|
|
235
|
-
|
|
236
|
-
this.tokenUnits = recipientTokenUnits;
|
|
219
|
+
intermediateKeySponge.update(indexedKey.toString(16))
|
|
237
220
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
recipientWallet.tokenUnits = recipientTokenUnits;
|
|
221
|
+
if (token) {
|
|
222
|
+
intermediateKeySponge.update(token)
|
|
241
223
|
}
|
|
242
|
-
remainderWallet.tokenUnits = remainderTokenUnits;
|
|
243
|
-
}
|
|
244
224
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
( typeof this.position === 'undefined' || null === this.position ) &&
|
|
251
|
-
( typeof this.address === 'undefined' || null === this.address )
|
|
252
|
-
);
|
|
225
|
+
// Hashing the intermediate key to produce the private key
|
|
226
|
+
const privateKeySponge = new JsSHA('SHAKE256', 'TEXT')
|
|
227
|
+
privateKeySponge.update(intermediateKeySponge.getHash('HEX', { outputLen: 8192 }))
|
|
228
|
+
return privateKeySponge.getHash('HEX', { outputLen: 8192 })
|
|
253
229
|
}
|
|
254
230
|
|
|
255
231
|
/**
|
|
256
|
-
*
|
|
232
|
+
* Generates a wallet address
|
|
257
233
|
*
|
|
258
|
-
* @param {
|
|
259
|
-
* @
|
|
234
|
+
* @param {string} key
|
|
235
|
+
* @return {string}
|
|
260
236
|
*/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
237
|
+
static generateAddress (key) {
|
|
238
|
+
// Subdivide private key into 16 fragments of 128 characters each
|
|
239
|
+
const keyFragments = chunkSubstr(key, 128)
|
|
240
|
+
// Generating wallet digest
|
|
241
|
+
const digestSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
242
|
+
|
|
243
|
+
for (const index in keyFragments) {
|
|
244
|
+
let workingFragment = keyFragments[index]
|
|
245
|
+
for (let fragmentCount = 1; fragmentCount <= 16; fragmentCount++) {
|
|
246
|
+
const workingSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
247
|
+
workingSponge.update(workingFragment)
|
|
248
|
+
workingFragment = workingSponge.getHash('HEX', { outputLen: 512 })
|
|
249
|
+
}
|
|
265
250
|
|
|
266
|
-
|
|
267
|
-
this.batchId = isRemainder ? sourceWallet.batchId : generateBatchId( {} );
|
|
251
|
+
digestSponge.update(workingFragment)
|
|
268
252
|
}
|
|
253
|
+
|
|
254
|
+
// Producing wallet address
|
|
255
|
+
const outputSponge = new JsSHA('SHAKE256', 'TEXT')
|
|
256
|
+
outputSponge.update(digestSponge.getHash('HEX', { outputLen: 8192 }))
|
|
257
|
+
return outputSponge.getHash('HEX', { outputLen: 256 })
|
|
269
258
|
}
|
|
270
259
|
|
|
271
260
|
/**
|
|
272
|
-
* Encrypts a message for this wallet instance
|
|
273
261
|
*
|
|
274
|
-
* @param
|
|
275
|
-
* @
|
|
262
|
+
* @param saltLength
|
|
263
|
+
* @returns {string}
|
|
276
264
|
*/
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const encrypt = {};
|
|
280
|
-
|
|
281
|
-
for ( let index = 1, length = arguments.length; index < length; index++ ) {
|
|
282
|
-
encrypt[ this.soda.shortHash( arguments[ index ] ) ] = this.soda.encrypt( message, arguments[ index ] );
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return encrypt;
|
|
265
|
+
static generatePosition (saltLength = 64) {
|
|
266
|
+
return randomString(saltLength, 'abcdef0123456789')
|
|
286
267
|
}
|
|
287
268
|
|
|
288
269
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* @param {string|object} message
|
|
292
|
-
* @return {null|any}
|
|
270
|
+
* Initializes the ML-KEM key pair
|
|
293
271
|
*/
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
encrypt = message[ this.soda.shortHash( pubKey ) ] || '';
|
|
272
|
+
initializeMLKEM () {
|
|
273
|
+
// Generate a 64-byte (512-bit) seed from the Knish.IO private key
|
|
274
|
+
const seedHex = generateSecret(this.key, 64)
|
|
275
|
+
|
|
276
|
+
// Convert the hex string to a Uint8Array
|
|
277
|
+
const seed = new Uint8Array(64)
|
|
278
|
+
for (let i = 0; i < 64; i++) {
|
|
279
|
+
seed[i] = parseInt(seedHex.substr(i * 2, 2), 16)
|
|
304
280
|
}
|
|
305
281
|
|
|
306
|
-
|
|
282
|
+
const { publicKey, secretKey } = MlKEM768.keygen(seed)
|
|
283
|
+
this.pubkey = this.serializeKey(publicKey)
|
|
284
|
+
this.privkey = secretKey // Note: We're keeping privkey as UInt8Array for security
|
|
307
285
|
}
|
|
308
286
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return ( new BaseX( { characters: 'BASE64' } ) ).decode( decrypt );
|
|
287
|
+
serializeKey (key) {
|
|
288
|
+
return btoa(String.fromCharCode.apply(null, key))
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
deserializeKey (serializedKey) {
|
|
292
|
+
const binaryString = atob(serializedKey)
|
|
293
|
+
return new Uint8Array(binaryString.length).map((_, i) => binaryString.charCodeAt(i))
|
|
317
294
|
}
|
|
318
295
|
|
|
319
296
|
/**
|
|
320
|
-
*
|
|
321
|
-
* @returns {
|
|
297
|
+
*
|
|
298
|
+
* @returns {*[]}
|
|
322
299
|
*/
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return
|
|
300
|
+
getTokenUnitsData () {
|
|
301
|
+
const result = []
|
|
302
|
+
this.tokenUnits.forEach(tokenUnit => {
|
|
303
|
+
result.push(tokenUnit.toData())
|
|
304
|
+
})
|
|
305
|
+
return result
|
|
329
306
|
}
|
|
330
307
|
|
|
331
308
|
/**
|
|
332
|
-
*
|
|
309
|
+
* Split token units
|
|
333
310
|
*
|
|
334
|
-
* @param {
|
|
335
|
-
* @param
|
|
336
|
-
* @
|
|
311
|
+
* @param {array} units
|
|
312
|
+
* @param remainderWallet
|
|
313
|
+
* @param recipientWallet
|
|
337
314
|
*/
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
315
|
+
splitUnits (
|
|
316
|
+
units, // Array: token unit IDs
|
|
317
|
+
remainderWallet,
|
|
318
|
+
recipientWallet = null
|
|
319
|
+
) {
|
|
320
|
+
// No units supplied, nothing to split
|
|
321
|
+
if (units.length === 0) {
|
|
322
|
+
return
|
|
323
|
+
}
|
|
347
324
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
325
|
+
// Init recipient & remainder token units
|
|
326
|
+
const recipientTokenUnits = []
|
|
327
|
+
const remainderTokenUnits = []
|
|
328
|
+
this.tokenUnits.forEach(tokenUnit => {
|
|
329
|
+
if (units.includes(tokenUnit.id)) {
|
|
330
|
+
recipientTokenUnits.push(tokenUnit)
|
|
331
|
+
} else {
|
|
332
|
+
remainderTokenUnits.push(tokenUnit)
|
|
351
333
|
}
|
|
334
|
+
})
|
|
352
335
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return btoa( JSON.stringify( encryptedData ) );
|
|
336
|
+
// Reset token units to the sending value
|
|
337
|
+
this.tokenUnits = recipientTokenUnits
|
|
356
338
|
|
|
339
|
+
// Set token units to recipient & remainder
|
|
340
|
+
if (recipientWallet !== null) {
|
|
341
|
+
recipientWallet.tokenUnits = recipientTokenUnits
|
|
357
342
|
}
|
|
358
|
-
|
|
343
|
+
remainderWallet.tokenUnits = remainderTokenUnits
|
|
344
|
+
}
|
|
359
345
|
|
|
360
346
|
/**
|
|
361
|
-
*
|
|
347
|
+
* Create a remainder wallet from the source one
|
|
362
348
|
*
|
|
363
|
-
* @param
|
|
364
|
-
* @param {string|null} fallbackValue
|
|
365
|
-
* @return {array|object}
|
|
349
|
+
* @param secret
|
|
366
350
|
*/
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
// Probably not actually encrypted
|
|
381
|
-
console.error( e );
|
|
382
|
-
return fallbackValue || data;
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
};
|
|
351
|
+
createRemainder (secret) {
|
|
352
|
+
const remainderWallet = Wallet.create({
|
|
353
|
+
secret,
|
|
354
|
+
token: this.token,
|
|
355
|
+
characters: this.characters
|
|
356
|
+
})
|
|
357
|
+
remainderWallet.initBatchId({
|
|
358
|
+
sourceWallet: this,
|
|
359
|
+
isRemainder: true
|
|
360
|
+
})
|
|
361
|
+
return remainderWallet
|
|
362
|
+
}
|
|
388
363
|
|
|
389
364
|
/**
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* @param {string} secret
|
|
393
|
-
* @param {string} token
|
|
394
|
-
* @param {string} position
|
|
395
|
-
* @return {string}
|
|
365
|
+
* @return boolean
|
|
396
366
|
*/
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
// Converting secret to bigInt
|
|
404
|
-
const bigIntSecret = bigInt( secret, 16 ),
|
|
405
|
-
// Adding new position to the user secret to produce the indexed key
|
|
406
|
-
indexedKey = bigIntSecret.add( bigInt( position, 16 ) ),
|
|
407
|
-
// Hashing the indexed key to produce the intermediate key
|
|
408
|
-
intermediateKeySponge = shake256.create( 8192 );
|
|
409
|
-
|
|
410
|
-
intermediateKeySponge.update( indexedKey.toString( 16 ) );
|
|
411
|
-
|
|
412
|
-
if ( token ) {
|
|
413
|
-
intermediateKeySponge.update( token );
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Hashing the intermediate key to produce the private key
|
|
417
|
-
return shake256.create( 8192 ).update( intermediateKeySponge.hex() ).hex();
|
|
367
|
+
isShadow () {
|
|
368
|
+
return (
|
|
369
|
+
(typeof this.position === 'undefined' || this.position === null) &&
|
|
370
|
+
(typeof this.address === 'undefined' || this.address === null)
|
|
371
|
+
)
|
|
418
372
|
}
|
|
419
373
|
|
|
420
374
|
/**
|
|
421
|
-
*
|
|
375
|
+
* Sets up a batch ID - either using the sender's, or a new one
|
|
422
376
|
*
|
|
423
|
-
* @param {
|
|
424
|
-
* @
|
|
377
|
+
* @param {Wallet} sourceWallet
|
|
378
|
+
* @param {boolean} isRemainder
|
|
425
379
|
*/
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
let workingFragment = keyFragments[ index ];
|
|
380
|
+
initBatchId ({
|
|
381
|
+
sourceWallet,
|
|
382
|
+
isRemainder = false
|
|
383
|
+
}) {
|
|
384
|
+
if (sourceWallet.batchId) {
|
|
385
|
+
this.batchId = isRemainder ? sourceWallet.batchId : generateBatchId({})
|
|
386
|
+
}
|
|
387
|
+
}
|
|
436
388
|
|
|
437
|
-
|
|
389
|
+
async encryptMessage (message, recipientPubkey) {
|
|
390
|
+
const messageString = JSON.stringify(message)
|
|
391
|
+
const messageUint8 = new TextEncoder().encode(messageString)
|
|
392
|
+
const deserializedPubkey = this.deserializeKey(recipientPubkey)
|
|
393
|
+
const { cipherText, sharedSecret } = MlKEM768.encapsulate(deserializedPubkey)
|
|
394
|
+
const encryptedMessage = await this.encryptWithSharedSecret(messageUint8, sharedSecret)
|
|
395
|
+
return {
|
|
396
|
+
cipherText: this.serializeKey(cipherText),
|
|
397
|
+
encryptedMessage: this.serializeKey(encryptedMessage)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
438
400
|
|
|
439
|
-
|
|
401
|
+
async decryptMessage (encryptedData) {
|
|
402
|
+
const { cipherText, encryptedMessage } = encryptedData
|
|
440
403
|
|
|
441
|
-
|
|
404
|
+
const sharedSecret = MlKEM768.decapsulate(this.deserializeKey(cipherText), this.privkey)
|
|
405
|
+
const decryptedUint8 = await this.decryptWithSharedSecret(this.deserializeKey(encryptedMessage), sharedSecret)
|
|
442
406
|
|
|
443
|
-
|
|
444
|
-
|
|
407
|
+
const decryptedString = new TextDecoder().decode(decryptedUint8)
|
|
408
|
+
return JSON.parse(decryptedString)
|
|
409
|
+
}
|
|
445
410
|
|
|
446
|
-
|
|
447
|
-
|
|
411
|
+
async encryptWithSharedSecret (message, sharedSecret) {
|
|
412
|
+
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
413
|
+
const algorithm = { name: 'AES-GCM', iv }
|
|
414
|
+
|
|
415
|
+
// Convert the shared secret to a CryptoKey
|
|
416
|
+
const key = await crypto.subtle.importKey(
|
|
417
|
+
'raw',
|
|
418
|
+
sharedSecret,
|
|
419
|
+
{ name: 'AES-GCM' },
|
|
420
|
+
false,
|
|
421
|
+
['encrypt']
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
// Encrypt the message
|
|
425
|
+
const encryptedContent = await crypto.subtle.encrypt(
|
|
426
|
+
algorithm,
|
|
427
|
+
key,
|
|
428
|
+
message
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
// Combine IV and encrypted content
|
|
432
|
+
const result = new Uint8Array(iv.length + encryptedContent.byteLength)
|
|
433
|
+
result.set(iv)
|
|
434
|
+
result.set(new Uint8Array(encryptedContent), iv.length)
|
|
435
|
+
|
|
436
|
+
return result
|
|
448
437
|
}
|
|
449
438
|
|
|
450
439
|
/**
|
|
451
|
-
*
|
|
452
|
-
* @param
|
|
453
|
-
* @
|
|
440
|
+
* Decrypts the given message using the shared secret
|
|
441
|
+
* @param encryptedMessage
|
|
442
|
+
* @param sharedSecret
|
|
443
|
+
* @returns {Promise<Uint8Array>}
|
|
454
444
|
*/
|
|
455
|
-
|
|
456
|
-
|
|
445
|
+
async decryptWithSharedSecret (encryptedMessage, sharedSecret) {
|
|
446
|
+
// Extract IV from the encrypted message
|
|
447
|
+
const iv = encryptedMessage.slice(0, 12)
|
|
448
|
+
const algorithm = { name: 'AES-GCM', iv }
|
|
449
|
+
|
|
450
|
+
// Convert the shared secret to a CryptoKey
|
|
451
|
+
const key = await crypto.subtle.importKey(
|
|
452
|
+
'raw',
|
|
453
|
+
sharedSecret,
|
|
454
|
+
{ name: 'AES-GCM' },
|
|
455
|
+
false,
|
|
456
|
+
['decrypt']
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
// Decrypt the message
|
|
460
|
+
const decryptedContent = await crypto.subtle.decrypt(
|
|
461
|
+
algorithm,
|
|
462
|
+
key,
|
|
463
|
+
encryptedMessage.slice(12)
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
return new Uint8Array(decryptedContent)
|
|
457
467
|
}
|
|
458
468
|
}
|