@bsv/sdk 1.9.31 → 1.10.2

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 (51) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/Peer.js +68 -48
  3. package/dist/cjs/src/auth/Peer.js.map +1 -1
  4. package/dist/cjs/src/identity/IdentityClient.js +124 -20
  5. package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
  6. package/dist/cjs/src/primitives/BigNumber.js +28 -54
  7. package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
  8. package/dist/cjs/src/primitives/ECDSA.js +36 -1
  9. package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
  10. package/dist/cjs/src/primitives/ReductionContext.js +35 -46
  11. package/dist/cjs/src/primitives/ReductionContext.js.map +1 -1
  12. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  13. package/dist/esm/src/auth/Peer.js +68 -48
  14. package/dist/esm/src/auth/Peer.js.map +1 -1
  15. package/dist/esm/src/identity/IdentityClient.js +124 -20
  16. package/dist/esm/src/identity/IdentityClient.js.map +1 -1
  17. package/dist/esm/src/primitives/BigNumber.js +28 -54
  18. package/dist/esm/src/primitives/BigNumber.js.map +1 -1
  19. package/dist/esm/src/primitives/ECDSA.js +36 -1
  20. package/dist/esm/src/primitives/ECDSA.js.map +1 -1
  21. package/dist/esm/src/primitives/ReductionContext.js +35 -46
  22. package/dist/esm/src/primitives/ReductionContext.js.map +1 -1
  23. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  24. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  25. package/dist/types/src/auth/types.d.ts +2 -0
  26. package/dist/types/src/auth/types.d.ts.map +1 -1
  27. package/dist/types/src/identity/IdentityClient.d.ts +8 -0
  28. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
  29. package/dist/types/src/primitives/BigNumber.d.ts +8 -0
  30. package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
  31. package/dist/types/src/primitives/ECDSA.d.ts +24 -0
  32. package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
  33. package/dist/types/src/primitives/ReductionContext.d.ts +9 -0
  34. package/dist/types/src/primitives/ReductionContext.d.ts.map +1 -1
  35. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  36. package/dist/umd/bundle.js +3 -3
  37. package/dist/umd/bundle.js.map +1 -1
  38. package/docs/index.md +15 -1
  39. package/docs/reference/auth.md +2 -0
  40. package/docs/reference/messages.md +0 -24
  41. package/docs/reference/primitives.md +91 -31
  42. package/package.json +1 -1
  43. package/src/auth/Peer.ts +122 -57
  44. package/src/auth/__tests/Peer.test.ts +166 -257
  45. package/src/auth/types.ts +2 -0
  46. package/src/identity/IdentityClient.ts +153 -29
  47. package/src/identity/__tests/IdentityClient.test.ts +289 -1
  48. package/src/primitives/BigNumber.ts +27 -31
  49. package/src/primitives/ECDSA.ts +41 -2
  50. package/src/primitives/ReductionContext.ts +44 -48
  51. package/src/primitives/__tests/ECDSA.test.ts +16 -0
@@ -328,6 +328,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
328
328
  )
329
329
 
330
330
  const { bobReceivedCertificates } = setupPeers(false, true)
331
+
331
332
  await mockGetVerifiableCertificates(
332
333
  aliceVerifiableCertificate,
333
334
  undefined,
@@ -335,32 +336,11 @@ describe('Peer class mutual authentication and certificate exchange', () => {
335
336
  bobPubKey
336
337
  )
337
338
 
338
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
339
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
340
- // Wrap async logic in an IIFE so the callback remains synchronous.
341
- (async () => {
342
- await bobReceivedCertificates
343
-
344
- if (certificatesReceivedByBob !== undefined && certificatesReceivedByBob.length > 0) {
345
- // Use a for...of loop instead of forEach with an async callback.
346
- for (const cert of certificatesReceivedByBob) {
347
- // Decrypt to ensure it has the correct fields.
348
- // const decryptedFields = await cert.decryptFields(walletB)
349
- if (cert.certifier !== 'bob') {
350
- // console.log('Bob accepted the message:', Utils.toUTF8(payload))
351
- // console.log('Decrypted fields:', decryptedFields)
352
- }
353
- }
354
- }
355
- resolve()
356
- })().catch((e) => {
357
- // console.error(e)
358
- })
359
- })
360
- })
339
+ // Initiate handshake ONLY
340
+ await alice.getAuthenticatedSession(bobPubKey)
361
341
 
