Scrambling and descrambling javascript array with salt - javascript

So i made a javascript string scrambler with md5.
var MD5 = function(d){var r = M(V(Y(X(d),8*d.length)));return r.toLowerCase()};function M(d){for(var _,m="0123456789ABCDEF",f="",r=0;r<d.length;r++)_=d.charCodeAt(r),f+=m.charAt(_>>>4&15)+m.charAt(15&_);return f}function X(d){for(var _=Array(d.length>>2),m=0;m<_.length;m++)_[m]=0;for(m=0;m<8*d.length;m+=8)_[m>>5]|=(255&d.charCodeAt(m/8))<<m%32;return _}function V(d){for(var _="",m=0;m<32*d.length;m+=8)_+=String.fromCharCode(d[m>>5]>>>m%32&255);return _}function Y(d,_){d[_>>5]|=128<<_%32,d[14+(_+64>>>9<<4)]=_;for(var m=1732584193,f=-271733879,r=-1732584194,i=271733878,n=0;n<d.length;n+=16){var h=m,t=f,g=r,e=i;f=md5_ii(f=md5_ii(f=md5_ii(f=md5_ii(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_hh(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_gg(f=md5_ff(f=md5_ff(f=md5_ff(f=md5_ff(f,r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+0],7,-680876936),f,r,d[n+1],12,-389564586),m,f,d[n+2],17,606105819),i,m,d[n+3],22,-1044525330),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+4],7,-176418897),f,r,d[n+5],12,1200080426),m,f,d[n+6],17,-1473231341),i,m,d[n+7],22,-45705983),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+8],7,1770035416),f,r,d[n+9],12,-1958414417),m,f,d[n+10],17,-42063),i,m,d[n+11],22,-1990404162),r=md5_ff(r,i=md5_ff(i,m=md5_ff(m,f,r,i,d[n+12],7,1804603682),f,r,d[n+13],12,-40341101),m,f,d[n+14],17,-1502002290),i,m,d[n+15],22,1236535329),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+1],5,-165796510),f,r,d[n+6],9,-1069501632),m,f,d[n+11],14,643717713),i,m,d[n+0],20,-373897302),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+5],5,-701558691),f,r,d[n+10],9,38016083),m,f,d[n+15],14,-660478335),i,m,d[n+4],20,-405537848),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+9],5,568446438),f,r,d[n+14],9,-1019803690),m,f,d[n+3],14,-187363961),i,m,d[n+8],20,1163531501),r=md5_gg(r,i=md5_gg(i,m=md5_gg(m,f,r,i,d[n+13],5,-1444681467),f,r,d[n+2],9,-51403784),m,f,d[n+7],14,1735328473),i,m,d[n+12],20,-1926607734),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+5],4,-378558),f,r,d[n+8],11,-2022574463),m,f,d[n+11],16,1839030562),i,m,d[n+14],23,-35309556),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+1],4,-1530992060),f,r,d[n+4],11,1272893353),m,f,d[n+7],16,-155497632),i,m,d[n+10],23,-1094730640),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+13],4,681279174),f,r,d[n+0],11,-358537222),m,f,d[n+3],16,-722521979),i,m,d[n+6],23,76029189),r=md5_hh(r,i=md5_hh(i,m=md5_hh(m,f,r,i,d[n+9],4,-640364487),f,r,d[n+12],11,-421815835),m,f,d[n+15],16,530742520),i,m,d[n+2],23,-995338651),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+0],6,-198630844),f,r,d[n+7],10,1126891415),m,f,d[n+14],15,-1416354905),i,m,d[n+5],21,-57434055),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+12],6,1700485571),f,r,d[n+3],10,-1894986606),m,f,d[n+10],15,-1051523),i,m,d[n+1],21,-2054922799),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+8],6,1873313359),f,r,d[n+15],10,-30611744),m,f,d[n+6],15,-1560198380),i,m,d[n+13],21,1309151649),r=md5_ii(r,i=md5_ii(i,m=md5_ii(m,f,r,i,d[n+4],6,-145523070),f,r,d[n+11],10,-1120210379),m,f,d[n+2],15,718787259),i,m,d[n+9],21,-343485551),m=safe_add(m,h),f=safe_add(f,t),r=safe_add(r,g),i=safe_add(i,e)}return Array(m,f,r,i)}function md5_cmn(d,_,m,f,r,i){return safe_add(bit_rol(safe_add(safe_add(_,d),safe_add(f,i)),r),m)}function md5_ff(d,_,m,f,r,i,n){return md5_cmn(_&m|~_&f,d,_,r,i,n)}function md5_gg(d,_,m,f,r,i,n){return md5_cmn(_&f|m&~f,d,_,r,i,n)}function md5_hh(d,_,m,f,r,i,n){return md5_cmn(_^m^f,d,_,r,i,n)}function md5_ii(d,_,m,f,r,i,n){return md5_cmn(m^(_|~f),d,_,r,i,n)}function safe_add(d,_){var m=(65535&d)+(65535&_);return(d>>16)+(_>>16)+(m>>16)<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_}
var hex = {
a: 10,
b: 11,
c: 13,
d: 14,
e: 15,
f: 16
}
function scrambleText(text, salt) {
if(text.length > 32) throw "No more than 32 chars.";
var strs = text.split("");
var final = "";
var hash = MD5(salt);
var hashlength = hash.length, i = 0;
while(i < hashlength) {
if(!strs.length) break;
var num = (hex[hash[i]] || parseInt(hash[i])) * 2 % strs.length;
final += strs[num];
strs.splice(num, 1);
++i;
}
return final
}
I can scramble strings but how do I unscramble the scrambled text assuming I know the salt?
I tried an attempt but it's not doing a good job
function unscrambleText(text, salt) {
var strs = text.split("");
var hash = MD5(salt);
var hashlength = hash.length, i = strs.length - 1, c = 0;;
var final = [];
while(i >= 0) {
var num = (hex[hash[i]] || parseInt(hash[i])) * 2 % (strs.length - i);
final[num] = strs[c];
final += strs[num];
strs.splice(num, 1);
--i;
++c;
}
return final
}
Can you guys help me solve this problem? I have been trying for hours.
I take the md5 of the string called "salt" then use it as a reference to scramble the string. I only use strings to simplify the problem. In my actual code, I have an array of image datas that I want to scramble, then be able to descramble it
example of image

