-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathsos_lib_crypto.js
204 lines (165 loc) · 6.51 KB
/
sos_lib_crypto.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
// tedivm 27 May 2017
// https://gist.github.com/tedivm/188b95437afa797515a4472d01c9ca09
/*
Copyright (c) 2017 Robert Hafner <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
*/
/*
This crypto library was made for the game Screeps.
It is not intended to be used in "real" situations- it has not been reviewed,
it was not built by an expert, and it probably has a billion holes. It was
created for fun and should only be used for fun. For all real purposes it should
be considered plain text.
This library uses Screeps `Memory` to store keys using a label. It will
automatically create keys where they do not exist, or they can be passed in
explicitly from another source.
The cipher used is a simple autokeyed streamcipher with an initialization vector
and block padding. In other words a message encrypted twice with the same key
will have different outputs, and the original message size can only be
approximated.
*/
let crypto = {};
// Reduce information leakage by padding message to fit a full block. On block
// of additional entropy is also padded to the beginning of the message.
crypto.blocksize = 4;
// skip lower unicode characters since they are symbols which may not
// transfer well.
crypto.minnumber = 0;
crypto.maxnumber = Math.pow(2, 16) - 1 - crypto.minnumber;
// Key will be autogenerated if it doesn't exist
crypto.getKey = function(label, length = 256) {
if (!Memory.sos) {
return false;
}
if (!Memory.sos.crypto) {
Memory.sos.crypto = {};
}
if (!Memory.sos.crypto.keys) {
Memory.sos.crypto.keys = {};
}
if (!Memory.sos.crypto.keys[label]) {
let key = "";
while (key.length < length) {
key += this.getCharacterFromNumber(_.random(0, this.maxnumber));
}
Memory.sos.crypto.keys[label] = key;
}
return Memory.sos.crypto.keys[label];
};
// Manually set the key for a specific label.
crypto.saveKey = function(label, key) {
if (!Memory.sos.crypto) {
Memory.sos.crypto = {};
}
if (!Memory.sos.crypto.keys) {
Memory.sos.crypto.keys = {};
}
Memory.sos.crypto.keys[label] = key;
};
// Remove key for a specific label.
crypto.removeKey = function(label) {
if (!Memory.sos.crypto) {
return;
}
if (!Memory.sos.crypto.keys) {
return;
}
if (!!Memory.sos.crypto.keys[label]) {
delete Memory.sos.crypto.keys[label];
}
};
// If 'askey' is true the label will be used as a key, otherwise it
// will be pulled from the getKey function.
crypto.encrypt = function(string, label, askey = false) {
const key = askey ? label : this.getKey(label);
// string + one character for initialization + one to define padding amount
const message_size = string.length + 2;
// Get number of blocks (rounded up) and add one more for entropy.
const blocks = Math.ceil(message_size / this.blocksize) + 1;
// Calculate full message size.
const message_size_filled = blocks * this.blocksize;
// Calculate number of needed padding characters.
const padding_count = message_size_filled - message_size;
// Start output with random character, saving it's value to add to keystream.
let output = this.getCharacterFromNumber(_.random(0, this.maxnumber));
let lastkey_value = this.getNumberFromCharacter(output);
// Add padding count and padding characters to front of message.
let prefix = "";
prefix += this.getCharacterFromNumber(padding_count);
for (let i = 0; i < padding_count; i++) {
prefix += this.getCharacterFromNumber(_.random(0, this.maxnumber));
}
string = prefix + string;
// Loop over string and build output.
for (let i = 0; i < string.length; i++) {
// Get number that represents current character in string.
const base_value = this.getNumberFromCharacter(string[i]);
// Get numeric value of current key character
const key_value = this.getNumberFromCharacter(key[i % key.length]);
// Add current value, key value, and lastkey_value. Modulus by max number.
let value = base_value + ((key_value + lastkey_value) % this.maxnumber);
// If number is higher than maxnumber subtract maxnumber.
if (value > this.maxnumber) {
value = value - this.maxnumber;
}
// Add ciphertext character to output string.
output += this.getCharacterFromNumber(value);
// Save current cleartext character to feed into next round.
lastkey_value = base_value;
}
return output;
};
// If 'askey' is true the label will be used as a key, otherwise it
// will be pulled from the getKey function.
crypto.decrypt = function(string, label, askey = false) {
const key = askey ? label : this.getKey(label);
let padding = Infinity;
let output = "";
let lastchar = string[0];
string = string.substr(1);
for (let i = 0; i < string.length; i++) {
// Get numeric value of current key character
const key_value = this.getNumberFromCharacter(key[i % key.length]);
// Get the numeric value of the last ciphertext character.
const modifier = this.getNumberFromCharacter(lastchar[0]);
// Add modifier and key value, mod by maxnumber, and subtract from ciphertext value.
let value =
this.getNumberFromCharacter(string[i]) -
((key_value + modifier) % this.maxnumber);
// If value is less than zero add maxnumber back.
if (value < 0) {
value = this.maxnumber + value;
}
// Store last cleartext character to act as modifier for next stage.
lastchar = this.getCharacterFromNumber(value);
if (i == 0) {
// Get padding value
padding = value;
} else if (padding <= 0) {
// Add cleartext character to output message
output += lastchar;
} else {
// Discard current character as it is part of the block padding
padding--;
}
}
return output;
};
// Converts unicode character to number.
crypto.getNumberFromCharacter = function(char) {
let number = char.charCodeAt(0);
number = number - this.minnumber;
return number;
};
// Converts number into unicode. Number should be less than maxnumber.
crypto.getCharacterFromNumber = function(number) {
return String.fromCharCode(+number + +this.minnumber);
};
module.exports = crypto;