This repository has been archived by the owner on Feb 15, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
/
discordCrypt.plugin.js
8403 lines (7289 loc) · 350 KB
/
discordCrypt.plugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*******************************************************************************
* This file is part of DiscordCrypt (https://gitlab.com/leogx9r/DiscordCrypt).
* Copyright (c) 2019-Present Leonardo Gates
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
"use strict";
/**
* @typedef {Object} ModulePredicate
* @desc Predicate for searching module.
* @property {*} module Module to test.
* @return {boolean} Returns `true` if `module` matches predicate.
*/
/**
* @typedef {Object} WebpackFinder
* @property {Object} module The module object.
*/
/**
* @typedef {Object} WebpackPrototypeFinder
* @property {string[]} prototypes All prototypes to search for.
*/
/**
* @typedef {Object} WebpackPropertyFinder
* @property {string[]} properties All properties to search for.
*/
/**
* @typedef {Object} WebpackDisplayNameFinder
* @property {string} displayName The display name to search for.
*/
/**
* @typedef {Object} WebpackModuleIdFinder
* @property {int} id The ID to locate.
*/
/**
* @typedef {Object} WebpackDispatchFinder
* @property {string[]} dispatchNames All dispatch names to search for.
*/
/**
* @typedef {Object} WebpackModuleSearcher
* @desc Returns various functions that can scan for webpack modules.
* @property {WebpackFinder} find Recursively iterates all webpack modules to the callback function.
* @property {WebpackPrototypeFinder} findByUniquePrototypes Iterates all modules looking for the defined prototypes.
* @property {WebpackPropertyFinder} findByUniqueProperties Iterates all modules look for the defined properties.
* @property {WebpackDisplayNameFinder} findByDisplayName Iterates all modules looking for the specified display name.
* @property {WebpackModuleIdFinder} findByDispatchToken Iterates all modules looking for the dispatch token by its ID.
* @property {WebpackDispatchFinder} findByDispatchNames Iterates all modules looking for the specified dispatch names.
*/
/**
* @typedef {Object} CachedModules
* @desc Cached Webpack modules for internal access.
* @property {Object} ChannelStore Internal channel resolver for retrieving a list of all channels available.
* @property {Object} GuildStore Internal Guild resolver for retrieving a list of all guilds currently in.
* @property {Object} MessageCreator Internal message parser that's used to translate tags to Discord symbols.
* @property {Object} MessageController Internal message controller used to receive, send and delete messages.
* @property {Object} EventDispatcher Internal message dispatcher for pending queued messages.
* @property {Object} MessageQueue Internal message Queue store for pending parsing.
* @property {Object} UserStore Internal user resolver for retrieving all users known.
*/
/**
* @typedef {Object} TimedMessage
* @desc Contains a timed message pending deletion.
* @property {string} messageId The identification tag of the timed message.
* @property {string} channelId The channel's identifier that this message was sent to.
* @property {Date} expireTime The time to purge the message from the channel.
*/
/**
* @typedef {Object} PublicKeyInfo
* @desc Contains information given an input public key.
* @property {number} index The index of the exchange algorithm.
* @property {string} fingerprint The SHA-256 sum of the public key.
* @property {string} canonical_name The canonical name describing the exchange algorithm.
* @property {string} algorithm The public key's type ( DH | ECDH ) extracted from the metadata.
* @property {int} bit_length The length, in bits, of the public key's security.
* @property {Buffer} salt The unique salt for this key.
* @property {Buffer} key The raw key.
*/
/**
* @typedef {Object} UpdateInfo
* @desc Contains information regarding a blacklisted update.
* @property {string} version Reported version of the blacklisted update.
* @property {string} payload The raw update payload.
* @property {boolean} valid The signature was marked as valid.
* @property {string} hash Checksum of the update data.
* @property {Buffer} signature The signed Ed25519 signature for the update payload.
* @property {string} changelog Reported changes that occurred during this update.
*/
/**
* @typedef {Object} ChannelStore
* @desc Storage information settings relating to the channel.
* @property {string} [primaryKey] Primary encryption key.
* @property {string} [secondaryKey] Secondary encryption key.
* @property {string[]} ignoreIds Message IDs to exclude from parsing.
* @property {boolean} autoEncrypt Whether to automatically encrypt messages.
*/
/**
* @typedef {Object} ChannelInfo
* @desc Contains settings regarding all channels.
* @property {string} channelId Channel's specific ID number.
* @property {ChannelStore} store Individual storage for this channel.
*/
/**
* @typedef {Object} Config
* @desc Contains the configuration data used for the plugin.
* @property {string} version The version of the configuration.
* @property {string} defaultPassword The default key to encrypt or decrypt message with, if not specifically defined.
* @property {string} decryptedPrefix The string that should be prepended to messages that have been decrypted.
* @property {string} encodeMessageTrigger The suffix trigger which, once appended to the message,
* forces encryption even if a key is not specifically defined for this channel.
* @property {number} encryptMode The index of the ciphers to use for message encryption.
* @property {string} encryptBlockMode The block operation mode of the ciphers used to encrypt message.
* @property {number} exchangeBitSize The size in bits of the exchange algorithm to use.
* @property {string} paddingMode Padding scheme to used to align all messages to the cipher's block length.
* @property {string} up1Host The full URI host of the Up1 service to use for encrypted file uploads.
* @property {string} up1ApiKey If specified, contains the API key used for authentication with the up1Host.
* @property {Array<TimedMessage>} timedMessages Contains all logged timed messages pending deletion.
* @property {number} timedMessageExpires How long after a message is sent should it be deleted in seconds.
* @property {boolean} automaticUpdates Whether to automatically check for updates.
* @property {boolean} autoAcceptKeyExchanges Whether to automatically accept incoming key exchange requests.
* @property {Array<UpdateInfo>} blacklistedUpdates Updates to ignore due to being blacklisted.
* @property {ChannelInfo} channels Specific data per channel.
*/
/**
* @typedef {Object} UpdateCallback
* @desc The function to execute after an update has been retrieved or if an error occurs.
* @property {UpdateInfo} [info] The update's information if valid.
*/
/**
* @typedef {Object} GetResultCallback
* @desc The function to execute at the end of a GET request containing the result or error that occurred.
* @property {int} statusCode The HTTP static code of the operation.
* @property {string|null} The HTTP error string if an error occurred.
* @property {string} data The returned data from the request.
* @return {boolean} Returns true if the data was parsed successfully.
*/
/**
* @typedef {Object} PBKDF2Callback
* @desc The function to execute after an async request for PBKDF2 is completed containing the result or error.
* @property {string} error The error that occurred during processing or null on success.
* @property {string} hash The hash either as a hex or Base64 encoded string ( or null on failure ).
*/
/**
* @typedef {Object} EncryptedFileCallback
* @desc The function to execute when a file has finished being encrypted.
* @property {string} error_string The error that occurred during operation or null if no error occurred.
* @property {Buffer} encrypted_data The resulting encrypted buffer as a Buffer() object.
* @property {string} identity The encoded identity of the encrypted file.
* @property {string} seed The initial seed used to decrypt the encryption keys of the file.
*/
/**
* @typedef {Object} UploadedFileCallback
* @desc The function to execute after a file has been uploaded to an Up1 service.
* @property {string} error_string The error that occurred or null if no error occurred.
* @property {string} file_url The URL of the uploaded file/
* @property {string} deletion_link The link used to delete the file.
* @property {string} encoded_seed The encoded encryption key used to decrypt the file.
*/
/**
* @typedef {Object} ScryptCallback
* @desc The function to execute for Scrypt based status updates.
* The function must return false repeatedly upon each call to have Scrypt continue running.
* Once [progress] === 1.f AND [key] is defined, no further calls will be made.
* @property {string} error The error message encountered or null.
* @property {real} progress The percentage of the operation completed. This ranges from [ 0.00 - 1.00 ].
* @property {Buffer} result The output result when completed or null if not completed.
* @returns {boolean} Returns false if the operation is to continue running or true if the cancel the running
* operation.
*/
/**
* @typedef {Object} HashCallback
* @desc The function to execute once the hash is calculated or an error has occurred.
* @property {string} error The error that occurred or null.
* @property {string} hash The hex or Base64 encoded result.
*/
/**
* @typedef {Object} ClipboardInfo
* @desc Contains extracted data from the current clipboard.
* @property {string} mime_type The MIME type of the extracted data.
* @property {string|null} name The name of the file, if a file was contained in the clipboard.
* @property {Buffer|null} data The raw data contained in the clipboard as a Buffer.
*/
/**
* @typedef {Object} UserMention
* @desc Contains a user-specific mention.
* @property {string} avatar The user's avatar hash.
* @property {string} discriminator The user's 4-digit discriminator.
* @property {string} id The user's unique identification number.
* @property {string} username The user's account name. ( Not display name. )
*/
/**
* @typedef {Object} MessageMentions
* @desc Contains information on what things were mentioned in a message.
* @property {boolean} mention_everyone Whether "@everyone" was used in the message.
* @property {Array<UserMention>} mentions Contains all user IDs mentioned in a message.
* @property {Array<string>} mention_roles Roles that were mentioned.
*/
/**
* @typedef {Object} LibraryInfo
* @desc Contains the library and necessary information.
* @property {boolean} requiresNode Whether this library relies on NodeJS internal support.
* @property {boolean} requiresBrowser Whether this library is meant to be run in a browser.
* @property {string} code The raw code for execution defined in the library.
*/
/**
* @typedef {Object} LibraryDefinition
* @desc Contains a definition of a raw library executed upon plugin startup.
* @property {string} name The name of the library file.
* @property {LibraryInfo} info The library info.
*/
/**
* @typedef {Object} MessageAuthor
* @desc The author of a message.
* @property {string} avatar The hash name of the user's avatar.
* @property {string} discriminator The 4-digit discriminator value for this user.
* @property {string} id The snowflake ID for the user.
* @property {string} username The name of the user.
*/
/**
* @typedef {Object} MemberInfo
* @desc The author of a message.
* @property {boolean} deaf Whether this user has been deafened.
* @property {string} joined_at The time the user joined
* @property {boolean} mute Whether the user is muted.
* @property {string} [nick] The nickname of the user, if any.
*/
/**
* @typedef {Object} EmbedFooter
* @property {string} [text] Footer text.
* @property {string} [icon_url] URL of the footer icon.
* @property {string} [proxy_icon_url] Alternative URL of the footer icon.
*/
/**
* @typedef {Object} EmbedImage
* @property {string} [url] Source url of the image. ( HTTPS links. )
* @property {string} [proxy_url] Alternative URL to the image.
* @property {number} [height] The height of the image to scale to.
* @property {number} [width] The width of the image to scale to.
*/
/**
* @typedef {Object} EmbedThumbnail
* @property {string} [url] Source URL of the thumbnail. ( HTTPS links. )
* @property {string} [proxy_url] Alternative URL to the thumbnail.
* @property {number} [height] The height of the thumbnail to scale to.
* @property {number} [width] The width of the thumbnail to scale to.
*/
/**
* @typedef {Object} EmbedVideo
* @property {string} [url] Source URL of the video. ( HTTPS links. )
* @property {number} [height] The height of the video to scale to.
* @property {number} [width] The width of the video to scale to.
*/
/**
* @typedef {Object} EmbedProvider
* @property {string} [name] The name of the provider.
* @property {string} [url] The URL of the provider.
*/
/**
* @typedef {Object} EmbedAuthor
* @property {string} [name] The name of the author.
* @property {string} [url] Source URL of the author.
* @property {string} [icon_url] URL of the author's profile icon.
* @property {string} [proxy_icon_url] Alternative URL of the author's profile icon.
*/
/**
* @typedef {Object} EmbedField
* @property {string} [name] The name of the field.
* @property {string} [value] The value of the field.
* @property {boolean} [inline] Whether this field should be inlined.
*/
/**
* @typedef {Object} Embed
* @desc Details an embedded object that may contain markdown or links.
* @property {string} [title] Optional title to be used for the embed.
* @property {string} [type] Type of the embed. Always "rich" for webhook embeds.
* @property {string} [description] Description of the embed.
* @property {string} [url] The URL this embed is referencing.
* @property {string} [timestamp] The timestamp of this embed.
* @property {number} [color] Color code of the embed.
* @property {EmbedFooter} [footer] The footer of the embed.
* @property {EmbedImage} [image] Image information.
* @property {EmbedThumbnail} [thumbnail] Thumbnail information.
* @property {EmbedVideo} [video] Video information.
* @property {EmbedProvider} [provider] Provider information
* @property {EmbedAuthor} [author] Author information
* @property {EmbedField[]} [fields] Field information.
*/
/**
* @typedef {Object} Attachment
* @property {string} id Attachment snowflake.
* @property {string} filename Attachment file name.
* @property {number} size Size of the file in bytes.
* @property {string} url Link to the attachment.
* @property {string} proxy_url A proxy to the file's URL.
* @property {number} [width] Width of the file if it's an image.
* @property {number} [height] Height of the file if it's an image.
*/
/**
* @typedef {Object} Message
* @desc An incoming or outgoing Discord message.
* @property {Array<Attachment>} [attachments] Message attachments, if any.
* @property {MessageAuthor} [author] The creator of the message.
* @property {string} channel_id The channel this message belongs to.
* @property {string} [content] The raw message content.
* @property {string} [edited_timestamp] If specified, when this message was edited.
* @property {string} [guild_id] If this message belongs to a Guild, this is the ID for it.
* @property {string} id The message's unique ID.
* @property {Embed} [embed] Optional embed for the outgoing message.
* @property {Embed[]} [embeds] Optional embeds for the incoming message.
* @property {MemberInfo} member The statistics for the author.
* @property {boolean} [mention_everyone] Whether this message attempts to mention everyone.
* @property {string[]} [mentions] User IDs or roles mentioned in this message.
* @property {string[]} [mention_roles] Role IDs mentioned in the message.
* @property {string} nonce The unique timestamp/snowflake for this message.
* @property {boolean} [pinned] Whether this message was pinned.
* @property {string} timestamp When this message was sent.
* @property {boolean} [tts] If this message should use TTS.
* @property {number} type The type of message this is.
*/
/**
* @callback EventHookCallback
* @desc This callback is executed when an event occurs.
* @desc {Object} event The event data that has occurred.
*/
/**
* @typedef {Object} EventHook
* @desc Defines an event that is handled via the dispatch event.
* @property {string} type The type of event that's handled.
* @property {EventHookCallback} callback The callback event to be executed.
*/
/**
* @typedef {Object} PublicKeyInfo
* @desc Information on a public key used for a key exchange.
* @property {Buffer} salt The user-generated salt used with this public key.
* @property {Buffer} key The raw public key buffer.
* @property {string} algorithm The exchange algorithm being used.
* @property {number} bit_length The length, in bits, of the public key.
* @property {string} fingerprint The SHA-256 sum of the public key.
*/
/**
* @typedef {Object} SessionKeyState
* @desc Indicates an active key exchange session.
* @property {PublicKeyInfo} [remoteKey] The remote party's public key.
* @property {PublicKeyInfo} [localKey] The local public key information for the session.
* @property {Object} [privateKey] The local private key corresponding to the local public key.
* @property {string} initiateTime The time this exchange was initiated.
*/
/**
* @typedef {Object} GlobalSessionState
* @desc Contains all session states being actively established.
* @property {string} channelId The channel this session establishment is taking place in.
* @property {SessionKeyState} state The local state for the session.
*/
/**
* @typedef {Object} PassphraseOptions
* @desc Parameters for generating a passphrase using the Diceware list.
* @property {number} [words] The number of words of the desired passphrase to generate.
* @property {number} [security] The desired security level in bits.
* This overrides the [words] parameter if specified.
*/
/**
* @interface
* @name PatchData
* @desc Contains local patch data and state of the function.
* @property {object} thisObject Original `this` value in current call of patched method.
* @property {Arguments} methodArguments Original `arguments` object in current call of patched method.
* Please, never change function signatures, as it may cause a lot of problems in future.
* @property {cancelPatch} cancelPatch Function with no arguments and no return value that may be
* called to reverse patching of current method. Calling this function prevents running of this
* callback on further original method calls.
* @property {function} originalMethod Reference to the original method that is patched. You can use
* it if you need some special usage. You should explicitly provide a value for `this` and any
* method arguments when you call this function.
* @property {function} callOriginalMethod This is a shortcut for calling original method using
* `this` and `arguments` from original call.
* @property {*} returnValue This is a value returned from original function call. This property is
* available only in `after` callback or in `instead` callback after calling `callOriginalMethod` function.
*/
/**
* @callback PatchCallback
* @desc A callback that modifies method logic. This callback is called on each call of the original method and is
* provided all data about original call. Any of the data can be modified if necessary, but do so wisely.
* @param {PatchData} data Data object with information about current call and original method that you may need in
* your patching callback.
* @return {*} Makes sense only when used as `instead` parameter in _monkeyPatch. If something other than
* `undefined` is returned, the returned value replaces the value of `data.returnValue`.
* If used as `before` or `after` parameters, return value is ignored.
*/
/**
* @module discordCrypt
* @desc Use a scoped variable to protect the internal state of the plugin.
* @type {_discordCrypt}
*/
const discordCrypt = ( ( ) => {
/**
* @private
* @desc Internal class instance.
* @type {_discordCrypt}
*/
let _self = null;
/**
* @private
* @desc Master database password. This is a Buffer() containing a 256-bit key.
* @type {Buffer|null}
*/
let _masterPassword = null;
/**
* @private
* @desc Used to store all event dispatcher hooks.
* @type {Array<EventHook>}
*/
let _eventHooks = [];
/**
* @private
* @desc The index of the handler used for automatic update checking.
* @type {int}
*/
let _updateHandlerInterval;
/**
* @private
* @desc The index of the handler used for timed message deletion.
* @type {int}
*/
let _timedMessageInterval;
/**
* @private
* @desc The index of the handler used for garbage collection.
* @type {int}
*/
let _garbageCollectorInterval;
/**
* @private
* @desc The configuration file currently in use. Only valid after decryption of the configuration database.
* @type {Config|null}
*/
let _configFile = null;
/**
* @private
* @desc Used to cache webpack modules.
* @type {CachedModules}
*/
let _cachedModules = {};
/**
* @private
* @desc Stores the update data for applying later on.
* @type {UpdateInfo}
*/
let _updateData = {};
/**
* @private
* @desc Array containing function callbacks to execute when stopping the plugin.
* @type {Array<function>}
*/
let _stopCallbacks = [];
/**
* @private
* @desc Contains all active sessions that are being established.
* @type {GlobalSessionState}
*/
let _globalSessionState = {};
/**
* @desc Proxy to the original file system module that doesn't read ASARs.
* @type {module:fs}
* @private
*/
let _original_fs = require( 'original-fs' );
/**
* @private
* @desc Mime-Types module for resolving file types.
* @type {function}
*/
let _mime_types = require( 'mime-types' );
/**
* @private
* @desc Form module for manipulating form objects.
* @type {FormData}
*/
let _form_data = require( 'form-data' );
/**
* @private
* @desc Main electron module to handle the application.
* @type {Electron}
*/
let _electron = require( 'electron' );
/**
* @private
* @desc Process module for receiving information on the current process.
* @type {NodeJS.Process}
*/
let _process = require( 'process' );
/**
* @private
* @desc Main crypto module for various methods.
* @type {module:crypto}
*/
let _crypto = require( 'crypto' );
/**
* @private
* @desc Path module for resolving paths on the disk.
* @type {module:path}
*/
let _path = require( 'path' );
/**
* @private
* @desc ZLib module for compression and decompression of data.
* @type {module:zlib}
*/
let _zlib = require( 'zlib' );
/**
* @private
* @desc File system module for access to the disk.
* @type {module:fs}
*/
let _fs = require( 'fs' );
/**
* @desc VM module for executing Javascript code and manipulating contexts.
* @type {module:vm}
* @private
*/
let _vm = require( 'vm' );
/**
* @private
* @desc The original methods of the Object descriptor as well as a prototype to freeze all object's props.
* @type {{freeze: function, isFrozen: function, getOwnPropertyNames: function, _freeze: function}}
*/
const _Object = {
freeze: Object.freeze,
isFrozen: Object.isFrozen,
getOwnPropertyNames: Object.getOwnPropertyNames,
_freeze: ( object ) => {
/* Skip non-objects. */
if( !object || typeof object !== 'object' )
return;
/* Recursively freeze all properties. */
for( let prop in _Object.getOwnPropertyNames( object ) )
_Object._freeze( object[ prop ] );
/* Freeze the object. */
_Object.freeze( object );
}
};
/**
* @desc The branch name used for receiving updates.
* @type {string}
*/
const UPDATE_BRANCH = 'master';
/**
* @private
* @desc Defines how many bytes can be sent in a single message that Discord will allow prior to encryption.
* @type {number}
*/
const MAX_ENCODED_DATA = 1820;
/**
* @private
* @desc The starting point that Discord uses for its timestamp calculations.
* @type {number}
*/
const DISCORD_EPOCH = 0x14AA2CAB000;
/**
* @private
* @desc Defines what an encrypted message starts with. Must be 4x UTF-16 bytes.
* @type {string}
*/
const ENCODED_MESSAGE_HEADER = "⢷⢸⢹⢺";
/**
* @private
* @desc Defines what a public key message starts with. Must be 4x UTF-16 bytes.
* @type {string}
*/
const ENCODED_KEY_HEADER = "⢻⢼⢽⢾";
/**
* @private
* @desc The Nothing-Up-My-Sleeve magic for KMAC key derivation for message payloads.
* This parameter ( P ) is used when combining the given master key, MK with a 64 bit salt S.
* It produces a unified key and IV such that:
* KEY = KMAC( MK, S, P )
* Where KEY corresponds to a concatenated message key ( mK ) and message IV ( mIV ).
* @type {Buffer}
*/
const ENCRYPT_PARAMETER = Buffer.from( 'DiscordCrypt KEY GENERATION PARAMETER' );
/**
* @private
* @desc The Nothing-Up-My-Sleeve magic for KMAC authentication tags added to messages.
* This parameter is used when computing the 256-bit authentication tag of a message.
* The parameters used here are:
* Primary Key: PK
* Secondary Key: SK
* Ciphertext Message: M
* Magic Parameter: P
* This produces the authentication tag of the message ( T ) such that:
* T = KMAC( PK || SK, M, P )
* N.B. "||" denotes concatenation.
* @type {Uint8Array}
*/
const AUTH_TAG_PARAMETER = new Uint8Array( Buffer.from( 'discordCrypt MAC' ) );
/**
* @private
* @desc The Nothing-Up-My-Sleeve magic for the KMAC-256 derivation for the primary key.
* This parameter is used when computing the primary key during a key exchange.
* The parameters used here are:
* Primary Salt: S
* Derived Secret: M
* Magic Parameter: P
* This derives the primary encryption key such that:
* PRIMARY_KEY = KMAC( S, M, P )
* @type {Uint8Array}
*/
const PRIMARY_KEY_PARAMETER = new Uint8Array( Buffer.from( 'discordCrypt-primary-secret' ) );
/**
* @private
* @desc The Nothing-Up-My-Sleeve magic for the KMAC-256 derivation for the secondary key.
* This parameter is used when computing the secondary key during a key exchange.
* The parameters used here are:
* Secondary Salt: S
* Derived Secret: M
* Magic Parameter: P
* This derives the secondary encryption key such that:
* PRIMARY_KEY = KMAC( S, M, P )
* @type {Uint8Array}
*/
const SECONDARY_KEY_PARAMETER = new Uint8Array( Buffer.from( 'discordCrypt-secondary-secret' ) );
/**
* @private
* @desc How long after a key-exchange message has been sent should it be ignored in milliseconds.
* @type {number}
*/
const KEY_IGNORE_TIMEOUT = 60 * 1000;
/**
* @private
* @desc How long after a key exchange message is sent should a client attempt to delete it in minutes.
* @type {number}
*/
const KEY_DELETE_TIMEOUT = 5;
/**
* @private
* @desc Indexes of each dual-symmetric encryption mode.
* @type {int[]}
*/
const ENCRYPT_MODES = [
/* Blowfish(Blowfish, AES, Camellia, IDEA, TripleDES) */
0, 1, 2, 3, 4,
/* AES(Blowfish, AES, Camellia, IDEA, TripleDES) */
5, 6, 7, 8, 9,
/* Camellia(Blowfish, AES, Camellia, IDEA, TripleDES) */
10, 11, 12, 13, 14,
/* IDEA(Blowfish, AES, Camellia, IDEA, TripleDES) */
15, 16, 17, 18, 19,
/* TripleDES(Blowfish, AES, Camellia, IDEA, TripleDES) */
20, 21, 22, 23, 24
];
/**
* @private
* @desc Symmetric block modes of operation.
* @type {string[]}
*/
const ENCRYPT_BLOCK_MODES = [
'CBC', /* Cipher Block-Chaining */
'CFB', /* Cipher Feedback Mode */
'OFB', /* Output Feedback Mode */
];
/**
* @private
* @desc Shorthand padding modes for block ciphers referred to in the code.
* @type {string[]}
*/
const PADDING_SCHEMES = [
'PKC7', /* PKCS #7 */
'ANS2', /* ANSI X.923 */
'ISO1', /* ISO-10126 */
'ISO9', /* ISO-97972 */
];
/**
* @private
* @desc The default host used to upload encrypted files using the Up1 specification.
* @type {string}
*/
const UP1_FILE_HOST = 'https://pastebin.synalabs.hosting';
/**
* @private
* @desc The API key used to authenticate against the Up1 host.
* @type {string}
*/
const UP1_FILE_HOST_API_KEY = '4034a170b4517897238b58ecbe902dee187bf890';
/**
* @private
* @desc Stores the base64 encoded Ed25519 public key used for update verification.
* @type {string}
*/
const ED25519_SIGNING_KEY = 'GTs+VoJSQC6e0GDTVRcskIVBhdqphUwqwrxqp64gXEQ=';
/**
* @desc The Base64 encoded SVG containing the unlocked status icon.
* @type {string}
*/
const UNLOCK_ICON = "PHN2ZyBjbGFzcz0iZGMtc3ZnIiBmaWxsPSJsaWdodGdyZXkiIGhlaWdodD0iMjBweCIgdmlld0JveD0iMCAwI" +
"DI0IDI0IiB3aWR0aD0iMjBweCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTIgMTdjMS4xI" +
"DAgMi0uOSAyLTJzLS45LTItMi0yLTIgLjktMiAyIC45IDIgMiAyem02LTloLTFWNmMwLTIuNzYtMi4yNC01LTUtNVM3IDMuMjQgN" +
"yA2aDEuOWMwLTEuNzEgMS4zOS0zLjEgMy4xLTMuMSAxLjcxIDAgMy4xIDEuMzkgMy4xIDMuMXYySDZjLTEuMSAwLTIgLjktMiAyd" +
"jEwYzAgMS4xLjkgMiAyIDJoMTJjMS4xIDAgMi0uOSAyLTJWMTBjMC0xLjEtLjktMi0yLTJ6bTAgMTJINlYxMGgxMnYxMHoiPjwvc" +
"GF0aD48L3N2Zz4=";
/**
* @desc The Base64 encoded SVG containing the locked status icon.
* @type {string}
*/
const LOCK_ICON = "PHN2ZyBjbGFzcz0iZGMtc3ZnIiBmaWxsPSJsaWdodGdyZXkiIGhlaWdodD0iMjBweCIgdmlld0JveD0iMCAwIDI" +
"0IDI0IiB3aWR0aD0iMjBweCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48cGF0aCBkPSJNMCAwaDI" +
"0djI0SDBWMHoiIGlkPSJhIi8+PC9kZWZzPjxjbGlwUGF0aCBpZD0iYiI+PHVzZSBvdmVyZmxvdz0idmlzaWJsZSIgeGxpbms6aHJ" +
"lZj0iI2EiLz48L2NsaXBQYXRoPjxwYXRoIGNsaXAtcGF0aD0idXJsKCNiKSIgZD0iTTEyIDE3YzEuMSAwIDItLjkgMi0ycy0uOS0" +
"yLTItMi0yIC45LTIgMiAuOSAyIDIgMnptNi05aC0xVjZjMC0yLjc2LTIuMjQtNS01LTVTNyAzLjI0IDcgNnYySDZjLTEuMSAwLTI" +
"gLjktMiAydjEwYzAgMS4xLjkgMiAyIDJoMTJjMS4xIDAgMi0uOSAyLTJWMTBjMC0xLjEtLjktMi0yLTJ6TTguOSA2YzAtMS43MSA" +
"xLjM5LTMuMSAzLjEtMy4xczMuMSAxLjM5IDMuMSAzLjF2Mkg4LjlWNnpNMTggMjBINlYxMGgxMnYxMHoiLz48L3N2Zz4=";
/**
* @desc Defines the CSS for the application overlays.
* @type {string}
*/
const APP_STYLE =
`/* ----- APPLICATION CSS GOES HERE DURING COMPILATION. DO NOT REMOVE. ------ */`;
/**
* @desc Contains the raw HTML used to inject into the search descriptor providing menu icons.
* @type {string}
*/
const TOOLBAR_HTML =
`/* ----- APPLICATION TOOLBAR GOES HERE DURING COMPILATION. DO NOT REMOVE. ------ */`;
/**
* @desc Contains the raw HTML injected into the overlay to prompt for the master password for database unlocking.
* @type {string}
*/
const UNLOCK_HTML =
`/* ----- APPLICATION UNLOCKING GOES HERE DURING COMPILATION. DO NOT REMOVE. ------ */`;
/**
* @desc Defines the raw HTML used describing each option menu.
* @type {string}
*/
const MENU_HTML =
`/* ----- SETTINGS GOES HERE DURING COMPILATION. DO NOT REMOVE. ------ */`;
/**
* @desc These contain all libraries that will be loaded dynamically in the current JS VM.
* @type {LibraryDefinition}
*/
const EXTERNAL_LIBRARIES = {
/* ----- LIBRARY DEFINITIONS GO HERE DURING COMPILATION. DO NOT REMOVE. ------ */
};
const BLACKLISTED_GUILDS = JSON.parse(
_zlib.inflateSync(
Buffer.from(
`/* ----- BLACKLISTED GUILD IDS GOES HERE DURING COMPILATION. DO NOT REMOVE. ------ */`,
'base64'
),
{ windowBits: 15 }
).toString( 'utf8' )
);
/**
* @desc Compressed Diceware word list provided by the official Diceware website for passphrase generation.
* @see https://world.std.com/~reinhold/diceware.html
* @type {string[]}
*/
const DICEWARE_WORD_LIST = _zlib.inflateSync(
Buffer.from(
`/* ------ DICEWARE PASSPHRASE WORD LIST GOES HERE DURING COMPILATION. DO NOT REMOVE ----- */`,
'base64'
),
{ windowBits: 15 }
).toString( 'utf8' ).split( '\r' ).join( '' ).split( '\n' );
/**
* @protected
* @class
* @desc Main plugin prototype.
*/
class _discordCrypt
{
/* ========================================================= */
/**
* @public
* @desc Initializes an instance of _discordCrypt.
* @example
* let instance = new _discordCrypt();
*/
constructor() {
/* Do this as early as possible. */
_self = this;
/* ============================================ */
/**
* Discord class names that changes ever so often because they're douches.
* These will usually be the culprit if the plugin breaks.
*/
/**
* @desc Used to find the search toolbar to inject all option buttons.
* @type {string}
*/
this._searchUiClass = '.da-search .da-searchBar';
/* ============================================ */
}
/* ==================== STANDARD CALLBACKS ================= */
/**
* @public
* @desc Returns the name of the plugin.
* @returns {string}
*/
getName() {
return 'DiscordCrypt';
}
/**
* @public
* @desc Returns the description of the plugin.
* @returns {string}
*/
getDescription() {
return 'Provides secure messaging for Discord using various cryptography standards.';
}
/**
* @public
* @desc Returns the plugin's original author.
* @returns {string}
*/
getAuthor() {
return 'leogx9r';
}
/**
* @public
* @desc Returns the current version of the plugin.
* @returns {string}
*/
getVersion() {
return '2.3.4';
}
/**
* @public
* @desc Starts the script execution. This is called by BetterDiscord if the plugin is enabled.
*/
start() {
/* Validate location startup. */
if( !_discordCrypt._ensureProperStartup() )
return;
/* Perform idiot-proof check to make sure the user named the plugin `discordCrypt.plugin.js` */
if ( !_discordCrypt._validPluginName() ) {
global.smalltalk.alert(
'Hi There! - DiscordCrypt',
"Oops!\r\n\r\n" +
"It seems you didn't read DiscordCrypt's usage guide. :(\r\n" +
"You need to name this plugin exactly as follows to allow it to function correctly.\r\n\r\n" +
`\t${_discordCrypt._getPluginName()}\r\n\r\n\r\n` +
"You should probably check the usage guide again just in case you missed anything else. :)"
);
return;
}
/* Perform startup and load the config file if not already loaded. */
if ( !_configFile ) {
/* Hook the necessary functions required for functionality. */
_discordCrypt._hookSetup();
/* Load the master password. */
_discordCrypt._loadMasterPassword();
/* Don't do anything further till we have a configuration file. */
return;
}
/* Don't check for updates if running a debug version. */
if ( !_discordCrypt._shouldIgnoreUpdates( this.getVersion() ) && _configFile.automaticUpdates ) {
/* Check for any new updates. */
_discordCrypt._checkForUpdates();
/* Add an update handler to check for updates every 60 minutes. */
_updateHandlerInterval = setInterval( () => {
_discordCrypt._checkForUpdates();
}, 3600000 );
}
/* Block tracking and analytics. */
_discordCrypt._blockTracking();
/* Start the garbage collector. */
_discordCrypt._initGarbageCollector();
}
/**
* @public
* @desc Stops the script execution.
* This is called by BetterDiscord if the plugin is disabled or during shutdown.
*/