282

I'm interested in building a small app for personal use that will encrypt and decrypt information on the client side using JavaScript. The encrypted information will be stored in a database on a server, but never the decrypted version.

It doesn't have to be super duper secure, but I would like to use a currently unbroken algorithm.

Ideally I'd be able to do something like

var gibberish = encrypt(string, salt, key);

to generate the encoded string, and something like

var sensical = decrypt(gibberish, key);

to decode it later.

So far I've seen this: http://bitwiseshiftleft.github.io/sjcl/

Any other libraries I should look at?

4
  • 2
    Take a look at Javascript AES encryption Commented Aug 16, 2013 at 17:44
  • 2
    jsfiddle.net/kein1945/M9K2c Commented Aug 16, 2013 at 17:56
  • 20
    Some terminology here is off, Here's a simple version 1. Salts are added to information (usually passwords) being hashed. Their purpose is to make the hash different than it would be without the salt. This is useful because it makes pre-generated of hashes if your database gets hacked and hashed user passwords get out. 2. Hashing is a one-way operation that translates input into output. It cannot be easily reversed or undone. 3. Encoding is not encryption. base64_encode, urlencode, etc. Commented Aug 16, 2013 at 17:58
  • 1
    I struggled to find that nowadays we have something like window.crypto natively. developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt Commented Dec 27, 2022 at 15:58

12 Answers 12

247

 var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
//U2FsdGVkX18ZUVvShFSES21qHsQEqZXMxQ9zgHy+bu0=

var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
//4d657373616765


document.getElementById("demo1").innerHTML = encrypted;
document.getElementById("demo2").innerHTML = decrypted;
document.getElementById("demo3").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
Full working sample actually is:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" integrity="sha256-/H4YS+7aYb9kJ5OKhFYPUjSJdrtV6AeyJOtTkw6X72o=" crossorigin="anonymous"></script>

<br><br>
<label>encrypted</label>
<div id="demo1"></div>
<br>

<label>decrypted</label>
<div id="demo2"></div>

<br>
<label>Actual Message</label>
<div id="demo3"></div>

Sign up to request clarification or add additional context in comments.

11 Comments

Encrypted is actually an object, but you can call encrypted.toString() to get the string. You will be able to decrypt that string later: jsbin.com/kofiqokoku/1
But how can we secure the Secret passphrase?
It seems crypto js is an archived project. There is a clone on github: github.com/sytelus/CryptoJS but it hasn't been updated in two years. Is this still the best option for js encryption?
I would go with this one: github.com/brix/crypto-js it is also available via NPM
@stom it is up to you how and where you store it. I don't know if there is truly secure way to store it in a browser. Request them from the server and store in memory.
|
111

I created an insecure but simple text cipher/decipher utility. No dependencies with any external library.

These are the functions:

const cipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const byteHex = n => ("0" + Number(n).toString(16)).substr(-2);
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);

    return text => text.split('')
      .map(textToChars)
      .map(applySaltToChar)
      .map(byteHex)
      .join('');
}
    
const decipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
    return encoded => encoded.match(/.{1,2}/g)
      .map(hex => parseInt(hex, 16))
      .map(applySaltToChar)
      .map(charCode => String.fromCharCode(charCode))
      .join('');
}

// To create a cipher
const myCipher = cipher('mySecretSalt')

//Then cipher any text:
console.log(myCipher('the secret string'))

