-
-
Save xyzzy529/f4c005c2582094b24c1f89d56276217b to your computer and use it in GitHub Desktop.
ScreepsOS Crytpo Library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment