1- import { Listener , PrivateChannel } from '@finos/fdc3-standard' ;
21import { Context } from '@finos/fdc3-context' ;
3-
4- import { createJosePrivateFDC3Security } from '../src/impl/JosePrivateFDC3Security' ;
5- import { MockChannel } from '../test/mocks/MockChannel' ;
2+ import { WebSocket } from 'ws' ;
3+ import { JosePrivateFDC3Security } from '../src/impl/JosePrivateFDC3Security' ;
64import { EncryptingChannelDelegate } from '../src/encryption/EncryptingChannelDelegate' ;
75import {
86 createSymmetricKeyRequestContextListener ,
97 createSymmetricKeyResponseContextListener ,
108} from '../src/encryption/SymmetricKeyContextListener' ;
11- import * as jose from 'jose' ;
12- import { JWKSResolver } from '../src/impl/JosePublicFDC3Security' ;
13-
14- class MockPrivateChannel extends MockChannel implements PrivateChannel {
15- constructor ( id : string ) {
16- super ( id , 'private' ) ;
17- }
18- onAddContextListener ( handler : ( contextType ?: string ) => void ) : Listener {
19- return { unsubscribe : async ( ) => { } } ;
20- }
21- onUnsubscribe ( handler : ( contextType ?: string ) => void ) : Listener {
22- return { unsubscribe : async ( ) => { } } ;
23- }
24- onDisconnect ( handler : ( ) => void ) : Listener {
25- return { unsubscribe : async ( ) => { } } ;
26- }
27- async disconnect ( ) : Promise < void > { }
28- async addEventListener ( type : string | null , handler : any ) : Promise < Listener > {
29- return { unsubscribe : async ( ) => { } } ;
30- }
9+ import { DefaultFDC3Handlers } from '../src/secure-boundary/FDC3Handlers' ;
10+ import { AppBackEnd } from '../test/mocks/AppBackEnd' ;
11+ import { MockPrivateChannel } from '../test/mocks/MockPrivateChannel' ;
12+
13+ /**
14+ * STEP 1: Setup App A (listener)
15+ */
16+ async function step1SetupAppA ( ) {
17+ console . log ( '1. Starting App A backend...' ) ;
18+ return AppBackEnd . start ( 0 , ( _ws : WebSocket , _security : JosePrivateFDC3Security ) => new DefaultFDC3Handlers ( ) ) ;
3119}
3220
33- async function runExample ( ) {
34- console . log ( '--- FDC3 Encrypted Private Channel Example Start ---' ) ;
35-
36- // Setup local registry for JWKS resolving without a real HTTP server
37- const registry : Record < string , JsonWebKey [ ] > = { } ;
38- const resolver = ( url : string ) => {
39- const keys = registry [ url ] || [ ] ;
40- const localSet = jose . createLocalJWKSet ( { keys : keys as any } ) ;
41- const r : any = async ( ph : any , tok : any ) => localSet ( ph , tok ) ;
42- r . reload = async ( ) => { } ;
43- r . jwks = ( ) => ( { keys } ) ;
44- return r as JWKSResolver ;
45- } ;
46-
47- // 1. Create App A and App B Security Objects
48- const appASecurity = await createJosePrivateFDC3Security ( 'http://localhost:1111' , resolver , ( ) => true ) ;
49- const appBSecurity = await createJosePrivateFDC3Security ( 'http://localhost:2222' , resolver , ( ) => true ) ;
50-
51- registry [ 'http://localhost:1111/.well-known/jwks.json' ] = [
52- appASecurity . signingPublicKey ,
53- appASecurity . wrappingPublicKey ,
54- ] ;
55- registry [ 'http://localhost:2222/.well-known/jwks.json' ] = [
56- appBSecurity . signingPublicKey ,
57- appBSecurity . wrappingPublicKey ,
58- ] ;
59-
60- // 2. Setup the underlying "wire" mock channel
61- const privateChannel = new MockPrivateChannel ( 'private-channel-1' ) ;
21+ /**
22+ * STEP 2: Setup App B (broadcaster)
23+ */
24+ async function step2SetupAppB ( ) {
25+ console . log ( '2. Starting App B backend...' ) ;
26+ return AppBackEnd . start ( 0 , ( _ws : WebSocket , _security : JosePrivateFDC3Security ) => new DefaultFDC3Handlers ( ) ) ;
27+ }
6228
63- // 3. App B Setup
64- console . log ( '[App B] Initializing private channel delegate...' ) ;
29+ /**
30+ * STEP 3: Setup App B (The Broadcaster & Key Creator)
31+ */
32+ async function step3SetupAppBDelegate ( appBSecurity : JosePrivateFDC3Security , privateChannel : MockPrivateChannel ) {
33+ console . log ( '3. App B Setup: Initializing private channel delegate for broadcasting...' ) ;
6534 // Note: We use metadataAvailable: true here so signatures are propagated in standard metadata argument
6635 const appBDelegate = new EncryptingChannelDelegate ( privateChannel , true , appBSecurity ) ;
6736 await appBDelegate . setChannelEncryption ( type => {
68- return type ! == 'fdc3.security.symmetricKeyRequest' && type !== 'fdc3.security.symmetricKeyResponse ';
69- } ) ; // Encrypt general contexts, but keep protocol negotiation in clear text
37+ return type == 'test.encrypted ' ;
38+ } ) ;
7039
7140 // Start the symmetric key request listener.
7241 // It will create a symmetric key automatically and provide it upon valid request.
7342 await createSymmetricKeyRequestContextListener ( appBSecurity , appBDelegate ) ;
7443
75- console . log ( '[App A] Resolving intent and connecting to Private Channel...' ) ;
44+ return appBDelegate ;
45+ }
46+
47+ /**
48+ * STEP 4: Setup App A (The Listener / Receiver)
49+ */
50+ async function step4SetupAppADelegate ( appASecurity : JosePrivateFDC3Security , privateChannel : MockPrivateChannel ) {
51+ console . log ( '4. App A Setup: Resolving intent and connecting to Private Channel as listener...' ) ;
7652
77- // 4. App A Setup
7853 const appADelegate = new EncryptingChannelDelegate ( privateChannel , true , appASecurity ) ;
7954
8055 // App A needs a listener to receive the wrapped symmetric key when requested
8156 await createSymmetricKeyResponseContextListener ( appASecurity , appADelegate ) ;
8257
83- // App A adds a normal context listener
58+ // App A adds a normal context listener for the encrypted content
8459 appADelegate . addContextListener ( 'test.encrypted' , ( ctx : Context ) => {
8560 console . log ( `\n[App A] \u2705 Successfully Received and Decrypted Context:` ) ;
8661 console . log ( JSON . stringify ( ctx , null , 2 ) ) ;
8762 } ) ;
8863
64+ return appADelegate ;
65+ }
66+
67+ /**
68+ * STEP 5: App B Broadcasts Encrypted Messages
69+ */
70+ function step5AppBBroadcasts ( appBDelegate : EncryptingChannelDelegate , apps : AppBackEnd [ ] ) : void {
8971 console . log ( '[App B] Starting to broadcast encrypted messages...' ) ;
9072
91- // 5. App B begins broadcasting
9273 let count = 0 ;
9374 const interval = setInterval ( async ( ) => {
9475 count ++ ;
@@ -98,10 +79,30 @@ async function runExample() {
9879 if ( count >= 10 ) {
9980 clearInterval ( interval ) ;
10081 console . log ( '\n--- FDC3 Encrypted Private Channel Example Complete ---' ) ;
82+ apps . forEach ( app => app . shutdown ( ) ) ;
10183 }
10284 } , 1000 ) ;
10385}
10486
87+ /**
88+ * MAIN EXECUTION
89+ *
90+ * This example simulates a private channel over which two applications stream encrypted
91+ * data securely using symmetric encryption, securely distributing the symmetric key
92+ * upon request.
93+ */
94+ async function runExample ( ) {
95+ console . log ( '--- FDC3 Encrypted Private Channel Example Start ---' ) ;
96+
97+ const appA = await step1SetupAppA ( ) ;
98+ const appB = await step2SetupAppB ( ) ;
99+ const privateChannel = new MockPrivateChannel ( 'private-channel-1' ) ;
100+
101+ const appBDelegate = await step3SetupAppBDelegate ( appB . security , privateChannel ) ;
102+ await step4SetupAppADelegate ( appA . security , privateChannel ) ;
103+ step5AppBBroadcasts ( appBDelegate , [ appA , appB ] ) ;
104+ }
105+
105106runExample ( ) . catch ( err => {
106107 console . error ( 'Error running example:' , err ) ;
107108} ) ;
0 commit comments