@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.
Files changed (63) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/compat/ECIES.js +24 -5
  3. package/dist/cjs/src/compat/ECIES.js.map +1 -1
  4. package/dist/cjs/src/primitives/Schnorr.js +92 -0
  5. package/dist/cjs/src/primitives/Schnorr.js.map +1 -0
  6. package/dist/cjs/src/primitives/index.js +3 -1
  7. package/dist/cjs/src/primitives/index.js.map +1 -1
  8. package/dist/cjs/src/totp/totp.js +1 -1
  9. package/dist/cjs/src/totp/totp.js.map +1 -1
  10. package/dist/cjs/src/transaction/Beef.js +139 -118
  11. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  12. package/dist/cjs/src/transaction/BeefParty.js +30 -26
  13. package/dist/cjs/src/transaction/BeefParty.js.map +1 -1
  14. package/dist/cjs/src/transaction/BeefTx.js +25 -15
  15. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  16. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  17. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/compat/ECIES.js +24 -5
  20. package/dist/esm/src/compat/ECIES.js.map +1 -1
  21. package/dist/esm/src/primitives/Schnorr.js +87 -0
  22. package/dist/esm/src/primitives/Schnorr.js.map +1 -0
  23. package/dist/esm/src/primitives/index.js +1 -0
  24. package/dist/esm/src/primitives/index.js.map +1 -1
  25. package/dist/esm/src/totp/totp.js +1 -1
  26. package/dist/esm/src/totp/totp.js.map +1 -1
  27. package/dist/esm/src/transaction/Beef.js +142 -121
  28. package/dist/esm/src/transaction/Beef.js.map +1 -1
  29. package/dist/esm/src/transaction/BeefParty.js +31 -27
  30. package/dist/esm/src/transaction/BeefParty.js.map +1 -1
  31. package/dist/esm/src/transaction/BeefTx.js +29 -19
  32. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  33. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  34. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/compat/ECIES.d.ts.map +1 -1
  37. package/dist/types/src/primitives/Schnorr.d.ts +65 -0
  38. package/dist/types/src/primitives/Schnorr.d.ts.map +1 -0
  39. package/dist/types/src/primitives/index.d.ts +1 -0
  40. package/dist/types/src/primitives/index.d.ts.map +1 -1
  41. package/dist/types/src/transaction/Beef.d.ts +94 -94
  42. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  43. package/dist/types/src/transaction/BeefParty.d.ts +23 -23
  44. package/dist/types/src/transaction/BeefParty.d.ts.map +1 -1
  45. package/dist/types/src/transaction/BeefTx.d.ts +5 -5
  46. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  47. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  48. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  49. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  50. package/dist/umd/bundle.js +1 -1
  51. package/docs/primitives.md +120 -9
  52. package/package.json +1 -1
  53. package/src/compat/ECIES.ts +38 -17
  54. package/src/compat/__tests/ECIES.test.ts +23 -1
  55. package/src/primitives/Schnorr.ts +95 -0
  56. package/src/primitives/__tests/Schnorr.test.ts +272 -0
  57. package/src/primitives/index.ts +1 -0
  58. package/src/totp/totp.ts +1 -1
  59. package/src/transaction/Beef.ts +351 -375
  60. package/src/transaction/BeefParty.ts +54 -58
  61. package/src/transaction/BeefTx.ts +128 -143
  62. package/src/transaction/MerklePath.ts +11 -11
  63. package/src/transaction/Transaction.ts +32 -32
@@ -8,15 +8,16 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
8
8
 
9
9
  | | | |
10
10
  | --- | --- | --- |
11
- | [BasePoint](#class-basepoint) | [Point](#class-point) | [SHA1HMAC](#class-sha1hmac) |
12
- | [BigNumber](#class-bignumber) | [PointInFiniteField](#class-pointinfinitefield) | [SHA256](#class-sha256) |
13
- | [Curve](#class-curve) | [Polynomial](#class-polynomial) | [SHA256HMAC](#class-sha256hmac) |
14
- | [DRBG](#class-drbg) | [PrivateKey](#class-privatekey) | [SHA512](#class-sha512) |
15
- | [JacobianPoint](#class-jacobianpoint) | [PublicKey](#class-publickey) | [SHA512HMAC](#class-sha512hmac) |
16
- | [K256](#class-k256) | [RIPEMD160](#class-ripemd160) | [Signature](#class-signature) |
17
- | [KeyShares](#class-keyshares) | [Reader](#class-reader) | [SymmetricKey](#class-symmetrickey) |
18
- | [Mersenne](#class-mersenne) | [ReductionContext](#class-reductioncontext) | [TransactionSignature](#class-transactionsignature) |
19
- | [MontgomoryMethod](#class-montgomorymethod) | [SHA1](#class-sha1) | [Writer](#class-writer) |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.1.28",
3
+ "version": "1.1.30",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -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
- decTable[1][sbox[tmp >> 16 & 255]] ^
50
- decTable[2][sbox[tmp >> 8 & 255]] ^
51
- decTable[3][sbox[tmp & 255]]
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
- sbox[a >>> 24] << 24 ^
166
- sbox[b >> 16 & 255] << 16 ^
167
- sbox[c >> 8 & 255] << 8 ^
168
- sbox[d & 255] ^
169
- key[kIndex++]
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
- (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
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
- if (!fromPublicKey) {
481
- // BIE1 use compressed public key, length is always 33.
482
- const pub = encBuf.slice(4, 37)
483
- fromPublicKey = PublicKey.fromString(toHex(pub))
484
- offset = 37
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
+ })
@@ -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