362
- await alice.toPeer(Utils.toArray('Hello Bob!'))
363
- await bobReceivedGeneralMessage
342
+ // Wait for Bob to receive Alice's certificates
343
+ await bobReceivedCertificates
364
344
 
365
345
  expect(certificatesReceivedByAlice).toEqual([])
366
346
  expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
@@ -418,16 +398,10 @@ describe('Peer class mutual authentication and certificate exchange', () => {
418
398
  })
419
399
 
420
400
  it('Alice requests Bob to present his library card before lending him a book', async () => {
421
- const alicePubKey = (await walletA.getPublicKey({ identityKey: true }))
422
- .publicKey
423
- const bobPubKey = (await walletB.getPublicKey({ identityKey: true }))
424
- .publicKey
401
+ const alicePubKey = (await walletA.getPublicKey({ identityKey: true })).publicKey
402
+ const bobPubKey = (await walletB.getPublicKey({ identityKey: true })).publicKey
425
403
 
426
- // Bob's certificate includes his library card number
427
- const bobMasterCertificate = await createMasterCertificate(
428
- walletB,
429
- bobFields
430
- )
404
+ const bobMasterCertificate = await createMasterCertificate(walletB, bobFields)
431
405
  const bobVerifiableCertificate = await createVerifiableCertificate(
432
406
  bobMasterCertificate,
433
407
  walletB,
@@ -435,7 +409,6 @@ describe('Peer class mutual authentication and certificate exchange', () => {
435
409
  ['libraryCardNumber']
436
410
  )
437
411
 
438
- // Alice requires Bob to present his library card number
439
412
  const aliceCertificatesToRequest = {
440
413
  certifiers: [certifierPublicKey],
441
414
  types: { [certificateType]: ['libraryCardNumber'] }
@@ -444,6 +417,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
444
417
  const { aliceReceivedCertificates } = setupPeers(true, false, {
445
418
  aliceCertsToRequest: aliceCertificatesToRequest
446
419
  })
420
+
447
421
  await mockGetVerifiableCertificates(
448
422
  undefined,
449
423
  bobVerifiableCertificate,
@@ -453,39 +427,19 @@ describe('Peer class mutual authentication and certificate exchange', () => {
453
427
 
454
428
  const aliceAcceptedLibraryCard = jest.fn()
455
429
 
456
- alice.listenForCertificatesReceived((senderPublicKey, certificates) => {
457
- (async () => {
458
- for (const cert of certificates) {
459
- // Decrypt Bob's certificate fields
460
- const decryptedFields = await cert.decryptFields(walletA)
461
-
462
- // Check and use the decrypted fields
463
- if (
464
- Object.keys(decryptedFields).length !== 0 &&
465
- typeof decryptedFields.libraryCardNumber !== 'undefined'
466
- ) {
467
- // console.log(
468
- // `Alice received Bob's library card number: ${decryptedFields.libraryCardNumber}`
469
- // )
470
- aliceAcceptedLibraryCard()
471
- }
430
+ alice.listenForCertificatesReceived(async (_sender, certificates) => {
431
+ for (const cert of certificates) {
432
+ const decrypted = await cert.decryptFields(walletA)
433
+ if (decrypted.libraryCardNumber !== undefined) {
434
+ aliceAcceptedLibraryCard()
472
435
  }
473
- })().catch(e => { console.error(e) })
436
+ }
474
437
  })
475
438
 
476
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
477
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
478
- // console.log('Bob received message from Alice:', Utils.toUTF8(payload))
479
- resolve()
480
- })
481
- })
439
+ // 🔑 Correct trigger: certificate request, NOT general message
440
+ await alice.requestCertificates(aliceCertificatesToRequest, bobPubKey)
482
441
 
483
- // Alice sends a message to Bob requesting his library card before lending him a book
484
- await alice.toPeer(
485
- Utils.toArray('Please present your library card to borrow a book.')
486
- )
487
442
  await aliceReceivedCertificates
488
- await bobReceivedGeneralMessage
489
443
 
490
444
  expect(aliceAcceptedLibraryCard).toHaveBeenCalled()
491
445
  expect(certificatesReceivedByAlice).toEqual([bobVerifiableCertificate])
@@ -501,6 +455,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
501
455
  const aliceMasterCertificate = await createMasterCertificate(walletA, {
502
456
  name: 'Alice'
503
457
  })
458
+
504
459
  const aliceVerifiableCertificate = await createVerifiableCertificate(
505
460
  aliceMasterCertificate,
506
461
  walletA,
@@ -509,6 +464,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
509
464
  )
510
465
 
511
466
  const { bobReceivedCertificates } = setupPeers(false, true)
467
+
512
468
  await mockGetVerifiableCertificates(
513
469
  aliceVerifiableCertificate,
514
470
  undefined,
@@ -516,45 +472,23 @@ describe('Peer class mutual authentication and certificate exchange', () => {
516
472
  bobPubKey
517
473
  )
518
474
 
519
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
520
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
521
- (async () => {
522
- await bobReceivedCertificates
523
- // console.log('Bob received message:', Utils.toUTF8(payload))
524
-
525
- // Bob requests additional certificates after initial communication
526
- await bob.requestCertificates(certificatesToRequest, senderPublicKey)
527
- resolve()
528
- })().catch(e => { console.error(e) })
529
- })
530
- })
531
-
532
- // Initial communication from Alice
533
- await alice.toPeer(Utils.toArray('Hello Bob!'))
534
- await bobReceivedGeneralMessage
475
+ // ---- Initial communication = handshake + initial cert exchange
476
+ await alice.getAuthenticatedSession(bobPubKey)
477
+ await bobReceivedCertificates
535
478
 
536
- // Listen for certificates received from the additional request
479
+ // ---- Bob requests additional certificates AFTER validation
537
480
  const bobReceivedAdditionalCertificates = new Promise<void>((resolve) => {
538
- bob.listenForCertificatesReceived((senderPublicKey, certificates) => {
539
- (async () => {
540
- if (certificates.length > 0) {
541
- // Decrypt to confirm
542
- for (const cert of certificates) {
543
- const decrypted = await cert.decryptFields(walletB)
544
- // console.log(
545
- // 'Bob received additional certificates from Alice:',
546
- // cert
547
- // )
548
- // console.log('Decrypted fields:', decrypted)
549
- }
550
- resolve()
481
+ bob.listenForCertificatesReceived(async (senderPublicKey, certificates) => {
482
+ if (certificates.length > 0) {
483
+ for (const cert of certificates) {
484
+ await cert.decryptFields(walletB)
551
485
  }
552
- })().catch((error) => {
553
- console.error(error)
554
- })
486
+ resolve()
487
+ }
555
488
  })
556
489
  })
557
490
 
491
+ await bob.requestCertificates(certificatesToRequest, alicePubKey)
558
492
  await bobReceivedAdditionalCertificates
559
493
 
560
494
  expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
@@ -566,11 +500,11 @@ describe('Peer class mutual authentication and certificate exchange', () => {
566
500
  const bobPubKey = (await walletB.getPublicKey({ identityKey: true }))
567
501
  .publicKey
568
502
 
569
- // Alice's certificate includes her membership status
570
503
  const aliceMasterCertificate = await createMasterCertificate(walletA, {
571
504
  ...aliceFields,
572
505
  membershipStatus: 'Gold'
573
506
  })
507
+
574
508
  const aliceVerifiableCertificate = await createVerifiableCertificate(
575
509
  aliceMasterCertificate,
576
510
  walletA,
@@ -578,7 +512,6 @@ describe('Peer class mutual authentication and certificate exchange', () => {
578
512
  ['membershipStatus']
579
513
  )
580
514
 
581
- // Bob requires Alice to present her membership status
582
515
  const bobCertificatesToRequest = {
583
516
  certifiers: [certifierPublicKey],
584
517
  types: { [certificateType]: ['membershipStatus'] }
@@ -587,6 +520,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
587
520
  const { bobReceivedCertificates } = setupPeers(false, true, {
588
521
  bobCertsToRequest: bobCertificatesToRequest
589
522
  })
523
+
590
524
  await mockGetVerifiableCertificates(
591
525
  aliceVerifiableCertificate,
592
526
  undefined,
@@ -597,40 +531,26 @@ describe('Peer class mutual authentication and certificate exchange', () => {
597
531
  const bobAcceptedMembershipStatus = jest.fn()
598
532
 
599
533
  const waitForCerts = new Promise<void>((resolve) => {
600
- bob.listenForCertificatesReceived((senderPublicKey, certificates) => {
601
- (async () => {
602
- for (const cert of certificates) {
603
- // Decrypt Alice's certificate fields
604
- const decryptedFields = await cert.decryptFields(walletB)
605
- if (typeof decryptedFields.membershipStatus !== 'undefined') {
606
- // console.log(
607
- // `Bob received Alice's membership status: ${decryptedFields.membershipStatus}`
608
- // )
609
- bobAcceptedMembershipStatus()
610
- resolve()
611
- }
534
+ bob.listenForCertificatesReceived(async (_, certificates) => {
535
+ for (const cert of certificates) {
536
+ const decryptedFields = await cert.decryptFields(walletB)
537
+ if (decryptedFields.membershipStatus === 'Gold') {
538
+ bobAcceptedMembershipStatus()
539
+ resolve()
612
540
  }
613
- })().catch((error) => {
614
- console.error('Error processing certificates:', error)
615
- })
616
- }
617
- )
618
- })
619
-
620
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
621
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
622
- // console.log('Bob received message from Alice:', Utils.toUTF8(payload))
623
- resolve()
541
+ }
624
542
  })
625
543
  })
626
544
 
627
- // Alice sends a message to Bob requesting access to premium content
545
+ // ---- INITIAL COMMUNICATION = handshake + certificate exchange
546
+ await alice.getAuthenticatedSession(bobPubKey)
547
+ await bobReceivedCertificates
548
+ await waitForCerts
549
+
550
+ // ---- OPTIONAL: now Alice can request premium content
628
551
  await alice.toPeer(
629
552
  Utils.toArray('I would like to access the premium content.')
630
553
  )
631
- await bobReceivedCertificates
632
- await bobReceivedGeneralMessage
633
- await waitForCerts
634
554
 
635
555
  expect(bobAcceptedMembershipStatus).toHaveBeenCalled()
636
556
  expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
@@ -638,12 +558,9 @@ describe('Peer class mutual authentication and certificate exchange', () => {
638
558
  }, 15000)
639
559
 
640
560
  it("Both peers require each other's driver's license before carpooling", async () => {
641
- const alicePubKey = (await walletA.getPublicKey({ identityKey: true }))
642
- .publicKey
643
- const bobPubKey = (await walletB.getPublicKey({ identityKey: true }))
644
- .publicKey
561
+ const alicePubKey = (await walletA.getPublicKey({ identityKey: true })).publicKey
562
+ const bobPubKey = (await walletB.getPublicKey({ identityKey: true })).publicKey
645
563
 
646
- // Both Alice and Bob have driver's license certificates
647
564
  const aliceMasterCertificate = await createMasterCertificate(walletA, {
648
565
  ...aliceFields,
649
566
  driversLicenseNumber: 'DLA123456'
@@ -666,7 +583,6 @@ describe('Peer class mutual authentication and certificate exchange', () => {
666
583
  ['driversLicenseNumber']
667
584
  )
668
585
 
669
- // Both peers require the driver's license number
670
586
  const certificatesToRequestDriversLicense = {
671
587
  certifiers: [certifierPublicKey],
672
588
  types: { [certificateType]: ['driversLicenseNumber'] }
@@ -680,6 +596,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
680
596
  bobCertsToRequest: certificatesToRequestDriversLicense
681
597
  }
682
598
  )
599
+
683
600
  await mockGetVerifiableCertificates(
684
601
  aliceVerifiableCertificate,
685
602
  bobVerifiableCertificate,
@@ -687,91 +604,31 @@ describe('Peer class mutual authentication and certificate exchange', () => {
687
604
  bobPubKey
688
605
  )
689
606
 
690
- const aliceAcceptedBobDL = jest.fn()
691
- const bobAcceptedAliceDL = jest.fn()
692
-
693
- const waitForAliceToAcceptBobDL = new Promise<void>((resolve) => {
694
- alice.listenForCertificatesReceived((senderPublicKey, certificates) => {
695
- (async () => {
696
- for (const cert of certificates) {
697
- const decryptedFields = await cert.decryptFields(walletA)
698
- if (decryptedFields.driversLicenseNumber !== undefined) {
699
- // console.log(
700
- // `Alice received Bob's driver's license number: ${decryptedFields.driversLicenseNumber}`
701
- // )
702
- aliceAcceptedBobDL()
703
- resolve()
704
- }
705
- }
706
- })().catch(e => { })
707
- }
708
- )
709
- })
710
-
711
- const waitForBobToAcceptAliceDL = new Promise<void>((resolve) => {
712
- bob.listenForCertificatesReceived((senderPublicKey, certificates) => {
713
- (async () => {
714
- for (const cert of certificates) {
715
- const decryptedFields = await cert.decryptFields(walletB)
716
- if (decryptedFields.driversLicenseNumber !== undefined) {
717
- // console.log(
718
- // `Bob received Alice's driver's license number: ${decryptedFields.driversLicenseNumber}`
719
- // )
720
- bobAcceptedAliceDL()
721
- resolve()
722
- }
723
- }
724
- })().catch(e => { })
725
- }
726
- )
727
- })
728
-
729
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
730
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
731
- (async () => {
732
- // console.log('Bob received message from Alice:', Utils.toUTF8(payload))
733
- await bob.toPeer(
734
- Utils.toArray('Looking forward to carpooling!'),
735
- senderPublicKey
736
- )
737
- resolve()
738
- })().catch(e => { })
739
- })
740
- })
741
-
742
- const aliceReceivedGeneralMessage = new Promise<void>((resolve) => {
743
- alice.listenForGeneralMessages((senderPublicKey, payload) => {
744
- // console.log('Alice received message from Bob:', Utils.toUTF8(payload))
745
- resolve()
746
- })
747
- })
748
-
749
- // Alice initiates the conversation
750
- await alice.toPeer(
751
- Utils.toArray("Please share your driver's license number for carpooling.")
752
- )
607
+ // 🔑 Step 1: Alice requests Bob's cert
608
+ await alice.requestCertificates(certificatesToRequestDriversLicense, bobPubKey)
753
609
  await aliceReceivedCertificates
754
- await bobReceivedCertificates
755
- await bobReceivedGeneralMessage
756
- await aliceReceivedGeneralMessage
757
610
 
758
- // Wait for both sides to fully accept each other's certificate
759
- await waitForAliceToAcceptBobDL
760
- await waitForBobToAcceptAliceDL
611
+ // 🔑 Step 2: Bob requests Alice's cert
612
+ await bob.requestCertificates(certificatesToRequestDriversLicense, alicePubKey)
613
+ await bobReceivedCertificates
761
614
 
762
- expect(aliceAcceptedBobDL).toHaveBeenCalled()
763
- expect(bobAcceptedAliceDL).toHaveBeenCalled()
764
615
  expect(certificatesReceivedByAlice).toEqual([bobVerifiableCertificate])
765
616
  expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
617
+
618
+ // 🔓 Step 3: NOW general messages are allowed
619
+ const bobReceivedMessage = new Promise<void>((resolve) => {
620
+ bob.listenForGeneralMessages(() => resolve())
621
+ })
622
+
623
+ await alice.toPeer(Utils.toArray('Ready to carpool!'), bobPubKey)
624
+ await bobReceivedMessage
766
625
  }, 20000)
767
626
 
768
627
  it('Peers accept partial certificates if at least one required field is present', async () => {
769
- const alicePubKey = (await walletA.getPublicKey({ identityKey: true }))
770
- .publicKey
771
- const bobPubKey = (await walletB.getPublicKey({ identityKey: true }))
772
- .publicKey
628
+ const alicePubKey = (await walletA.getPublicKey({ identityKey: true })).publicKey
629
+ const bobPubKey = (await walletB.getPublicKey({ identityKey: true })).publicKey
773
630
 
774
- // Alice's certificate contains 'name' and 'email'; Bob's contains only 'email'
631
+ // Alice has name+email, Bob has only email
775
632
  const aliceMasterCertificate = await createMasterCertificate(walletA, {
776
633
  name: 'Alice',
777
634
  email: 'alice@example.com'
@@ -806,6 +663,7 @@ describe('Peer class mutual authentication and certificate exchange', () => {
806
663
  bobCertsToRequest: partialCertificatesToRequest
807
664
  }
808
665
  )
666
+
809
667
  await mockGetVerifiableCertificates(
810
668
  aliceVerifiableCertificate,
811
669
  bobVerifiableCertificate,
@@ -813,63 +671,36 @@ describe('Peer class mutual authentication and certificate exchange', () => {
813
671
  bobPubKey
814
672
  )
815
673
 
816
- const aliceAcceptedPartialCert = jest.fn()
817
- const bobAcceptedPartialCert = jest.fn()
674
+ // --- Exchange certs explicitly (no general messages yet) ---
675
+ await alice.requestCertificates(partialCertificatesToRequest, bobPubKey)
676
+ await bob.requestCertificates(partialCertificatesToRequest, alicePubKey)
818
677
 
819
- const waitForAlicePartialCert = new Promise<void>((resolve) => {
820
- alice.listenForCertificatesReceived((senderPublicKey, certificates) => {
821
- (async () => {
822
- for (const cert of certificates) {
823
- const decryptedFields = await cert.decryptFields(walletA)
824
- if (decryptedFields.email !== undefined || decryptedFields.name !== undefined) {
825
- // console.log(
826
- // `Alice received Bob's certificate with fields: ${Object.keys(decryptedFields).join(', ')}`
827
- // )
828
- aliceAcceptedPartialCert()
829
- resolve()
830
- }
831
- }
832
- })().catch(e => { })
833
- })
834
- })
835
-
836
- const waitForBobPartialCert = new Promise<void>((resolve) => {
837
- bob.listenForCertificatesReceived((senderPublicKey, certificates) => {
838
- (async () => {
839
- for (const cert of certificates) {
840
- const decryptedFields = await cert.decryptFields(walletB)
841
- if (decryptedFields.email !== undefined || decryptedFields.name !== undefined) {
842
- // console.log(
843
- // `Bob received Alice's certificate with fields: ${Object.keys(decryptedFields).join(', ')}`
844
- // )
845
- bobAcceptedPartialCert()
846
- resolve()
847
- }
848
- }
849
- })().catch(e => { })
850
- })
851
- })
852
-
853
- const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
854
- bob.listenForGeneralMessages((senderPublicKey, payload) => {
855
- // console.log('Bob received message:', Utils.toUTF8(payload))
856
- resolve()
857
- })
858
- })
859
-
860
- await alice.toPeer(Utils.toArray('Hello Bob!'))
861
678
  await aliceReceivedCertificates
862
679
  await bobReceivedCertificates
863
- await bobReceivedGeneralMessage
864
680
 
865
- // Wait for both sides to fully accept the partial cert
866
- await waitForAlicePartialCert
867
- await waitForBobPartialCert
681
+ // --- Validate "partial" acceptance by decrypting on each side ---
682
+ const aliceDecrypted = await certificatesReceivedByAlice![0].decryptFields(walletA)
683
+ const bobDecrypted = await certificatesReceivedByBob![0].decryptFields(walletB)
684
+
685
+ // Alice received Bob's cert which only has email, but request was name+email
686
+ expect(aliceDecrypted.email).toBeDefined()
687
+ // (Bob did not reveal name, so it may be undefined)
688
+ expect(aliceDecrypted.name).toBeUndefined()
689
+
690
+ // Bob received Alice's cert which has both name+email
691
+ expect(bobDecrypted.email).toBeDefined()
692
+ expect(bobDecrypted.name).toBeDefined()
868
693
 
869
- expect(aliceAcceptedPartialCert).toHaveBeenCalled()
870
- expect(bobAcceptedPartialCert).toHaveBeenCalled()
871
694
  expect(certificatesReceivedByAlice).toEqual([bobVerifiableCertificate])
872
695
  expect(certificatesReceivedByBob).toEqual([aliceVerifiableCertificate])
696
+
697
+ // --- Optional: now general messages should work (since validation happened) ---
698
+ const bobReceivedGeneralMessage = new Promise<void>((resolve) => {
699
+ bob.listenForGeneralMessages(() => resolve())
700
+ })
701
+
702
+ await alice.toPeer(Utils.toArray('Hello Bob!'), bobPubKey)
703
+ await bobReceivedGeneralMessage
873
704
  }, 20000)
874
705
 
875
706
  describe('Transport Error Handling', () => {
@@ -1056,4 +887,82 @@ describe('Peer class mutual authentication and certificate exchange', () => {
1056
887
  }
1057
888
  }, 15000)
1058
889
  })
890
+
891
+ describe('Certificate gating for general messages', () => {
892
+ it('rejects incoming general messages before certificate validation', async () => {
893
+ const bobPubKey = (await walletB.getPublicKey({ identityKey: true })).publicKey
894
+
895
+ setupPeers(false, true) // Bob requires certs
896
+
897
+ let received = false
898
+ bob.listenForGeneralMessages(() => {
899
+ received = true
900
+ })
901
+
902
+ // Send message — error is thrown internally
903
+ try {
904
+ await alice.toPeer(Utils.toArray('Hello Bob!'), bobPubKey)
905
+ } catch {
906
+ // swallow — error is expected but not part of assertion
907
+ }
908
+
909
+ // Allow transport handlers to run
910
+ await new Promise(r => setTimeout(r, 50))
911
+
912
+ // Message must NOT be delivered
913
+ expect(received).toBe(false)
914
+ })
915
+
916
+ it('blocks outgoing messages until certificates are validated', async () => {
917
+ setupPeers(true, false) // Alice requires certs from Bob
918
+
919
+ // Prevent Bob from auto-supplying certificates during the handshake.
920
+ // This keeps Alice in "certs required but not validated" state.
921
+ bob.listenForCertificatesRequested(() => {
922
+ // Intentionally do nothing (no auto-response)
923
+ })
924
+
925
+ await expect(
926
+ alice.toPeer(Utils.toArray('Hello Bob!'))
927
+ ).rejects.toThrow('certificate validation')
928
+ })
929
+
930
+ it('allows general messages after certificate validation completes', async () => {
931
+ const alicePubKey = (await walletA.getPublicKey({ identityKey: true })).publicKey
932
+ const bobPubKey = (await walletB.getPublicKey({ identityKey: true })).publicKey
933
+
934
+ const bobMasterCert = await createMasterCertificate(walletB, { name: 'Bob' })
935
+ const bobCert = await createVerifiableCertificate(
936
+ bobMasterCert,
937
+ walletB,
938
+ alicePubKey,
939
+ ['name']
940
+ )
941
+
942
+ // Alice requires certs from Bob
943
+ const { aliceReceivedCertificates } = setupPeers(true, false)
944
+
945
+ // Bob will provide his cert to Alice when asked
946
+ await mockGetVerifiableCertificates(
947
+ undefined,
948
+ bobCert,
949
+ alicePubKey,
950
+ bobPubKey
951
+ )
952
+
953
+ // Trigger handshake + cert exchange WITHOUT sending a general message
954
+ await alice.toPeer(Utils.toArray('handshake'), bobPubKey)
955
+
956
+ // Wait until Alice has validated Bob’s certificate
957
+ await aliceReceivedCertificates
958
+
959
+ // Now general messages must be allowed
960
+ const received = new Promise<void>((resolve) => {
961
+ bob.listenForGeneralMessages(() => resolve())
962
+ })
963
+
964
+ await alice.toPeer(Utils.toArray('Hello Bob!'), bobPubKey)
965
+ await received
966
+ })
967
+ })
1059
968
  })
package/src/auth/types.ts CHANGED
@@ -39,4 +39,6 @@ export interface PeerSession {
39
39
  peerNonce?: string
40
40
  peerIdentityKey?: string
41
41
  lastUpdate: number
42
+ certificatesRequired?: boolean
43
+ certificatesValidated?: boolean
42
44
  }