Here's a silly script to scramble/unscramble text.
The hash function is from here: Simple (non-secure) hash function for JavaScript? Apparently, it's a JavaScript version of a Java hash function.
The script takes the "salt", hashes it, and uses each character in the hash as an offset to shift the letters up or down the ascii table. So it's essentially ROT n where n is one of the values of the hashed salt. "Rotten" is probably a pretty good description of this scramble function :D.
const hash = (str) =>
str.split("").reduce((hash, char) => {
hash = (hash << 5) - hash + char.charCodeAt(0);
return hash & hash;
}, 0);
const offset = (num, i) =>
parseInt(num.toString().charAt(i % num.toString().length));
const scramble = (st, sa, d = 1) =>
st
.split("")
.map((c, i) => String.fromCharCode(c.charCodeAt(0) + offset(sa, i) * d))
.join("");
const unscramble = (str, salt) => scramble(str, salt, -1);
const salt = "This is my salt";
const str = "My text to 'scramble'.";
const scram = scramble(str, hash(salt));
const unscram = unscramble(scram, hash(salt));
console.log("scrambled:", scram);
console.log("unscrambled:", unscram);

Related

How to increment set of 2 letters based on word occurrences in the range using GAS?

I got this one that looks hairy to me, but I'm confident you guys can crack it while having fun.
The problem:
Check of Company exists in the range
If not, get the latest ID prefix, which looks like AA, AB, etc
Generate a new prefix, which would be the following, according to item above: AC
If that company occurs more than once, then increment the ID number sufix XX001, XX002, etc.
This is what I've come up with so far:
function generateID() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const clientSheet = ss.getSheetByName('Clients');
const dataRng = clientSheet.getRange(8, 1, clientSheet.getLastRow(), clientSheet.getLastColumn());
const values = dataRng.getValues();
const companies = values.map(e => e[0]);//Gets the company for counting
for (let a = 0; a < values.length; a++) {
let company = values[a][0];
//Counts the number of occurrences of that company in the range
var companyOccurrences = companies.reduce(function (a, b) {
return a + (b == company ? 1 : 0);
}, 0);
if (companyOccurrences > 1) {
let clientIdPrefix = values[a][2].substring(0, 2);//Gets the first 2 letters of the existing company's ID
} else {
//Generate ID prefix, incrementing on the existing ID Prefixes ('AA', 'AB', 'AC'...);
let clientIdPrefix = incrementChar(values[a][2].substring(0,1));
Logger.log('Incremented Prefixes: ' + clientIdPrefix)
}
}
}
//Increment passed letter
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
function incrementChar(c) {
var index = alphabet.indexOf(c)
if (index == -1) return -1 // or whatever error value you want
return alphabet[index + 1 % alphabet.length]
}
...and this is borrowing from tckmn's answer, which deals with one letter only.
This is the expected result:
This is the link to the file, should anyone want to give it a shot.
Thank you!
In your situation, how about the following modification?
Modified script:
// Please run this function.
function generateID() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Clients');
const dataRng = sheet.getRange(8, 1, sheet.getLastRow() - 7, 1);
const values = dataRng.getValues();
let temp = "";
let init = "";
let count = 0;
const res = values.map(([a], i) => {
count++;
if (temp != a) {
count = 1;
temp = a;
init = i == 0 ? "AA" : wrapper(init);
}
return [`${init}${count.toString().padStart(3, "0")}`];
});
console.log(res)
sheet.getRange(8, 4, res.length, 1).setValues(res);
}
//Increment
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
function incrementChar(c) {
var index = alphabet.indexOf(c)
if (index == -1) return -1 // or whatever error value you want
return alphabet[index + 1 % alphabet.length]
}
// I added this function.
function wrapper(str) {
const [a, b] = [...str];
const r1 = incrementChar(a);
const r2 = incrementChar(b);
return (r2 ? [a, r2] : (r1 ? [r1, "A"] : ["over"])).join("");
}
In this modification, I added a wrapper function. This wrapper function uses your showing script of incrementChar.
When this script is run to your sample Spreadsheet, console.log(res) shows [["AA001"],["AA002"],["AA003"],["AA004"],["AA005"],["AB001"],["AB002"],["AB003"],["AC001"]]. And this value is put to the column "D".
Note:
This modified sample is for your provided Spreadsheet. So please be careful this.
Reference:
map()