//To decipher, you need to create a decipher and use it:
const myDecipher = decipher('mySecretSalt')
console.log(myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f"))

12 Comments

let myDecipher = decipher('CartelSystem') - This salt will decipher the string as well. You don't have to know the exact word 'mySecretSalt'
Yet another post where someone is blindly using let. 😒︎
The length of byteHex should be extended to 4 since the maximum charCode is 2^16 - 1(i.e. 65535), which will be ffff when encoded to base 16. Fix: const byteHex = n => ("000" + Number(n).toString(16)).substr(-4); and return encoded => encoded.match(/.{1,4}/g)
Isn't this a) super broken and insecure and b) the 'salt' is in fact your 'secret key' as salts are not expected to be private? I think its very dangerous to post code like this without any comments that this fun-code not intended for any real world use. The amount of upvotes is worrisome. crypto.stackexchange.com/questions/11466/…
Well at least they use sound crypto. What you are doing is basically a Caesar Chipher (applying the same key to every character) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Regarding the other answers... I expect it to be obvious that something called a "secret" is expected to be kept secret (by the user)
|
98

How about CryptoJS?

It's a solid crypto library, with a lot of functionality. It implements hashers, HMAC, PBKDF2 and ciphers. In this case ciphers is what you need. Check out the quick-start quide on the project's homepage.

You could do something like with the AES:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>

<script>
    var encryptedAES = CryptoJS.AES.encrypt("Message", "My Secret Passphrase");
    var decryptedBytes = CryptoJS.AES.decrypt(encryptedAES, "My Secret Passphrase");
    var plaintext = decryptedBytes.toString(CryptoJS.enc.Utf8);
</script>

As for security, at the moment of my writing AES algorithm is thought to be unbroken

Edit :

Seems online URL is down & you can use the downloaded files for encryption from below given link & place the respective files in your root folder of the application.

https://code.google.com/archive/p/crypto-js/downloads

or used other CDN like https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js

4 Comments

What is the difference between rollups and components under folder 3.1.2?
After playing a bit the components are the separated parts. You will need to know which components to take (and in what order) to make it work. The rollups files contain everything you need to make it work with just one script reference (much better as the hard job is already done).
But how can we secure the Secret passphrase ?
@shaijut You don't. You don't even save it anywhere except in RAM when encrypting/decrypting the plaintext. The passphrase should only be stored in the user's brain (or a password manager)
62

This code is based on @Jorgeblom's answer above.


@Jorgeblom my man, that's fantastic small crypto lib :D I touch it a bit because I didn't like that I have to assign the salt and to call it again but in general, for my needs is absolutely perfect.

const crypt = (salt, text) => {
  const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
  const byteHex = (n) => ("0" + Number(n).toString(16)).substr(-2);
  const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);

  return text
    .split("")
    .map(textToChars)
    .map(applySaltToChar)
    .map(byteHex)
    .join("");
};

const decrypt = (salt, encoded) => {
  const textToChars = (text) => text.split("").map((c) => c.charCodeAt(0));
  const applySaltToChar = (code) => textToChars(salt).reduce((a, b) => a ^ b, code);
  return encoded
    .match(/.{1,2}/g)
    .map((hex) => parseInt(hex, 16))
    .map(applySaltToChar)
    .map((charCode) => String.fromCharCode(charCode))
    .join("");
};

And you use it

// encrypting
const encrypted_text = crypt("salt", "Hello"); // -> 426f666665

// decrypting
const decrypted_string = decrypt("salt", "426f666665"); // -> Hello

9 Comments

you did a great job
Thanks, Hamza but as much as I'd like to take credit, @Jorgeblom did the real job :)
What is the equivalent for PHP?
Thanks, it's a simple version that uses string only. Pretty useful :D
First, it is a simple substitution cipher based on whatever 8 bit number comes out of textToChars(salt), which is weak to start with, but if the value of that function happens to be zero then the bytes that come out are the hex ascii values of what you are trying to encrypt. For instance, if you use salt = "ABC123p" then textToChars(salt) = 0 and if you encrypt "the secret string" you get out 74 68 65 20 73 65 63 72 65 74 20 73 74 72 69 6E 67 which is the same as if you typed that phrase into rapidtables.com/convert/number/ascii-to-hex.html
|
31

Modern browsers now support the crypto.subtle API, which provides native encryption and decryption functions (async no less!) using one of these method: AES-CBC, AES-CTR, AES-GCM, or RSA-OAEP.

https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto

3 Comments

