25
25
import { world , system } from '@minecraft/server'
26
26
27
27
namespace IPC {
28
- const MAX_STR_LENGTH = 1024
29
28
let ID = 0
30
29
30
+ export namespace CONFIG {
31
+ export namespace ENCRYPTION {
32
+ /**
33
+ * @description Used to generate secrets, must be a prime number
34
+ * @default 19893121
35
+ * @warning Modify only if you know what you're doing, incorrect values can cause issues
36
+ */
37
+ export let PRIME : number = 19893121
38
+ /**
39
+ * @description Used to generate secrets, must be a prime root of {@link PRIME}
40
+ * @default 341
41
+ * @warning Modify only if you know what you're doing, incorrect values can cause issues
42
+ */
43
+ export let MOD : number = 341
44
+ }
45
+ export namespace FRAGMENTATION {
46
+ /**
47
+ * @description Used when fragmenting data strings
48
+ * @default 1024
49
+ * @warning Modify only if you know what you're doing, incorrect values can cause issues
50
+ */
51
+ export let MAX_STR_LENGTH : number = 1024
52
+ }
53
+ }
54
+
55
+ namespace ENCRYPTION {
56
+ export function generate_secret ( mod : number = CONFIG . ENCRYPTION . MOD ) : number {
57
+ return Math . floor ( Math . random ( ) * ( mod - 1 ) ) + 1
58
+ }
59
+
60
+ export function generate_public (
61
+ secret : number ,
62
+ mod : number = CONFIG . ENCRYPTION . MOD ,
63
+ prime : number = CONFIG . ENCRYPTION . PRIME
64
+ ) : string {
65
+ return HEX ( mod_exp ( mod , secret , prime ) )
66
+ }
67
+
68
+ export function generate_shared (
69
+ secret : number ,
70
+ other_key : string ,
71
+ prime : number = CONFIG . ENCRYPTION . PRIME
72
+ ) : string {
73
+ return HEX ( mod_exp ( NUM ( other_key ) , secret , prime ) )
74
+ }
75
+
76
+ export function encrypt ( raw : string , key : string ) : string {
77
+ let encrypted = ''
78
+ for ( let i = 0 ; i < raw . length ; i ++ ) {
79
+ encrypted += String . fromCharCode ( raw . charCodeAt ( i ) ^ key . charCodeAt ( i % key . length ) )
80
+ }
81
+ return encrypted
82
+ }
83
+
84
+ export function decrypt ( encrypted : string , key : string ) : string {
85
+ let decrypted = ''
86
+ for ( let i = 0 ; i < encrypted . length ; i ++ ) {
87
+ decrypted += String . fromCharCode ( encrypted . charCodeAt ( i ) ^ key . charCodeAt ( i % key . length ) )
88
+ }
89
+ return decrypted
90
+ }
91
+
92
+ function mod_exp ( base : number , exp : number , mod : number ) : number {
93
+ let result = 1
94
+ base = base % mod
95
+ while ( exp > 0 ) {
96
+ if ( exp % 2 === 1 ) {
97
+ result = ( result * base ) % mod
98
+ }
99
+ exp = Math . floor ( exp / 2 )
100
+ base = ( base * base ) % mod
101
+ }
102
+ return result
103
+ }
104
+
105
+ function HEX ( num : number ) : string {
106
+ return num . toString ( 16 ) . toUpperCase ( )
107
+ }
108
+ function NUM ( hex : string ) : number {
109
+ return parseInt ( hex , 16 )
110
+ }
111
+ }
112
+
31
113
export class Connection {
32
114
private readonly _from : string
33
115
private readonly _to : string
116
+ private readonly _enc : string | false
34
117
35
118
get from ( ) {
36
119
return this . _from
@@ -40,20 +123,24 @@ namespace IPC {
40
123
return this . _to
41
124
}
42
125
43
- constructor ( from : string , to : string ) {
126
+ constructor ( from : string , to : string , encryption : string | false ) {
44
127
this . _from = from
45
128
this . _to = to
129
+ this . _enc = encryption
46
130
}
47
131
48
132
send ( channel : string , ...args : any [ ] ) : void {
49
- emit ( 'send' , `${ this . _to } :${ channel } ` , [ this . _from , args ] )
133
+ const data = this . _enc !== false ? ENCRYPTION . encrypt ( JSON . stringify ( args ) , this . _enc ) : args
134
+ emit ( 'send' , `${ this . _to } :${ channel } ` , [ this . _from , data ] )
50
135
}
51
136
52
137
invoke ( channel : string , ...args : any [ ] ) : Promise < any > {
53
- emit ( 'invoke' , `${ this . _to } :${ channel } ` , [ this . _from , args ] )
138
+ const data = this . _enc !== false ? ENCRYPTION . encrypt ( JSON . stringify ( args ) , this . _enc ) : args
139
+ emit ( 'invoke' , `${ this . _to } :${ channel } ` , [ this . _from , data ] )
54
140
return new Promise ( resolve => {
55
141
const listener = listen ( 'handle' , `${ this . _from } :${ channel } ` , args => {
56
- resolve ( args [ 1 ] )
142
+ const data = this . _enc !== false ? JSON . parse ( ENCRYPTION . decrypt ( args [ 1 ] as string , this . _enc ) ) : args [ 1 ]
143
+ resolve ( data )
57
144
system . afterEvents . scriptEventReceive . unsubscribe ( listener )
58
145
} )
59
146
} )
@@ -62,66 +149,76 @@ namespace IPC {
62
149
63
150
export class ConnectionManager {
64
151
private readonly _id : string
152
+ private readonly _enc_map : Map < string , string | false >
153
+ private readonly _enc_force : boolean
65
154
66
155
get id ( ) {
67
156
return this . _id
68
157
}
69
158
70
- constructor ( id : string ) {
159
+ constructor ( id : string , force_encryption : boolean = false ) {
71
160
this . _id = id
72
-
161
+ this . _enc_map = new Map < string , string | false > ( )
162
+ this . _enc_force = force_encryption
73
163
listen ( 'handshake' , `${ this . _id } :SYN` , args => {
74
- emit ( 'handshake' , `${ args [ 0 ] } :ACK` , [ this . _id ] )
164
+ const secret = ENCRYPTION . generate_secret ( args [ 4 ] )
165
+ const public_key = ENCRYPTION . generate_public ( secret , args [ 4 ] , args [ 3 ] )
166
+ const enc = args [ 1 ] === 1 || this . _enc_force ? ENCRYPTION . generate_shared ( secret , args [ 2 ] , args [ 3 ] ) : false
167
+ this . _enc_map . set ( args [ 0 ] , enc )
168
+ emit ( 'handshake' , `${ args [ 0 ] } :ACK` , [ this . _id , this . _enc_force ? 1 : 0 , public_key ] )
75
169
} )
76
170
}
77
171
78
- connect ( to : string , timeout ?: number ) : Promise < Connection > {
79
- return new Promise < Connection > ( ( resolve , reject ) => {
80
- this . handshake ( to , timeout ) . then ( success => {
81
- if ( success ) {
82
- resolve ( new Connection ( this . _id , to ) )
83
- } else {
84
- reject ( )
172
+ connect ( to : string , encrypted : boolean = false , timeout : number = 20 ) : Promise < Connection > {
173
+ const secret = ENCRYPTION . generate_secret ( )
174
+ const public_key = ENCRYPTION . generate_public ( secret )
175
+ const enc_flag = encrypted ? 1 : 0
176
+ emit ( 'handshake' , `${ to } :SYN` , [ this . _id , enc_flag , public_key , CONFIG . ENCRYPTION . PRIME , CONFIG . ENCRYPTION . MOD ] )
177
+ return new Promise ( ( resolve , reject ) => {
178
+ function clear ( ) {
179
+ system . afterEvents . scriptEventReceive . unsubscribe ( listener )
180
+ system . clearRun ( timeout_handle )
181
+ }
182
+ const timeout_handle = system . runTimeout ( ( ) => {
183
+ reject ( )
184
+ clear ( )
185
+ } , timeout )
186
+ const listener = listen ( 'handshake' , `${ this . _id } :ACK` , args => {
187
+ if ( args [ 0 ] === to ) {
188
+ const enc = args [ 1 ] === 1 || encrypted ? ENCRYPTION . generate_shared ( secret , args [ 2 ] ) : false
189
+ resolve ( new Connection ( this . _id , to , enc ) )
190
+ clear ( )
85
191
}
86
192
} )
87
193
} )
88
194
}
89
195
90
196
handle ( channel : string , listener : ( ...args : any [ ] ) => any ) {
91
197
listen ( 'invoke' , `${ this . _id } :${ channel } ` , args => {
92
- const result = listener ( ...args [ 1 ] )
93
- emit ( 'handle' , `${ args [ 0 ] } :${ channel } ` , [ this . _id , result ] )
198
+ const enc = this . _enc_map . get ( args [ 0 ] ) as string | false
199
+ const data : any [ ] = enc !== false ? JSON . parse ( ENCRYPTION . decrypt ( args [ 1 ] as string , enc ) ) : args [ 1 ]
200
+ const result = listener ( ...data )
201
+ const return_data = enc !== false ? ENCRYPTION . encrypt ( JSON . stringify ( result ) , enc ) : result
202
+ emit ( 'handle' , `${ args [ 0 ] } :${ channel } ` , [ this . _id , return_data ] )
94
203
} )
95
204
}
96
205
97
206
on ( channel : string , listener : ( ...args : any [ ] ) => void ) {
98
- listen ( 'send' , `${ this . _id } :${ channel } ` , args => listener ( ...args [ 1 ] ) )
207
+ listen ( 'send' , `${ this . _id } :${ channel } ` , args => {
208
+ const enc = this . _enc_map . get ( args [ 0 ] ) as string | false
209
+ const data : any [ ] = enc !== false ? JSON . parse ( ENCRYPTION . decrypt ( args [ 1 ] as string , enc ) ) : args [ 1 ]
210
+ listener ( ...data )
211
+ } )
99
212
}
100
213
101
214
once ( channel : string , listener : ( ...args : any [ ] ) => void ) {
102
215
const event = listen ( 'send' , `${ this . _id } :${ channel } ` , args => {
103
- listener ( ...args [ 1 ] )
216
+ const enc = this . _enc_map . get ( args [ 0 ] ) as string | false
217
+ const data : any [ ] = enc !== false ? JSON . parse ( ENCRYPTION . decrypt ( args [ 1 ] as string , enc ) ) : args [ 1 ]
218
+ listener ( ...data )
104
219
system . afterEvents . scriptEventReceive . unsubscribe ( event )
105
220
} )
106
221
}
107
-
108
- private handshake ( receiver : string , timeout : number = 20 ) : Promise < boolean > {
109
- emit ( 'handshake' , `${ receiver } :SYN` , [ this . _id ] )
110
- return new Promise ( resolve => {
111
- const run_timeout = system . runTimeout ( ( ) => {
112
- resolve ( false )
113
- system . afterEvents . scriptEventReceive . unsubscribe ( listener )
114
- system . clearRun ( run_timeout )
115
- } , timeout )
116
- const listener = listen ( 'handshake' , `${ this . _id } :ACK` , args => {
117
- if ( args [ 0 ] === receiver ) {
118
- resolve ( true )
119
- system . afterEvents . scriptEventReceive . unsubscribe ( listener )
120
- system . clearRun ( run_timeout )
121
- }
122
- } )
123
- } )
124
- }
125
222
}
126
223
127
224
interface Payload {
@@ -191,7 +288,8 @@ namespace IPC {
191
288
}
192
289
193
290
function emit ( event_id : string , channel : string , args : any [ ] ) {
194
- const data_fragments : string [ ] = JSON . stringify ( args ) . match ( new RegExp ( `.{1,${ MAX_STR_LENGTH } }` , 'g' ) ) ?? [ ]
291
+ const data_fragments : string [ ] =
292
+ JSON . stringify ( args ) . match ( new RegExp ( `.{1,${ CONFIG . FRAGMENTATION . MAX_STR_LENGTH } }` , 'g' ) ) ?? [ ]
195
293
const payload_strings = data_fragments
196
294
. map ( ( data , index ) => {
197
295
return data_fragments . length > 1
0 commit comments