Converting a 64 bit HEX to Decimal Floating-Pointt in Javascript

So basically I have a couple of numbers that come out as HEX values in the form of "3FF0000000000000" and I want to get float values of out these, pretty much like in here So in this particular case I'd expect "20.000000000000000" as a result - which I'll later trim to only 5 decimals, but that should be easy enough.
I've tried a couple of solutions but unfortunately I know too little about conversions (and javascript aswell) to know exactly what I might be doing wrong.
The latest try looks something like this:
const hexToFloat64 = (hex) => {
var int32 = [],
float64;
if (hex.length > 4) {
var high = hex.substr(0, 8),
low = hex.substr(8, 8);
int32.push(parseInt(high, 16), parseInt(low, 16));
}
var uint32 = new Uint32Array(int32);
float64 = new Float64Array(uint32.buffer);
var returnValue = float64[0];
return returnValue;
};
Much obliged!
This is NOT an answer to your exact problem. It IS a solution to decode the hex. Thats all i am doing here. I have no context to solve your problem.
function convHexStringToString(ss) {
// ss length must be even (or 0) when passed to this function
var s = "";
var p;
if (ss.length > 0) {
if (ss.length % 2 == 0) {
var l = Math.floor(ss.length / 2); // floor must never have to do work
for (var i = 0; i < l; i++) {
var i2 = i * 2;
if (ss.charAt(i2) != "0") {
p = ss.charAt(i2) + ss.charAt((i2) + 1);
}
else {
p = ss.charAt((i2) + 1);
}
d = parseInt(p,16);
s += String.fromCharCode(d);
}
}
}
return s;
}

In which way I can validate a random password?