Out of the above options, only AES-GCM and RSA-OAEP are reasonable. :(
crypt.subtle AES-GCM, self-contained, tested: async function aesGcmEncrypt(plaintext, password) async function aesGcmDecrypt(ciphertext, password) gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a
31

The existing answers which leverage SJCL, CryptoJS, and/or WebCrypto aren't necessarily wrong but they're not as safe as you might initially suspect. Generally you want to use libsodium. First I'll explain why, then how.

Why Not SJCL, CryptoJS, WebCrypto, etc.?

Short answer: In order for your encryption to actually be secure, these libraries expect you to make too many choices e.g. the block cipher mode (CBC, CTR, GCM; if you can't tell which of the three I just listed is secure to use and under what constraints, you shouldn't be burdened with this sort of choice at all).

Unless your job title is cryptography engineer, the odds are stacked against you implementing it securely.

Why to Avoid CryptoJS?

CryptoJS offers a handful of building blocks and expects you to know how to use them securely. It even defaults to CBC mode (archived).

Why is CBC mode bad?

Read this write-up on AES-CBC vulnerabilities.

Why to Avoid WebCrypto?

WebCrypto is a potluck standard, designed by committee, for purposes that are orthogonal to cryptography engineering. Specifically, WebCrypto was meant to replace Flash, not provide security.

Why to Avoid SJCL?

SJCL's public API and documentation begs users to encrypt data with a human-remembered password. This is rarely, if ever, what you want to do in the real world.

Additionally: Its default PBKDF2 round count is roughly 86 times as small as you want it to be. AES-128-CCM is probably fine.

Out of the three options above, SJCL is the least likely to end in tears. But there are better options available.

Why is Libsodium Better?

You don't need to choose between a menu of cipher modes, hash functions, and other needless options. You'll never risk screwing up your parameters and removing all security from your protocol.

Instead, libsodium just gives you simple options tuned for maximum security and minimalistic APIs.

  • crypto_box() / crypto_box_open() offer authenticated public-key encryption.
    • The algorithm in question combines X25519 (ECDH over Curve25519) and XSalsa20-Poly1305, but you don't need to know (or even care) about that to use it securely
  • crypto_secretbox() / crypto_secretbox_open() offer shared-key authenticated encryption.
    • The algorithm in question is XSalsa20-Poly1305, but you don't need to know/care

Additionally, libsodium has bindings in dozens of popular programming languages, so it's very likely that libsodium will just work when trying to interoperate with another programming stack. Also, libsodium tends to be very fast without sacrificing security.

How to Use Libsodium in JavaScript?

First, you need to decide one thing:

  1. Do you just want to encrypt/decrypt data (and maybe still somehow use the plaintext in database queries securely) and not worry about the details? Or...
  2. Do you need to implement a specific protocol?

If you selected the first option, get CipherSweet.js.

The documentation is available online. EncryptedField is sufficient for most use cases, but the EncryptedRow and EncryptedMultiRows APIs may be easier if you have a lot of distinct fields you want to encrypt.

With CipherSweet, you don't need to even know what a nonce/IV is to use it securely.

Additionally, this handles int/float encryption without leaking facts about the contents through ciphertext size.

Otherwise, you'll want sodium-plus, which is a user-friendly frontend to various libsodium wrappers. Sodium-Plus allows you to write performant, asynchronous, cross-platform code that's easy to audit and reason about.

To install sodium-plus, simply run...

npm install sodium-plus

There is currently no public CDN for browser support. This will change soon. However, you can grab sodium-plus.min.js from the latest Github release if you need it.

const { SodiumPlus } = require('sodium-plus');
let sodium;

(async function () {
    if (!sodium) sodium = await SodiumPlus.auto();
    let plaintext = 'Your message goes here';
    let key = await sodium.crypto_secretbox_keygen();
    let nonce = await sodium.randombytes_buf(24);
    let ciphertext = await sodium.crypto_secretbox(
        plaintext,
        nonce,
        key    
    );
    console.log(ciphertext.toString('hex'));

    let decrypted = await sodium.crypto_secretbox_open(
        ciphertext,
        nonce,
        key
    );

    console.log(decrypted.toString());
})();

The documentation for sodium-plus is available on Github.

If you'd like a step-by-step tutorial, this dev.to article has what you're looking for.

6 Comments

whats your relation to sodium projects?
it would be good to more sustained argument against WebCrypto - it seems you just linked anonymous comment saying WebCrypto was not designed with security in mind
Downvoting because there is zero indication/acknowledgement he is a maintainer (possibly head) of the project on GitHub (bias)...
@MaciejUrbański I contribute to libsodium, and led the initiative to get it into the PHP standard library. wiki.php.net/rfc/libsodium My relation to sodium is as a) a user, b) an independent security expert that specializes in applied cryptography.
Sodium is too much of a black box and it's documentation is awful. It's ok if the decryptor and encryptor are the same entity but for standardised encryption for transmission it's atrocious.
|
12

