Skip to content

Instantly share code, notes, and snippets.

@xyzzy529
Forked from tedivm/sos_lib_crypto.js
Created May 31, 2017 20:53
Show Gist options
  • Save xyzzy529/f4c005c2582094b24c1f89d56276217b to your computer and use it in GitHub Desktop.
Save xyzzy529/f4c005c2582094b24c1f89d56276217b to your computer and use it in GitHub Desktop.
ScreepsOS Crytpo Library
/*
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