I wrote a code to generate a random password, but in which way i can check that in the password there is a number, uppercase, lowercase and special characters?
function randomPassword(length) {
var chars = "abcdefghijklmnopqrstuvwxyz!##$%^&*1234567890ABCDEFGHIJKLMNOPQRSTUVWYZ";
var pass = "";
for (var x = 0; x < length; x++) {
var i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
return pass;
}
Why not loop over the different sets of characters. Then you don't have to check it at all:
function randomPassword(length) {
var chars = [
"abcdefghijklmnopqrstuvwxyz",
"!##$%^&*",
"1234567890",
"ABCDEFGHIJKLMNOPQRSTUVWYZ"
];
var pass = "";
while (pass.length < length) {
chars.forEach(set => {
if(pass.length < length) {
var i = Math.floor(Math.random() * set.length);
pass += set.charAt(i);
};
});
};
return pass;
};
Every iteration of the while loop will add one character from each of the sets. We check the pass.length the second time to make sure we aren't going over the limit when in the middle of a while loop. You should probably also make sure you return early if the length parameter is less than 4.
Maybe you can leave the password like it is, but to make sure you have the four required characters (number, uppercase, lowercase and special characters), you can later add one of each. These can also be chosen at random.
What you need is RegEx. Here are a few examples:
https://www.thepolyglotdeveloper.com/2015/05/use-regex-to-test-password-strength-in-javascript/
https://gist.github.com/leandromoh/470b0b54208f02a9ba223cdbdd1534bd
https://www.w3schools.com/howto/howto_js_password_validation.asp
You could generate passwords untill you get one that satisfies RegEx which would be the most random way:
function randomPassword(length) {
var chars = "abcdefghijklmnopqrstuvwxyz!##$%^&*1234567890ABCDEFGHIJKLMNOPQRSTUVWYZ";
var pass = "";
for (var x = 0; x < length; x++) {
var i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
return pass;
}
function randomStrongPassword(length) {
const strongRegex = new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{${length},})`)
let pass, valid
do {
pass = randomPassword(length)
valid = strongRegex.test(pass)
} while (valid !== true)
return pass
}
console.log(randomStrongPassword(8))
Or generate it like this and than randomize (I've added a test at end):
function randomPassword(length) {
const lower = 'abcdefghijklmnopqrstuvwxyz'
const upper = lower.toUpperCase()
const special ='!##$%^&*'
const numbers = '1234567890'
const characters = [lower, upper, special, numbers]
let pass = ""
for (var x = 0; x < length; x++) {
characters.forEach(e => {
const i = Math.floor(Math.random() * e.length)
pass += e.charAt(i)
})
}
const splice = Math.max(Math.floor(Math.random() * length) - length, 0)
return [...pass].splice(splice, length).sort(e => Math.random() - 0.5).join('')
}
console.log(randomPassword(10))
// Test password
const minLength = 8
const strongRegex = new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{${minLength},})`)
console.log(strongRegex.test(randomPassword(6)))
console.log(strongRegex.test(randomPassword(8)))

How to match and group strings efficiently in Javascript?

Write a function that returns an integer indicating number of times a group of string "pzmcb" appears in a string in no particualr orther. for example
input string 1 -> "abdpsclmhz"
output 1 -> 1
input string 2 : pzmcbcbpzmpcm
output 2: 2
I have written the code but it is not efficient and cannot handle large input string. I will appreciate it if an efficent way of writing this function can be provided
'use strict';
//pmzcbpmzcbpmz [0 -4] [5 - 9] returns 2
function matchGroup(word) {
let regex = /[pzmcb]/g
let stringArray = word.match(regex);
//console.log(stringArray);
let cloneArray = [...stringArray];
let stored = [];
let searchString = "";
let secondString = "";
let temp = "";
let tempArray = [];
stringArray.forEach(item => {
if (cloneArray.indexOf(item) >= 0 && searchString.indexOf(item) === -1) {
searchString += item;
if (searchString.length === 5) {
stored.push(searchString);
searchString = "";
}
} else if(secondString.indexOf(item) === -1){
secondString += item;
if (secondString.length === 5) {
stored.push(searchString);
secondString = "";
}
}else {
temp += item;
if (temp.length === 5) {
tempArray.push(temp);
temp = "";
}
}
});
return stored.length;
// return integer
}
var paragraph = 'pzmcbpdfbcmz';
let result = matchGroup("abcdefpfklmhgzpzmcbpdfbcmzjklmoplzdsaklmcxheqgexcmpzdhgiwqertyhgdsbnmkjilopazxcsdertijuhgbdmlpoiqarstiguzcmnbgpoimhrwqasfgdhuetiopmngbczxsgreqad");
console.log(result);
I expect that the matchGroup function to return exact integers for large inputs
I'd build up a map of character counts:
function countChars(str) {
const count = {};
for(const char of str) count[char] = (count[char] || 0) + 1;
return count;
}
Now we can easily build up count maps of the string to find and the source:
const toFind = countChars("pzmbc"),
source = countChars("pzmcbpdfbcmz");
Now we can find the smallest relationship of chars to find and chars that are there:
const result = Math.min(
...Object.entries(toFind).map(([char, need]) => Math.floor((source[char] || 0) / need))
);
function countChar(char, string) {
return (string.match(new RegExp(char, "g")) || []).length;
}
function countDistinctSubstring(sub, string) {
firstChars = {};
for (i = 0; i < sub.length; i++) {
if (sub[i] in firstChars)
firstChars[sub[i]]++;
else
firstChars[sub[i]] = 1;
}
return Math.min(...Object.keys(firstChars).map(key => Math.floor(countChar(key, string) / firstChars[key])));
}
> countDistinctSubstring("pzmcb", "abdpsclmhz");
< 1
> countDistinctSubstring("pzmcb", "pzmcbcbpzmpcm");
< 2
> countDistinctSubstring("pzmcbpdfbcmz", "abcdefpfklmhgzpzmcbpdfbcmzjklmoplzdsaklmcxheqgexcmpzdhgiwqertyhgdsbnmkjilopazxcsdertijuhgbdmlpoiqarstiguzcmnbgpoimhrwqasfgdhuetiopmngbczxsgreqad");
< 3
I can't tell for sure, but I think this is what you are looking for. It counts the number of occurrences of each letter in the small string, then finds the minimum ratio of occurrences in the large string to those in the small string for each character. This minimum ratio is the maximum number of distinct times the small string can be composed of letters from the larger one.
Note that this answer was used in making the countChar function.

Caesar Cipher (How do I convert for into .map)

It's a Caesar cipher program, I have written this code by myself and want to convert this for loop into .map JavaScript built-in function, I have tried so many times but can't figure out.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
There are many questions on .map on this website but that doesn't work for me.
function rot13(str) {
var chArray = [];
str = str.split("");
for(var i in str){
var char = str[i].charCodeAt(0);
if(/[A-M]/g.test(str[i])){
chArray.push(char + 13);
}
else if(/[N-Z]/g.test(str[i])){
chArray.push(char - 13);
}
else chArray.push(char);
}
str = String.fromCharCode.apply(String,chArray);
return str;
}
rot13("SERR PBQR PNZC");
Below is a simple adaptation of your code to use map (used a variable rotation to avoid duplicated code):
function rot13(str) {
str = str.split("");
var encryptedChArray = str.map(char => {
var rotation = 0;
if(/[A-M]/g.test(char)){
rotation = 13;
}
else if(/[N-Z]/g.test(char)) {
rotation = -13;
}
return char.charCodeAt(0) + rotation;
});
return String.fromCharCode.apply(String, encryptedChArray);
}
console.log(rot13("SERR PBQR PNZC"));
You can split the string into an array of characters, and then map over it, performing the transformations, and then at the end, join the array of transformed characters back to a string.
function rot13(str) {
// get the char code for the character
const charCode = str.charCodeAt(0);
if(/[A-M]/g.test(str)){
return String.fromCharCode(charCode + 13);
}
else if(/[N-Z]/g.test(str)){
return String.fromCharCode(charCode - 13);
}
else {
return String.fromCharCode(charCode);
}
}
// first split the string into an array of single characters
const newString = "SERR PBQR PNZC".split("").map(rot13).join('');
console.log(newString);
If you want to do this very compactly, you can do the following:
const A = 'A'.charCodeAt(0)
const M = 'M'.charCodeAt(0)
const N = 'N'.charCodeAt(0)
const Z = 'Z'.charCodeAt(0)
function rot13(str) {
var chArray = [];
return String.fromCharCode(...(
str.split('')
.map(c => c.charCodeAt(0))
.map(c =>
(A <= c && c <= M) ? c + 13 :
(N <= c && c <= Z) ? c - 13 :
c
)
))
}
console.log(rot13("SERR PBQR PNZC"))
Another option would be to use array.map here.
The mapping method takes in a callback, and that callback takes as parameter each of the elements in the array.
Example:
var arr = [1,2,3,4,5];
arr.map(someFunc);
var someFunc = function(currentItem){
console.log(currentItem);
//This will output 1
//2
//...
}
Therefore you can define you cypher transformation inside of the someFunc that you pass to map.
Suppose you want to encode the following string
var secretMessage = "hello"
msgArray = secretMessage.split("");
msgArray.map(cypher);
var cypher = function(i){
///doStuff
}

Categories