@bsv/sdk 1.1.28 → 1.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/compat/ECIES.js +24 -5
- package/dist/cjs/src/compat/ECIES.js.map +1 -1
- package/dist/cjs/src/primitives/Schnorr.js +92 -0
- package/dist/cjs/src/primitives/Schnorr.js.map +1 -0
- package/dist/cjs/src/primitives/index.js +3 -1
- package/dist/cjs/src/primitives/index.js.map +1 -1
- package/dist/cjs/src/totp/totp.js +1 -1
- package/dist/cjs/src/totp/totp.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +139 -118
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/src/transaction/BeefParty.js +30 -26
- package/dist/cjs/src/transaction/BeefParty.js.map +1 -1
- package/dist/cjs/src/transaction/BeefTx.js +25 -15
- package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/compat/ECIES.js +24 -5
- package/dist/esm/src/compat/ECIES.js.map +1 -1
- package/dist/esm/src/primitives/Schnorr.js +87 -0
- package/dist/esm/src/primitives/Schnorr.js.map +1 -0
- package/dist/esm/src/primitives/index.js +1 -0
- package/dist/esm/src/primitives/index.js.map +1 -1
- package/dist/esm/src/totp/totp.js +1 -1
- package/dist/esm/src/totp/totp.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +142 -121
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/src/transaction/BeefParty.js +31 -27
- package/dist/esm/src/transaction/BeefParty.js.map +1 -1
- package/dist/esm/src/transaction/BeefTx.js +29 -19
- package/dist/esm/src/transaction/BeefTx.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/compat/ECIES.d.ts.map +1 -1
- package/dist/types/src/primitives/Schnorr.d.ts +65 -0
- package/dist/types/src/primitives/Schnorr.d.ts.map +1 -0
- package/dist/types/src/primitives/index.d.ts +1 -0
- package/dist/types/src/primitives/index.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +94 -94
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefParty.d.ts +23 -23
- package/dist/types/src/transaction/BeefParty.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefTx.d.ts +5 -5
- package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/primitives.md +120 -9
- package/package.json +1 -1
- package/src/compat/ECIES.ts +38 -17
- package/src/compat/__tests/ECIES.test.ts +23 -1
- package/src/primitives/Schnorr.ts +95 -0
- package/src/primitives/__tests/Schnorr.test.ts +272 -0
- package/src/primitives/index.ts +1 -0
- package/src/totp/totp.ts +1 -1
- package/src/transaction/Beef.ts +351 -375
- package/src/transaction/BeefParty.ts +54 -58
- package/src/transaction/BeefTx.ts +128 -143
- package/src/transaction/MerklePath.ts +11 -11
- package/src/transaction/Transaction.ts +32 -32
package/docs/primitives.md
CHANGED
|
@@ -8,15 +8,16 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
|
|
|
8
8
|
|
|
9
9
|
| | | |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| [BasePoint](#class-basepoint) | [
|
|
12
|
-
| [BigNumber](#class-bignumber) | [
|
|
13
|
-
| [Curve](#class-curve) | [
|
|
14
|
-
| [DRBG](#class-drbg) | [
|
|
15
|
-
| [JacobianPoint](#class-jacobianpoint) | [
|
|
16
|
-
| [K256](#class-k256) | [
|
|
17
|
-
| [KeyShares](#class-keyshares) | [
|
|
18
|
-
| [Mersenne](#class-mersenne) | [
|
|
19
|
-
| [MontgomoryMethod](#class-montgomorymethod) | [
|
|
11
|
+
| [BasePoint](#class-basepoint) | [PointInFiniteField](#class-pointinfinitefield) | [SHA256HMAC](#class-sha256hmac) |
|
|
12
|
+
| [BigNumber](#class-bignumber) | [Polynomial](#class-polynomial) | [SHA512](#class-sha512) |
|
|
13
|
+
| [Curve](#class-curve) | [PrivateKey](#class-privatekey) | [SHA512HMAC](#class-sha512hmac) |
|
|
14
|
+
| [DRBG](#class-drbg) | [PublicKey](#class-publickey) | [Schnorr](#class-schnorr) |
|
|
15
|
+
| [JacobianPoint](#class-jacobianpoint) | [RIPEMD160](#class-ripemd160) | [Signature](#class-signature) |
|
|
16
|
+
| [K256](#class-k256) | [Reader](#class-reader) | [SymmetricKey](#class-symmetrickey) |
|
|
17
|
+
| [KeyShares](#class-keyshares) | [ReductionContext](#class-reductioncontext) | [TransactionSignature](#class-transactionsignature) |
|
|
18
|
+
| [Mersenne](#class-mersenne) | [SHA1](#class-sha1) | [Writer](#class-writer) |
|
|
19
|
+
| [MontgomoryMethod](#class-montgomorymethod) | [SHA1HMAC](#class-sha1hmac) | |
|
|
20
|
+
| [Point](#class-point) | [SHA256](#class-sha256) | |
|
|
20
21
|
|
|
21
22
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
22
23
|
|
|
@@ -7349,6 +7350,116 @@ public hasLowS(): boolean
|
|
|
7349
7350
|
|
|
7350
7351
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
7351
7352
|
|
|
7353
|
+
---
|
|
7354
|
+
### Class: Schnorr
|
|
7355
|
+
|
|
7356
|
+
Class representing the Schnorr Zero-Knowledge Proof (ZKP) protocol.
|
|
7357
|
+
|
|
7358
|
+
This class provides methods to generate and verify proofs that demonstrate knowledge of a secret without revealing it.
|
|
7359
|
+
Specifically, it allows one party to prove to another that they know the private key corresponding to a public key
|
|
7360
|
+
and have correctly computed a shared secret, without disclosing the private key itself.
|
|
7361
|
+
|
|
7362
|
+
The protocol involves two main methods:
|
|
7363
|
+
- `generateProof`: Generates a proof linking a public key `A` and a shared secret `S`, proving knowledge of the corresponding private key `a`.
|
|
7364
|
+
- `verifyProof`: Verifies the provided proof, ensuring its validity without revealing any secret information.
|
|
7365
|
+
|
|
7366
|
+
The class utilizes elliptic curve cryptography (ECC) and the SHA-256 hash function to compute challenges within the proof.
|
|
7367
|
+
|
|
7368
|
+
Example
|
|
7369
|
+
|
|
7370
|
+
```typescript
|
|
7371
|
+
const schnorr = new Schnorr();
|
|
7372
|
+
const a = PrivateKey.fromRandom(); // Prover's private key
|
|
7373
|
+
const A = a.toPublicKey(); // Prover's public key
|
|
7374
|
+
const b = PrivateKey.fromRandom(); // Other party's private key
|
|
7375
|
+
const B = b.toPublicKey(); // Other party's public key
|
|
7376
|
+
const S = B.mul(a); // Shared secret
|
|
7377
|
+
|
|
7378
|
+
// Prover generates the proof
|
|
7379
|
+
const proof = schnorr.generateProof(a, A, B, S);
|
|
7380
|
+
|
|
7381
|
+
// Verifier verifies the proof
|
|
7382
|
+
const isValid = schnorr.verifyProof(A.point, B.point, S.point, proof);
|
|
7383
|
+
console.log(`Proof is valid: ${isValid}`);
|
|
7384
|
+
```
|
|
7385
|
+
```ts
|
|
7386
|
+
export default class Schnorr {
|
|
7387
|
+
constructor()
|
|
7388
|
+
generateProof(aArg: PrivateKey, AArg: PublicKey, BArg: PublicKey, S: Point): {
|
|
7389
|
+
R: Point;
|
|
7390
|
+
SPrime: Point;
|
|
7391
|
+
z: BigNumber;
|
|
7392
|
+
}
|
|
7393
|
+
verifyProof(A: Point, B: Point, S: Point, proof: {
|
|
7394
|
+
R: Point;
|
|
7395
|
+
SPrime: Point;
|
|
7396
|
+
z: BigNumber;
|
|
7397
|
+
}): boolean
|
|
7398
|
+
}
|
|
7399
|
+
```
|
|
7400
|
+
|
|
7401
|
+
<details>
|
|
7402
|
+
|
|
7403
|
+
<summary>Class Schnorr Details</summary>
|
|
7404
|
+
|
|
7405
|
+
#### Method generateProof
|
|
7406
|
+
|
|
7407
|
+
Generates a proof that demonstrates the link between public key A and shared secret S
|
|
7408
|
+
|
|
7409
|
+
```ts
|
|
7410
|
+
generateProof(aArg: PrivateKey, AArg: PublicKey, BArg: PublicKey, S: Point): {
|
|
7411
|
+
R: Point;
|
|
7412
|
+
SPrime: Point;
|
|
7413
|
+
z: BigNumber;
|
|
7414
|
+
}
|
|
7415
|
+
```
|
|
7416
|
+
|
|
7417
|
+
Returns
|
|
7418
|
+
|
|
7419
|
+
Proof (R, S', z)
|
|
7420
|
+
|
|
7421
|
+
Argument Details
|
|
7422
|
+
|
|
7423
|
+
+ **a**
|
|
7424
|
+
+ Private key corresponding to public key A
|
|
7425
|
+
+ **A**
|
|
7426
|
+
+ Public key
|
|
7427
|
+
+ **B**
|
|
7428
|
+
+ Other party's public key
|
|
7429
|
+
+ **S**
|
|
7430
|
+
+ Shared secret
|
|
7431
|
+
|
|
7432
|
+
#### Method verifyProof
|
|
7433
|
+
|
|
7434
|
+
Verifies the proof of the link between public key A and shared secret S
|
|
7435
|
+
|
|
7436
|
+
```ts
|
|
7437
|
+
verifyProof(A: Point, B: Point, S: Point, proof: {
|
|
7438
|
+
R: Point;
|
|
7439
|
+
SPrime: Point;
|
|
7440
|
+
z: BigNumber;
|
|
7441
|
+
}): boolean
|
|
7442
|
+
```
|
|
7443
|
+
|
|
7444
|
+
Returns
|
|
7445
|
+
|
|
7446
|
+
True if the proof is valid, false otherwise
|
|
7447
|
+
|
|
7448
|
+
Argument Details
|
|
7449
|
+
|
|
7450
|
+
+ **A**
|
|
7451
|
+
+ Public key
|
|
7452
|
+
+ **B**
|
|
7453
|
+
+ Other party's public key
|
|
7454
|
+
+ **S**
|
|
7455
|
+
+ Shared secret
|
|
7456
|
+
+ **proof**
|
|
7457
|
+
+ Proof (R, S', z)
|
|
7458
|
+
|
|
7459
|
+
</details>
|
|
7460
|
+
|
|
7461
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
7462
|
+
|
|
7352
7463
|
---
|
|
7353
7464
|
## Functions
|
|
7354
7465
|
|
package/package.json
CHANGED
package/src/compat/ECIES.ts
CHANGED
|
@@ -46,9 +46,9 @@ function AES (key) {
|
|
|
46
46
|
decKey[j] = tmp
|
|
47
47
|
} else {
|
|
48
48
|
decKey[j] = decTable[0][sbox[tmp >>> 24]] ^
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
decTable[1][sbox[tmp >> 16 & 255]] ^
|
|
50
|
+
decTable[2][sbox[tmp >> 8 & 255]] ^
|
|
51
|
+
decTable[3][sbox[tmp & 255]]
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -162,11 +162,11 @@ AES.prototype = {
|
|
|
162
162
|
// Last round.
|
|
163
163
|
for (i = 0; i < 4; i++) {
|
|
164
164
|
out[dir ? 3 & -i : i] =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
sbox[a >>> 24] << 24 ^
|
|
166
|
+
sbox[b >> 16 & 255] << 16 ^
|
|
167
|
+
sbox[c >> 8 & 255] << 8 ^
|
|
168
|
+
sbox[d & 255] ^
|
|
169
|
+
key[kIndex++]
|
|
170
170
|
a2 = a; a = b; b = c; c = d; d = a2
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -200,10 +200,10 @@ class AESWrapper {
|
|
|
200
200
|
const words = []
|
|
201
201
|
for (let i = 0; i < buf.length / 4; i++) {
|
|
202
202
|
const val =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
(buf[i * 4] * 0x1000000) + // Shift the first byte by 24 bits
|
|
204
|
+
((buf[i * 4 + 1] << 16) | // Shift the second byte by 16 bits
|
|
205
|
+
(buf[i * 4 + 2] << 8) | // Shift the third byte by 8 bits
|
|
206
|
+
buf[i * 4 + 3]) // The fourth byte
|
|
207
207
|
words.push(val)
|
|
208
208
|
}
|
|
209
209
|
return words
|
|
@@ -477,12 +477,32 @@ export default class ECIES {
|
|
|
477
477
|
throw new Error('Invalid Magic')
|
|
478
478
|
}
|
|
479
479
|
let offset = 4
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
480
|
+
|
|
481
|
+
// Determine if the sender's public key is included in encBuf
|
|
482
|
+
let Rbuf: number[] | null = null
|
|
483
|
+
if (encBuf.length - offset - tagLength >= 33) {
|
|
484
|
+
const firstByte = encBuf[offset]
|
|
485
|
+
if (firstByte === 0x02 || firstByte === 0x03) {
|
|
486
|
+
// Compressed public key
|
|
487
|
+
Rbuf = encBuf.slice(offset, offset + 33)
|
|
488
|
+
offset += 33
|
|
489
|
+
} else if (firstByte === 0x04) {
|
|
490
|
+
// Uncompressed public key
|
|
491
|
+
Rbuf = encBuf.slice(offset, offset + 65)
|
|
492
|
+
offset += 65
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (Rbuf) {
|
|
497
|
+
if (!fromPublicKey) {
|
|
498
|
+
fromPublicKey = PublicKey.fromString(toHex(Rbuf))
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
if (!fromPublicKey) {
|
|
502
|
+
throw new Error('Sender public key is required')
|
|
503
|
+
}
|
|
485
504
|
}
|
|
505
|
+
|
|
486
506
|
const { iv, kE, kM } = ECIES.ivkEkM(toPrivateKey, fromPublicKey)
|
|
487
507
|
const ciphertext = encBuf.slice(offset, encBuf.length - tagLength)
|
|
488
508
|
const hmac = encBuf.slice(encBuf.length - tagLength, encBuf.length)
|
|
@@ -492,6 +512,7 @@ export default class ECIES {
|
|
|
492
512
|
if (toHex(hmac) !== toHex(hmac2)) {
|
|
493
513
|
throw new Error('Invalid checksum')
|
|
494
514
|
}
|
|
515
|
+
|
|
495
516
|
return AESCBC.decrypt(ciphertext, kE, iv)
|
|
496
517
|
}
|
|
497
518
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ECIES from '../../../dist/cjs/src/compat/ECIES'
|
|
2
2
|
import * as Hash from '../../../dist/cjs/src/primitives/Hash'
|
|
3
3
|
import PrivateKey from '../../../dist/cjs/src/primitives/PrivateKey'
|
|
4
|
-
import { toArray, toHex, encode, toBase64 } from '../../../dist/cjs/src/primitives/utils'
|
|
4
|
+
import { toArray, toHex, encode, toBase64, toUTF8 } from '../../../dist/cjs/src/primitives/utils'
|
|
5
5
|
|
|
6
6
|
describe('#ECIES', () => {
|
|
7
7
|
it('should make a new ECIES object', () => {
|
|
@@ -93,5 +93,27 @@ describe('#ECIES', () => {
|
|
|
93
93
|
expect(ECIES.electrumDecrypt(encryptedMessage, bobPrivateKey))
|
|
94
94
|
.toEqual(message)
|
|
95
95
|
})
|
|
96
|
+
|
|
97
|
+
it('should encrypt and decrypt message with counterparty public key', () => {
|
|
98
|
+
const wif = 'L211enC224G1kV8pyyq7bjVd9SxZebnRYEzzM3i7ZHCc1c5E7dQu'
|
|
99
|
+
const senderPrivateKey = PrivateKey.fromWif(wif)
|
|
100
|
+
const senderPublicKey = senderPrivateKey.toPublicKey()
|
|
101
|
+
const msgStr = 'hello world'
|
|
102
|
+
const messageBuf = toArray(msgStr, 'utf8')
|
|
103
|
+
|
|
104
|
+
// Create a random counterparty (recipient) public/private key pair
|
|
105
|
+
const recipientPrivateKey = PrivateKey.fromRandom()
|
|
106
|
+
const recipientPublicKey = recipientPrivateKey.toPublicKey()
|
|
107
|
+
|
|
108
|
+
// Encrypt the message using electrumEncrypt
|
|
109
|
+
const encryptedMessage = ECIES.electrumEncrypt(messageBuf, recipientPublicKey, senderPrivateKey)
|
|
110
|
+
|
|
111
|
+
// Decrypt the message using electrumDecrypt
|
|
112
|
+
const decryptedMessageBuf = ECIES.electrumDecrypt(encryptedMessage, recipientPrivateKey, senderPublicKey)
|
|
113
|
+
|
|
114
|
+
const decryptedMsgStr = toUTF8(decryptedMessageBuf)
|
|
115
|
+
|
|
116
|
+
expect(decryptedMsgStr).toEqual(msgStr)
|
|
117
|
+
})
|
|
96
118
|
})
|
|
97
119
|
})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import BigNumber from './BigNumber.js'
|
|
2
|
+
import Curve from './Curve.js'
|
|
3
|
+
import Point from './Point.js'
|
|
4
|
+
import { sha256 } from './Hash.js'
|
|
5
|
+
import { PrivateKey, PublicKey } from './index.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Class representing the Schnorr Zero-Knowledge Proof (ZKP) protocol.
|
|
9
|
+
*
|
|
10
|
+
* This class provides methods to generate and verify proofs that demonstrate knowledge of a secret without revealing it.
|
|
11
|
+
* Specifically, it allows one party to prove to another that they know the private key corresponding to a public key
|
|
12
|
+
* and have correctly computed a shared secret, without disclosing the private key itself.
|
|
13
|
+
*
|
|
14
|
+
* The protocol involves two main methods:
|
|
15
|
+
* - `generateProof`: Generates a proof linking a public key `A` and a shared secret `S`, proving knowledge of the corresponding private key `a`.
|
|
16
|
+
* - `verifyProof`: Verifies the provided proof, ensuring its validity without revealing any secret information.
|
|
17
|
+
*
|
|
18
|
+
* The class utilizes elliptic curve cryptography (ECC) and the SHA-256 hash function to compute challenges within the proof.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const schnorr = new Schnorr();
|
|
23
|
+
* const a = PrivateKey.fromRandom(); // Prover's private key
|
|
24
|
+
* const A = a.toPublicKey(); // Prover's public key
|
|
25
|
+
* const b = PrivateKey.fromRandom(); // Other party's private key
|
|
26
|
+
* const B = b.toPublicKey(); // Other party's public key
|
|
27
|
+
* const S = B.mul(a); // Shared secret
|
|
28
|
+
*
|
|
29
|
+
* // Prover generates the proof
|
|
30
|
+
* const proof = schnorr.generateProof(a, A, B, S);
|
|
31
|
+
*
|
|
32
|
+
* // Verifier verifies the proof
|
|
33
|
+
* const isValid = schnorr.verifyProof(A.point, B.point, S.point, proof);
|
|
34
|
+
* console.log(`Proof is valid: ${isValid}`);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export default class Schnorr {
|
|
38
|
+
private readonly curve: Curve
|
|
39
|
+
|
|
40
|
+
constructor () {
|
|
41
|
+
this.curve = new Curve()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generates a proof that demonstrates the link between public key A and shared secret S
|
|
46
|
+
* @param a Private key corresponding to public key A
|
|
47
|
+
* @param A Public key
|
|
48
|
+
* @param B Other party's public key
|
|
49
|
+
* @param S Shared secret
|
|
50
|
+
* @returns Proof (R, S', z)
|
|
51
|
+
*/
|
|
52
|
+
generateProof (aArg: PrivateKey, AArg: PublicKey, BArg: PublicKey, S: Point): { R: Point, SPrime: Point, z: BigNumber } {
|
|
53
|
+
const r = PrivateKey.fromRandom()
|
|
54
|
+
const R = r.toPublicKey()
|
|
55
|
+
const SPrime = BArg.mul(r)
|
|
56
|
+
const e = this.computeChallenge(AArg, BArg, S, SPrime, R)
|
|
57
|
+
const z = r.add(e.mul(aArg)).umod(this.curve.n)
|
|
58
|
+
return { R, SPrime, z }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Verifies the proof of the link between public key A and shared secret S
|
|
63
|
+
* @param A Public key
|
|
64
|
+
* @param B Other party's public key
|
|
65
|
+
* @param S Shared secret
|
|
66
|
+
* @param proof Proof (R, S', z)
|
|
67
|
+
* @returns True if the proof is valid, false otherwise
|
|
68
|
+
*/
|
|
69
|
+
verifyProof (A: Point, B: Point, S: Point, proof: { R: Point, SPrime: Point, z: BigNumber }): boolean {
|
|
70
|
+
const { R, SPrime, z } = proof
|
|
71
|
+
const e = this.computeChallenge(A, B, S, SPrime, R)
|
|
72
|
+
|
|
73
|
+
// Check zG = R + eA
|
|
74
|
+
const zG = this.curve.g.mul(z)
|
|
75
|
+
const RpluseA = R.add(A.mul(e))
|
|
76
|
+
if (!zG.eq(RpluseA)) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check zB = S' + eS
|
|
81
|
+
const zB = B.mul(z)
|
|
82
|
+
const SprimeeS = SPrime.add(S.mul(e))
|
|
83
|
+
if (!zB.eq(SprimeeS)) {
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private computeChallenge (A: Point, B: Point, S: Point, SPrime: Point, R: Point): BigNumber {
|
|
91
|
+
const message = [...A.encode(true), ...B.encode(true), ...S.encode(true), ...SPrime.encode(true), ...R.encode(true)] as number[]
|
|
92
|
+
const hash = sha256(message)
|
|
93
|
+
return new BigNumber(hash).umod(this.curve.n)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import Schnorr from '../../../dist/cjs/src/primitives/Schnorr.js'
|
|
2
|
+
import BigNumber from '../../../dist/cjs/src/primitives/BigNumber.js'
|
|
3
|
+
import Curve from '../../../dist/cjs/src/primitives/Curve.js'
|
|
4
|
+
import PrivateKey from '../../../dist/cjs/src/primitives/PrivateKey.js'
|
|
5
|
+
|
|
6
|
+
describe('Schnorr Zero-Knowledge Proof', () => {
|
|
7
|
+
let schnorr: Schnorr
|
|
8
|
+
let curve: Curve
|
|
9
|
+
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
schnorr = new Schnorr()
|
|
12
|
+
curve = new Curve()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should verify a valid proof', () => {
|
|
16
|
+
// Generate private keys
|
|
17
|
+
const a = PrivateKey.fromRandom()
|
|
18
|
+
const b = PrivateKey.fromRandom()
|
|
19
|
+
|
|
20
|
+
// Compute public keys
|
|
21
|
+
const A = a.toPublicKey()
|
|
22
|
+
const B = b.toPublicKey()
|
|
23
|
+
|
|
24
|
+
// Compute shared secret S = B * a
|
|
25
|
+
const S = B.mul(a)
|
|
26
|
+
|
|
27
|
+
// Generate proof
|
|
28
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
29
|
+
|
|
30
|
+
// Verify proof
|
|
31
|
+
const result = schnorr.verifyProof(A, B, S, proof)
|
|
32
|
+
expect(result).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should fail verification if proof is tampered (R modified)', () => {
|
|
36
|
+
// Generate private keys
|
|
37
|
+
const a = PrivateKey.fromRandom()
|
|
38
|
+
const b = PrivateKey.fromRandom()
|
|
39
|
+
|
|
40
|
+
// Compute public keys
|
|
41
|
+
const A = a.toPublicKey()
|
|
42
|
+
const B = b.toPublicKey()
|
|
43
|
+
|
|
44
|
+
// Compute shared secret S = B * a
|
|
45
|
+
const S = B.mul(a)
|
|
46
|
+
|
|
47
|
+
// Generate proof
|
|
48
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
49
|
+
|
|
50
|
+
// Tamper with R
|
|
51
|
+
const tamperedR = proof.R.add(curve.g)
|
|
52
|
+
const tamperedProof = { ...proof, R: tamperedR }
|
|
53
|
+
|
|
54
|
+
// Verify proof
|
|
55
|
+
const result = schnorr.verifyProof(A, B, S, tamperedProof)
|
|
56
|
+
expect(result).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should fail verification if proof is tampered (z modified)', () => {
|
|
60
|
+
// Generate private keys
|
|
61
|
+
const a = PrivateKey.fromRandom()
|
|
62
|
+
const b = PrivateKey.fromRandom()
|
|
63
|
+
|
|
64
|
+
// Compute public keys
|
|
65
|
+
const A = a.toPublicKey()
|
|
66
|
+
const B = b.toPublicKey()
|
|
67
|
+
|
|
68
|
+
// Compute shared secret S = B * a
|
|
69
|
+
const S = B.mul(a)
|
|
70
|
+
|
|
71
|
+
// Generate proof
|
|
72
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
73
|
+
|
|
74
|
+
// Tamper with z
|
|
75
|
+
const tamperedZ = proof.z.add(new BigNumber(1)).umod(curve.n)
|
|
76
|
+
const tamperedProof = { ...proof, z: tamperedZ }
|
|
77
|
+
|
|
78
|
+
// Verify proof
|
|
79
|
+
const result = schnorr.verifyProof(A, B, S, tamperedProof)
|
|
80
|
+
expect(result).toBe(false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should fail verification if proof is tampered (S\' modified)', () => {
|
|
84
|
+
// Generate private keys
|
|
85
|
+
const a = PrivateKey.fromRandom()
|
|
86
|
+
const b = PrivateKey.fromRandom()
|
|
87
|
+
|
|
88
|
+
// Compute public keys
|
|
89
|
+
const A = a.toPublicKey()
|
|
90
|
+
const B = b.toPublicKey()
|
|
91
|
+
|
|
92
|
+
// Compute shared secret S = B * a
|
|
93
|
+
const S = B.mul(a)
|
|
94
|
+
|
|
95
|
+
// Generate proof
|
|
96
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
97
|
+
|
|
98
|
+
// Tamper with S'
|
|
99
|
+
const tamperedSPrime = proof.SPrime.add(curve.g)
|
|
100
|
+
const tamperedProof = { ...proof, SPrime: tamperedSPrime }
|
|
101
|
+
|
|
102
|
+
// Verify proof
|
|
103
|
+
const result = schnorr.verifyProof(A, B, S, tamperedProof)
|
|
104
|
+
expect(result).toBe(false)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should fail verification if inputs are tampered (A modified)', () => {
|
|
108
|
+
// Generate private keys
|
|
109
|
+
const a = PrivateKey.fromRandom()
|
|
110
|
+
const b = PrivateKey.fromRandom()
|
|
111
|
+
|
|
112
|
+
// Compute public keys
|
|
113
|
+
const A = a.toPublicKey()
|
|
114
|
+
const B = b.toPublicKey()
|
|
115
|
+
|
|
116
|
+
// Compute shared secret S = B * a
|
|
117
|
+
const S = B.mul(a)
|
|
118
|
+
|
|
119
|
+
// Generate proof
|
|
120
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
121
|
+
|
|
122
|
+
// Tamper with A
|
|
123
|
+
const tamperedA = A.add(curve.g)
|
|
124
|
+
|
|
125
|
+
// Verify proof
|
|
126
|
+
const result = schnorr.verifyProof(tamperedA, B, S, proof)
|
|
127
|
+
expect(result).toBe(false)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should fail verification if inputs are tampered (B modified)', () => {
|
|
131
|
+
// Generate private keys
|
|
132
|
+
const a = PrivateKey.fromRandom()
|
|
133
|
+
const b = PrivateKey.fromRandom()
|
|
134
|
+
|
|
135
|
+
// Compute public keys
|
|
136
|
+
const A = a.toPublicKey()
|
|
137
|
+
const B = b.toPublicKey()
|
|
138
|
+
|
|
139
|
+
// Compute shared secret S = B * a
|
|
140
|
+
const S = B.mul(a)
|
|
141
|
+
|
|
142
|
+
// Generate proof
|
|
143
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
144
|
+
|
|
145
|
+
// Tamper with B
|
|
146
|
+
const tamperedB = B.add(curve.g)
|
|
147
|
+
|
|
148
|
+
// Verify proof
|
|
149
|
+
const result = schnorr.verifyProof(A, tamperedB, S, proof)
|
|
150
|
+
expect(result).toBe(false)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should fail verification if inputs are tampered (S modified)', () => {
|
|
154
|
+
// Generate private keys
|
|
155
|
+
const a = PrivateKey.fromRandom()
|
|
156
|
+
const b = PrivateKey.fromRandom()
|
|
157
|
+
|
|
158
|
+
// Compute public keys
|
|
159
|
+
const A = a.toPublicKey()
|
|
160
|
+
const B = b.toPublicKey()
|
|
161
|
+
|
|
162
|
+
// Compute shared secret S = B * a
|
|
163
|
+
const S = B.mul(a)
|
|
164
|
+
|
|
165
|
+
// Generate proof
|
|
166
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
167
|
+
|
|
168
|
+
// Tamper with S
|
|
169
|
+
const tamperedS = S.add(curve.g)
|
|
170
|
+
|
|
171
|
+
// Verify proof
|
|
172
|
+
const result = schnorr.verifyProof(A, B, tamperedS, proof)
|
|
173
|
+
expect(result).toBe(false)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should fail verification if using wrong private key', () => {
|
|
177
|
+
// Generate private keys
|
|
178
|
+
const a = PrivateKey.fromRandom()
|
|
179
|
+
const wrongA = PrivateKey.fromRandom()
|
|
180
|
+
const b = PrivateKey.fromRandom()
|
|
181
|
+
|
|
182
|
+
// Compute public keys
|
|
183
|
+
const A = a.toPublicKey()
|
|
184
|
+
const B = b.toPublicKey()
|
|
185
|
+
|
|
186
|
+
// Compute shared secret S = B * a
|
|
187
|
+
const S = B.mul(a)
|
|
188
|
+
|
|
189
|
+
// Generate proof using wrong private key
|
|
190
|
+
const proof = schnorr.generateProof(wrongA, A, B, S)
|
|
191
|
+
|
|
192
|
+
// Verify proof
|
|
193
|
+
const result = schnorr.verifyProof(A, B, S, proof)
|
|
194
|
+
expect(result).toBe(false)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('should fail verification if using wrong public key', () => {
|
|
198
|
+
// Generate private keys
|
|
199
|
+
const a = PrivateKey.fromRandom()
|
|
200
|
+
const b = PrivateKey.fromRandom()
|
|
201
|
+
const wrongB = PrivateKey.fromRandom()
|
|
202
|
+
|
|
203
|
+
// Compute public keys
|
|
204
|
+
const A = a.toPublicKey()
|
|
205
|
+
const B = b.toPublicKey()
|
|
206
|
+
const wrongBPublic = wrongB.toPublicKey()
|
|
207
|
+
|
|
208
|
+
// Compute shared secret S = B * a
|
|
209
|
+
const S = B.mul(a)
|
|
210
|
+
|
|
211
|
+
// Generate proof
|
|
212
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
213
|
+
|
|
214
|
+
// Verify proof with wrong B
|
|
215
|
+
const result = schnorr.verifyProof(A, wrongBPublic, S, proof)
|
|
216
|
+
expect(result).toBe(false)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should fail verification if shared secret S is incorrect', () => {
|
|
220
|
+
// Generate private keys
|
|
221
|
+
const a = PrivateKey.fromRandom()
|
|
222
|
+
const b = PrivateKey.fromRandom()
|
|
223
|
+
|
|
224
|
+
// Compute public keys
|
|
225
|
+
const A = a.toPublicKey()
|
|
226
|
+
const B = b.toPublicKey()
|
|
227
|
+
|
|
228
|
+
// Intentionally compute incorrect shared secret
|
|
229
|
+
const S = B.mul(a).add(curve.g)
|
|
230
|
+
|
|
231
|
+
// Generate proof with correct S
|
|
232
|
+
const proof = schnorr.generateProof(a, A, B, B.mul(a))
|
|
233
|
+
|
|
234
|
+
// Verify proof with incorrect S
|
|
235
|
+
const result = schnorr.verifyProof(A, B, S, proof)
|
|
236
|
+
expect(result).toBe(false)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('should verify a valid proof with fixed keys', () => {
|
|
240
|
+
// Use fixed private keys for determinism
|
|
241
|
+
const a = new PrivateKey(new BigNumber('123456789abcdef123456789abcdef123456789abcdef123456789abcdef', 16))
|
|
242
|
+
const b = new PrivateKey(new BigNumber('abcdef123456789abcdef123456789abcdef123456789abcdef123456789', 16))
|
|
243
|
+
|
|
244
|
+
// Compute public keys
|
|
245
|
+
const A = a.toPublicKey()
|
|
246
|
+
const B = b.toPublicKey()
|
|
247
|
+
|
|
248
|
+
// Compute shared secret S = B * a
|
|
249
|
+
const S = B.mul(a)
|
|
250
|
+
|
|
251
|
+
// Generate proof
|
|
252
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
253
|
+
|
|
254
|
+
// Verify proof
|
|
255
|
+
const result = schnorr.verifyProof(A, B, S, proof)
|
|
256
|
+
expect(result).toBe(true)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('should throw an error if inputs are invalid', () => {
|
|
260
|
+
const a = PrivateKey.fromRandom()
|
|
261
|
+
const b = PrivateKey.fromRandom()
|
|
262
|
+
const A = a.toPublicKey()
|
|
263
|
+
const B = b.toPublicKey()
|
|
264
|
+
const S = B.mul(a)
|
|
265
|
+
const proof = schnorr.generateProof(a, A, B, S)
|
|
266
|
+
|
|
267
|
+
expect(() => schnorr.verifyProof(null as any, B, S, proof)).toThrow()
|
|
268
|
+
expect(() => schnorr.verifyProof(A, null as any, S, proof)).toThrow()
|
|
269
|
+
expect(() => schnorr.verifyProof(A, B, null as any, proof)).toThrow()
|
|
270
|
+
expect(() => schnorr.verifyProof(A, B, S, null as any)).toThrow()
|
|
271
|
+
})
|
|
272
|
+
})
|
package/src/primitives/index.ts
CHANGED
|
@@ -11,3 +11,4 @@ export * as Hash from './Hash.js'
|
|
|
11
11
|
export { default as Random } from './Random.js'
|
|
12
12
|
export { default as TransactionSignature } from './TransactionSignature.js'
|
|
13
13
|
export { default as Polynomial, PointInFiniteField } from './Polynomial.js'
|
|
14
|
+
export { default as Schnorr } from './Schnorr.js'
|
package/src/totp/totp.ts
CHANGED
|
@@ -106,7 +106,7 @@ function generateHOTP (
|
|
|
106
106
|
options: Required<TOTPOptions>
|
|
107
107
|
): string {
|
|
108
108
|
const timePad = new BigNumber(counter).toArray('be', 8)
|
|
109
|
-
//console.log({ timePad })
|
|
109
|
+
// console.log({ timePad })
|
|
110
110
|
const hmac = calcHMAC(secret, timePad, options.algorithm)
|
|
111
111
|
const signature = hmac.digest()
|
|
112
112
|
|