crypt.subtle AES-GCM, self-contained, tested:

async function aesGcmEncrypt(plaintext, password)

async function aesGcmDecrypt(ciphertext, password) 

https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a

1 Comment

Thanks! Dificult to find simple to use example of using native browser Crypto API. Only restriction is IE11...
9

Before implementying any of this, please see Scott Arciszewski's answer.

I want you to be very careful with what I'm about to share as I have little to no security knowledge (There's a high chance that I'm misusing the API below), so I'd be more than welcome to update this answer with the help of the community.

As @richardtallent mentioned in his answer, there's support for the Web Crypto API, so this example uses the standard. As of this writing, there's a 95.88% of global browser support.

I'm going to be sharing an example using the Web Crypto API

Before we proceed, please note (Quoting from MDN):

This API provides a number of low-level cryptographic primitives. It's very easy to misuse them, and the pitfalls involved can be very subtle.

Even assuming you use the basic cryptographic functions correctly, secure key management and overall security system design are extremely hard to get right and are generally the domain of specialist security experts.

Errors in security system design and implementation can make the security of the system completely ineffective.

If you're not sure you know what you are doing, you probably shouldn't be using this API.

I respect security a lot, and I even bolded additional parts from MDN... You've been warned

Now, to the actual example...


JSFiddle:

Found here: https://jsfiddle.net/superjose/rm4e0gqa/5/

Note:

Note the use of await keywords. Use it inside an async function or use .then() and .catch().

Generate the key:

// https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    const stringToEncrypt = 'https://localhost:3001';
    // https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    // The resultant publicKey will be used to encrypt
    // and the privateKey will be used to decrypt. 
    // Note: This will generate new keys each time, you must store both of them in order for 
    // you to keep encrypting and decrypting.
    //
    // I warn you that storing them in the localStorage may be a bad idea, and it gets out of the scope
    // of this post. 
    const key = await crypto.subtle.generateKey({
      name: 'RSA-OAEP',
      modulusLength: 4096,
      publicExponent:  new Uint8Array([0x01, 0x00, 0x01]),
      hash: {name: 'SHA-512'},
      
    }, true,
    // This depends a lot on the algorithm used
    // Go to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
    // and scroll down to see the table. Since we're using RSA-OAEP we have encrypt and decrypt available
    ['encrypt', 'decrypt']);

    // key will yield a key.publicKey and key.privateKey property.

Encrypt:

    const encryptedUri = await crypto.subtle.encrypt({
      name: 'RSA-OAEP'
    }, key.publicKey, stringToArrayBuffer(stringToEncrypt))
    
    console.log('The encrypted string is', encryptedUri);


Decrypt

   const msg = await  crypto.subtle.decrypt({
      name: 'RSA-OAEP',
    }, key.privateKey, encryptedUri);
    console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)

Converting ArrayBuffer back and forth from String (Done in TypeScript):

  private arrayBufferToString(buff: ArrayBuffer) {
    return String.fromCharCode.apply(null, new Uint16Array(buff) as unknown as number[]);
  }

  private stringToArrayBuffer(str: string) {
    const buff = new ArrayBuffer(str.length*2) // Because there are 2 bytes for each char.
    const buffView = new Uint16Array(buff);
    for(let i = 0, strLen = str.length; i < strLen; i++) {
      buffView[i] = str.charCodeAt(i);
    }
    return buff;
  }

You can find more examples here (I'm not the owner): // https://github.com/diafygi/webcrypto-examples

Comments

7

you can use those function it's so easy the First one for encryption so you just call the function and send the text you wanna encrypt it and take the result from encryptWithAES function and send it to decrypt Function like this:

const CryptoJS = require("crypto-js");


   //The Function Below To Encrypt Text
   const encryptWithAES = (text) => {
      const passphrase = "My Secret Passphrase";
      return CryptoJS.AES.encrypt(text, passphrase).toString();
    };
    //The Function Below To Decrypt Text
    const decryptWithAES = (ciphertext) => {
      const passphrase = "My Secret Passphrase";
      const bytes = CryptoJS.AES.decrypt(ciphertext, passphrase);
      const originalText = bytes.toString(CryptoJS.enc.Utf8);
      return originalText;
    };

  let encryptText = encryptWithAES("YAZAN"); 
  //EncryptedText==>  //U2FsdGVkX19GgWeS66m0xxRUVxfpI60uVkWRedyU15I= 

  let decryptText = decryptWithAES(encryptText);
  //decryptText==>  //YAZAN 

1 Comment

It's working properly but only once. If I again encrypt same data with same salt it will give another encrypted string why?
6

UPDATE December 2021

use crypto api provided by MDN Web Docs:

https://developer.mozilla.org/en-US/docs/Web/API/Crypto


Old answer

Use SimpleCrypto

Using encrypt() and decrypt()

To use SimpleCrypto, first create a SimpleCrypto instance with a secret key (password). Secret key parameter MUST be defined when creating a SimpleCrypto instance.

To encrypt and decrypt data, simply use encrypt() and decrypt() function from an instance. This will use AES-CBC encryption algorithm.

var _secretKey = "some-unique-key";
 
var simpleCrypto = new SimpleCrypto(_secretKey);
 
var plainText = "Hello World!";
var chiperText = simpleCrypto.encrypt(plainText);
console.log("Encryption process...");
console.log("Plain Text    : " + plainText);
console.log("Cipher Text   : " + cipherText);
var decipherText = simpleCrypto.decrypt(cipherText);
console.log("... and then decryption...");
console.log("Decipher Text : " + decipherText);
console.log("... done.");

2 Comments

SimpleCrypto uses unauthenticated AES-CBC and is therefore vulnerable to chosen-ciphertext attacks.
I struggled to find that nowadays we have something like window.crypto natively. Thanks!
4

CryptoJS is no longer supported. If you want to continue using it, you may switch to this url:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

2 Comments

What is the difference between rollups and components under folder 3.1.2?
Crypto is recommending the forge library when you enter their site.
1

Important note regarding @Jorgeblom's algo:

BE WARNED!!! The text with @Jorgeblom is NOT "encrypted", but only "masked" due to the same pattern. Please do not be fooled by knotted script wordings and schematic shorthand notations! This does not necessarily make the algo more complex, nor make it more effective or faster - but quite the opposite: Separately declared variable-functions require unnecessary resources and additional management overhead and are often only intended to impress the layman - ridiculous!

If you break down the "arrow notation" into its components, you will find chained "split" and "merge" actions over several nested arrays, which also lead to unnecessary costs and delays in the program flow due to their constant rearrangements. At the end the core algorithm for encryption (that's the main topic here) only consists of a primitive XOR operation - nothing more!

The monstrous fuss can be reconstructed with two simple "for-next" loops, as you can see below - and you'll recognize all the nonsense immediately:

function crypt(salt, text) {
    // First, we assign a virgin output string:
    var output = "";
    // Then, we walk letter-wise through the given original text:
    for(var i = 0; i < text.length; i++){
        var val = text.charCodeAt(i);
        // Now we manipulate each letter value with a sum of "salt" XOR's.
        // This is absolutely stupid, because it repeats for every main-loop
        // the same sub-loop. So it gives each time also an identical XOR-sum
        // and retards the entire iteration extremely - just for nothing:
        for(var k = 0; k < salt.length; k++){
            val = val^(salt.charCodeAt(k));
        }
        // Append each XOR-result as 2-digit HEX-value to the output string:
        output += (val < 16 ? '0' : "") + val.toString(16);
    }
    // Return that very poor encrypted (let's say "masked") output:
    return output;
}

... surprisingly poor, isn't it? The second (nested) "for-next" loop is pointless because it always returns the same pattern and therefore always codes the same characters in the same way. This is why we can only speak of "masking" instead of "encrypting". A simple pattern comparison of the ciphers reveals frequently occurring identical letters on the spot.

As the matching XOR value does not change anyway during the entire sequence, the task can be solved very conveniently with my following, little helper function:

function hackThis(crypt) {
    // Try the max. amount of 256 XOR values (0 - 255) to decode the cipher.
    // The result are 256 text blocks and you have to check each (256 items are
    // manageable). I ASSURE: One of them is READABLE AND CORRECT DECRYPTED!!!
    for(var Xval = 0; Xval < 256; Xval++){
        // Convert each to an ASCI-Char. to be digestible for the "decrypt" algo:
        var Xstr = String.fromCharCode(Xval);
        // Call the decrypt algo with the questionable character as a "salt":
        var output = decrypt(Xstr, crypt);
        // List the test result on the screen:
        document.writeln('Nr.'+('00'+Xval).slice(-3)+': ' + output + '<br>');
    }
}

... so easy! This "hack" works with any previously used password (no matter how complex), and is the doubt free evidence of the uselessness of the crypt algo described above.

However - please let me being constructive:

First, I use the code of each already encrypted character from the original text as an encryption basis for the next followed character. This method (see below) creates a "self-encryption" that is hard to evaluate from the outside.

The initial encryption is performed by using a "pseudo hash" of the password. I call it "pseudo" because only values from 0 to 255 can be considered due to the ASCII compatibility of the XOR results. This limitation causes another problem, which is addressed in the 2nd example further below. For a start, please take a look at my first example "selfCryptBasics":

function selfCryptBasics(orgtext, passphrase) {

    // First, assign some initial variables:
    var value, pseudoHash = 0, preVal = 0, orgLen = orgtext.length, output = "";

    // Force the "string" type of a possible numeric passphrase (assignment
    // of a different variable name is essential for a successful conversion):
    var cryptKey = String(passphrase);
    var cryptKeyLen = cryptKey.length;

    // Create a "pseudo hash" of the given crypt key (= pass phrase):
    for(var i = 0; i < cryptKeyLen; i++) {
        pseudoHash = pseudoHash^(cryptKey.charCodeAt(i));
    }

    // Walk letter-wise through the org. text and get their ASCI value:
    for(var i = 0; i < orgLen; i++){
        var value = orgtext.charCodeAt(i);

        // Double encrypt the current letter with 2 nested XOR operations.
        // The first XOR operator uses the "pseudo hash" and the second
        // the code value of the previously encrypted letter:
        value = (value^pseudoHash)^preVal;

        // Store the current code value temporarily for the next loop:
        preVal = value;

        // Append the XOR-result as 2-digit HEX to the output string:
        output += (value < 16 ? '0' : "") + value.toString(16);
    }

    // Return the encrypted output:
    return output;
}

You may try this example with the original text "stresslessness" and look at the code. There is 7 times the letter "s" and 3 times the letter "e" encoded but its hard to find equal ciphers of them - quite good, eh?!

But we have still that poor encryption problem via XOR values which can be easily evaluated with my "hack" script" above!!

I insist on XOR encryption because it is uncomplicated and extremely fast. Generating "real" hash codes with dozens of digits requires complex calculations and correspondingly expensive processing. However, if the original password is chopped up into pieces and "XOR'ed" individually, the sum of all the little keys results also in a nearly unique hash - so I did in my 3rd (and final) example:


function selfCrypt(orgtext, passphrase) {

    var value, preVal, cutPos = 0, tmpHash = 0, output = "";
    var orgLen = orgtext.length, cryptKey = String(passphrase);
    var cryptKeyLen = cryptKey.length, pseudoHash = new Array(cryptKeyLen);

    // Prevent zero keys:
    if(cryptKeyLen < 1) return orgtext;

    // Create an array of different "pseudo hashes":
    // Therefore truncate the crypt key incrementally from left to right and
    // create a specific hash value for each version. In this way, we hope to
    // get significantly different values from the starting hash at each pass
    // (which is highly probable, except keys like "xxxx" or "0000" are used).
    for(var i = 0; i < cryptKeyLen; i++) {
        for(var k = i; k < cryptKeyLen; k++) {
            tmpHash = tmpHash^(cryptKey.charCodeAt(k));
        }
        // Fill each hash version into a fast accessible array:
        pseudoHash[i] = tmpHash;
        tmpHash = 0;
    }

    // Let the encryption begin!
    // To do this, the variable “cutPos” is used as a pointer for the
    // hash array. We start at the end of the array and jump back
    // step by step after each run. (Note: Index of the last array field
    // is the crypt length -1:
    cryptKeyLen--;
    cutPos = cryptKeyLen;

    // Walk letter-wise through the org. text as usual:
    for(var i = 0; i < orgLen; i++){
        var value = orgtext.charCodeAt(i);

        // Double encrypt like we did in the 1st example, but...
        // ...watch out: Instead one single "pseudo hash" we have
        // now varying values from the hash array on each loop:
        value = (value^pseudoHash[cutPos])^preVal;

        // Store the current code value temporarily for the next loop
        // and set the next lower pointer position. If the zero position
        // is reached, reset the pointer at it's end position:
        preVal = value;
        cutPos--;
        if(cutPos < 0) cutPos = cryptKeyLen;

        // Prepare the output string as usual:
        output += (value < 16 ? '0' : "") + value.toString(16);
    }
    // Return the encrypted output:
    return output;
}

The resulting code is almost impossible to crack, as it provides no external clues to any patterns. The algorithm presented here can be placed openly in any type of document without risk, as it is not the script itself that keeps the key, but only user's password and the self encrypting text!

By the way: Due to its low costs combined with a very high speed, this script can also process long text blocks (and long passwords too)!

...aah - you want also decode all this scrambled stuff??

OK, here we go:

function selfDecrypt(cipher, passphrase) {

    var value, memVal, preVal, cutPos = 0, tmpHash = 0, output = "";
    var cipherLen = cipher.length, cryptKey = String(passphrase);
    var cryptKeyLen = cryptKey.length, pseudoHash = new Array(cryptKeyLen);
    if(cryptKeyLen < 1) return cipher;

    // Generate the "pseudo hash" array:
    for(var i = 0; i < cryptKeyLen; i++) {
        for(var k = i; k < cryptKeyLen; k++) {
            tmpHash = tmpHash^(cryptKey.charCodeAt(k));
        }
        pseudoHash[i] = tmpHash;
        tmpHash = 0;
    }
    cryptKeyLen--;
    cutPos = cryptKeyLen;

    // Walk through the hex cipher in steps of 2 char's:
    for(var i=0; i < cipherLen; i+=2){
        // Get the decimal value of the current 2-digit hex:
        value = (parseInt(cipher.substring(i,i+2),16));

        // Hold this code value temporarily for the next decryption loop:
        memVal = value;

        // Decrypt the code with the double XOR algo:
        value = (value^pseudoHash[cutPos])^preVal;

        // Store the above hold original code for the next loop:
        preVal = memVal;

        // Update the control variables:
        cutPos--;
        if(cutPos < 0) cutPos = cryptKeyLen;

        // Convert the decoded value into a guilty ASCII-character
        // and append it to the output string:
        output += String.fromCharCode(value);
    }

    // Return the plain text:
    return output;
}

... that's it!

Due to its simple structure and the widely known commands, this script can be easily translated into other program languages (e.g. PHP, Phyton or even VBA)!

Have fun and a nice day!

P.S.:

Hash codes always come with a "collision risk", which means two different passwords might accidentally generate the same hash. This very vague risk could also occur with my "pseudo" hashes, but is even less likely, as this method always uses different lengths of the original password. It may be that some sections generate identical hashes, but in their sum as an array chain, this will hardly be the case. Alan Turing's "bomb" would be suitable for detecting such collisions - but who has such a loud, huge thing lying around their basement? ;-)

2nd Edit:

Subsequently, I re-checked my loud-mouthed assertion that an XOR chain can produce an unique hash value. Fact is, that the hit rate with only 256 variations of an XOR chain is a massive 25% - that's really far too much! However, you must bear in mind that the hit probability for each additional position is 25% of the previous hit. With 2 digits, we would therefore only have a hit rate of 6.25% and this rate decreases exponentially with each additional digit. At the 5th digit the probability is already 1 per thousand (not per hundered)! Of course, you can't classify such a probability rate as "highly reliable" - but this is about the practical and not the theoretical benefits. If we stick to 5 digits, the first 5 characters of the ciphertext could therefore be deciphered with a probability of 0.1% in the worst case. This doesn't mean in practice, that the "hacker" can do anything with this information, as he has no clue as to whether this so far decryption (just 5 characters of the original text) is really correct. However, since a password should always be much longer than 5 characters, probability considerations come to an end: From this point onwards, the ciphertext is unmanageably jumbled up with a very low probability rate. As I said before: is about the practical and not the theoretical benefits!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.