Is this kind of encryption "safe"? - security

I must first say I have never studied cryptography, and everything I know on this topic is just basic notions.
We were looking at a fast and easy way to encrypt some data (to be stored into a database) using a password.
I know the "safest" algorithm is AES, but it's probably too complicated for us and I know it requires us to obtain authorizations from the US government, etc.
We thought about this (simple) algorithm, which reminds me (but I may be wrong) a sort of "One time pad".
(it's not written in any specific language... it's just the idea :) )
// The string we need to encrypt
string data = "hello world";
// Long string of random bytes that will be generated the first time we need to encrypt something
string randomData = "aajdfskjefafdsgsdewrbhf";
// The passphrase the user selected
string passphrase = "foo";
// Let's generate the encryption key, using randomData XOR passphrase (repeating this one)
string theKey = "";
j = 0;
for(i = 0; i < randomData.length; i++)
{
theKey += randomData[i] ^ passphrase[j];
j++;
if(j == passphrase.length) j = 0;
}
// Encrypt the data, using data XOR theKey (with theKey.length >= data.length)
string encryptedData = "";
for(i = 0; i < data.length; i++)
{
encryptedData += data[i] ^ theKey[i];
}
On disk, we will store then only randomData and encryptedData.
passphrase will be asked to the user every time.
How safe will an algorithm like this be?
Except with a brute force, are there other ways this could be cracked? I don't think statistical analysis will work on this, does it?
Is it "as safe as" a One Time Pad?
Thank you!

You can just import an AES library and let it do all the heavy work. Authorizations from the US government? It is a public function, and the US government also uses it.

No, this is not secure.
If the random data is stored alongside the encrypted data, then it is simply equivalent to XORing with the passphrase: this is because the attacker can simply XOR the encrypted data with the random data, and obtain plaintext XOR passphrase as the result.

This is extremely weak. Statistical analysis would crack it in the blink of an eye. Some diligent pen-and-paper guesswork would probably crack it pretty quickly too.
The only exception would be if (1) randomData was taken from a truly crypto-strength source, (2) randomData was at least as long as your plaintext data, (3) randomData was never, ever re-used for a different message, and (4) you got rid of passphrase altogether and treated randomData as your key. In that case you'd have what amounts to a one-time pad.

No, it isn't safe. Using xor with random data and password this way is completely wrong.
A one time pad cryptograpy needs the random data to be the same length as the data to be encrypted.

Related

What is the difference between a Non-base 58 character and a invalid checksum private WIF key?

I am just playing around with a NodeJS dependency called CoinKey
Here is the question:
I randomly generate WIF keys with this function:
case 'crc':
let randomChars = 'cbldfganhijkmopqwesztuvxyr0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let generatedPrivateKey = ''
for (let i = 0; i < 50; i++)
generatedPrivateKey += randomChars.charAt(Math.floor(Math.random() * randomChars.length))
return prefix + generatedPrivateKey
I have here 2 real examples:
1 = LSY3MrnXcjnW5QU2ymwZn6UYu412jr577U9AnR9akwTg2va7h9B -> Invalid checksum
2 = L2l37v6EPDN423O02BqV02T2PK1A6vnjBO1HRmgsZ6384LNdSCj -> Non-base58 character
I call the function CoinKey.fromWif(privateKey) with of course one of the 2 private keys above. But why does key 1 give me the error Invalid checksum and key 2 gives me the error Non-base58 character?
I am just a simple developer, I don't have any knowledge about encryption etc. The only thing i know that i try to generate a WIF key, and a WIF key is a shorter encryption of a larger private key. And yes i also know that it's almost as good as impossible to brute-force such a big private key but as i said i am just playing around.
The base 58 character set only contains 58 characters. a-z, A-Z, 0-9 are 62, so four of them are not valid. Case 2 apparently contains one or more of the invalid characters.
And each key has a checksum. So if all the characters are valid the checksum is checked. Looks like your first case has all valid characters, purely by coincidence, but not the correct checksum.
It's pretty nonstandard and a bit inefficient; base 64 is commonly used.

How can I quickly encrypt a 128 bit value into another equal length value in NodeJS 14

My server will be sending many IDs to the browser/user, and, for that session, the user might operate using those IDs. Between users, and between multiple sessions of a single user, I need IDs to be encrypted, so they cannot be traced except within the context of a single session. Due to the number of IDs that will be used in each session, it is not reasonable to dereference or hash them and store lookup tables for each session.
The IDs are effectively UUIDs, unique 128 bit values. The server will encrypt them in the context of a session, and when the user queries using them, and the server can decrypt those values within the context of that same session. I would like for the encrypted output to also be 128 bits in length (for example, so they could be rendered as UUIDs even in their encrypted state). What is the best way for me to achieve this?
This is my sample code, demonstrating that I can encrypt 16 bytes (the size of a block), but the cipher extends it to 2 blocks, doubling the size to 32 bytes, when I finalize it. I think because it is OK for a value to be encrypted the same way twice in the context of a single session, it is acceptable to reuse the same IV for each item; so the server stores a key and IV for the session, and can encrypt and decrypt all of the IDs with those.
async function sampleCrypt() {
const algorithm = 'aes-128-cbc';
crypto.scrypt("samplePassword", "salty", 16, (err, key) => {
const iv: Buffer = crypto.randomBytes(16);
const cipher: crypto.Cipher = crypto.createCipheriv(algorithm, key, iv);
const inbuffer = Buffer.allocUnsafe(16);
inbuffer.writeUInt32BE(1960); // just some sample data
console.log(cipher.update(inbuffer, undefined, 'hex')); // loads the whole buffer in
console.log(cipher.final('hex'));
});
}
sampleCrypt();
/* Sample output:
83134f7dc2f9b175bd70a7dd0512eaf7
9495e0cfceab0439fddc92f3fffa48c2
*/
Please advise if I have made any incorrect assumptions here as well. Thanks!
Block ciphers such as AES require plaintexts whose lengths are an integer multiple of the blocksize (16 bytes for AES). If this isn't the case, padding must be used. NodeJS applies PKCS7 padding by default. Here a complete padding block is appended if the plaintext length is already an integer multiple of the blocksize. This is the reason why in your case a 16 bytes plaintext results in a 32 bytes ciphertext. But since the plaintext is always exactly one block long, there is actually no need for padding. In NodeJS the padding can be disabled with cipher.setAutoPadding(false), so in your case plaintext and ciphertext are both 16 bytes long.
A block cipher only encrypts one block. To encrypt longer plaintexts, an operation mode must be used, e.g. CBC as in the posted code. Generally, these operation modes use an initialization vector (IV) whose size is equal to the blocksize (16 bytes for AES). The IV must meet certain conditions, e.g. a key/IV pair may only be used once. Since the IV isn't secret it's usually placed before the ciphertext. In your case, this would result in a 32 bytes result (IV + ciphertext). The condition mentioned also means that the concept you use (one key/IV pair for all encryptions) is inherently insecure.
An operating mode that doesn't require an IV is ECB. ECB generates the same ciphertext for the same plaintext, which generallay allows conclusions from the ciphertext to the plaintext. This problem doesn't exist for a mode with an IV. Therefore ECB is more insecure compared to a mode with an IV. However, the severity of this insecurity ultimately depends on the characteristics of the plaintext and the particular application, and the respective requirements determine whether this disadvantage is tolerable or not. 1-block plaintexts containing a GUID are less vulnerable in this respect than multi-block plaintexts with some message content, so ECB may be an option here.
With disabled padding and ECB mode a 16 bytes plaintext results in a 16 bytes ciphertext, as the following TypeScript code demonstrates:
import * as crypto from "crypto";
const algorithm:string = 'aes-128-ecb';
const key:Buffer = crypto.randomBytes(16);
// Encryption
const plaintextEnc:Buffer = Buffer.from('0123456789012345');
const cipherEnc:crypto.Cipher = crypto.createCipheriv(algorithm, key, null);
cipherEnc.setAutoPadding(false);
const ciphertext:Buffer = Buffer.concat([cipherEnc.update(plaintextEnc), cipherEnc.final()]);
console.log(ciphertext.toString('hex'));
// Decryption
const cipherDec:crypto.Decipher = crypto.createDecipheriv(algorithm, key, null);
cipherDec.setAutoPadding(false);
const plaintextDec:Buffer = Buffer.concat([cipherDec.update(ciphertext), cipherDec.final()]);
console.log(plaintextDec.toString('hex'));
If the limitation to 16 bytes is dropped, GCM would be a recommendable mode that provides besides confidentiality also authenticity and integrity. GCM uses a 12 bytes IV (nonce) and generates a tag (typically 16 bytes) that is used for authentication. In your case, the result (IV + ciphertext + tag) would have a length of 44 bytes. Note that if a key/IV pair is used more than once for GCM, security is lost.

Ocaml-What is the most efficient way to calculate hash values for all substrings in a string?

What is the most efficient way to obtain hash values for all substrings in a string. I tried to use:
let str1 = "AHTG...";;(*1000000 chars*)
let tam = 2;;
for i = 0 to String.length str1 - tam do
let st = String.sub str1 i tam in
Hashtbl.add hash_table (Hashtbl.hash st) i;
done;
to calculate all substrings with size =2 (AC,CH,TA,...) of a string with size = 1000000 and add values to hash_table but it takes a lot of time to finish the process,i think. I was wondering if there is any process more efficient and faster than the one presented above?
First of all, there are a lot of substrings of a string, around n^2/2 of them I would say. This is a big number when n = 1e6. If your hash function is a black box with no known arithmetic properties, and your string also has no known extra properties, you basically have to do O(n^2) calls to your hash function, which will take a long time.
If your hash function has interesting arithmetic properties, like say hash(a ^ b) = hash(a) + hash(b) mod K, you might be able to do a little better. On the other hand, properties like this probably make a weaker hash.
As an immediate improvement, you might consider a hash function that works directly on a substring. That will save you a lot of calls to String.sub and the associated consing and GC. (Probably this won't help a lot as OCaml has a really good GC for short-lived values.)

CryptoJS with hex key not decrypting properly

I have a dataset that was encrypted that I'm trying to decrypt with CryptoJS. I've tried various combination of things but for some reason the result is not what I'm expecting. I have provided the key below and also the text I want to decrypt. What I'm expecting at msg1 is 32 characters long but I keep getting 48. Its as of its padding it with an extra 16 characters.
Thanks in advance for the help.
key = 'd13484fc2f28fd0426ffd201bbd2fe6ac213542d28a7ca421f17adc0cf234381';
text = '8bf3955488af91feb7bd87220910cee0';
decrypt(text: string): void{
let msg1 = CryptoJS.AES.decrypt(text, CryptoJS.enc.Hex.parse(key), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding});
msg1 = CryptoJS.enc.Hex.stringify(msg1 );
}
Solving it is pretty simple, but reading the docs and the code, I'm not quite clear why.
This is clearly wrong:
let msg1 = CryptoJS.AES.decrypt(text, CryptoJS.enc.Hex.parse(key), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding});
Given your description, you are expecting the byte sequence represented by the hex digits "8bf3955488af91feb7bd87220910cee0" to be the body. But that's not what your passing. You're passing the characters. So when it decrypts it, the first byte is the ASCII value of 8 (0x38), not 0x8b. Given that, you should be parsing the hex like this:
let msg1 = CryptoJS.AES.decrypt(CryptoJS.enc.Hex.parse(text), ...
But, for reasons I'm having trouble understanding, that doesn't work. decrypt expects Base64 (or at least it will accept Base64). I can't find any documentation that says this, and the code creates the decrypt function magically in a way that I don't fully understand, and this is why I really hate doing crypto work in JavaScript.
That's out of my system now, so let's get to the answer:
cipher = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(text))
let msg1 = CryptoJS.AES.decrypt(cipher, CryptoJS.enc.Hex.parse(key), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding});
And that should give you the result you're expecting.
Given:
key = 'd13484fc2f28fd0426ffd201bbd2fe6ac213542d28a7ca421f17adc0cf234381';
text = '8bf3955488af91feb7bd87220910cee0';.
Decrypting the text with the key actually produces: C5640000B550000079320000217C0000.
See AES Calc
Verify the encoding that CryptoJS.AES.decrypt requires for it's inputs and output encoding.

Node.js crypto key and iv to match java SecretKeySpec / IvParameterSpec

I'm trying to to port a Java (simple) encryption algorythm to Node JS. I will need to be able to decrypt/encrypt stuff encrypted/decrypted from the Java side.
I'm stuck at the very beginning, the initialization of the cipher.
In Java, I get the key with SecretKeySpec, and the Initialization Vector with IvParameterSpec:
public CryptStuff(String password) throws zillion_exceptions {
if (password==null) throw new InvalidKeyException("No encryption password is set!");
key = new SecretKeySpec(password.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ivSpec=new IvParameterSpec(new byte[cipher.getBlockSize()]);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
}
NodeJS requires a Key Buffer and an IV buffer, however, I don't know how to calculate them from scratch:
var mCrypto = require('crypto'),
key=[0,0,0,0,0,0,.......],
iv=[0,0,0,0,0,.........];
function init (password) {
// generate key from password
// generate IV from blocksize?
var aesCipher = mCrypto.createCipheriv("aes-????????", (new Buffer(key)), (new Buffer(iv)));
.
.
.
}
Also, what's the matching algorithm string for AES/CBC/PKCS5Padding?
Assuming you have the same password string as in the Java code, you can create a key buffer like this in node:
var key = new Buffer(password, "utf8");
Since you're using a zero filled IV (bad!) in Java, this is the equivalent code in node:
var iv = new Buffer(16); // 16 byte buffer with random data
iv.fill(0); // fill with zeros
Since you're using CBC mode in Java, you have to do the same in node. Note that you have to select the correct key size when selecting the cipher string depending on your "password" length:
var aesCipher = mCrypto.createCipheriv("aes-128-cbc", key, iv);
// or
var aesCipher = mCrypto.createCipheriv("aes-192-cbc", key, iv);
// or
var aesCipher = mCrypto.createCipheriv("aes-256-cbc", key, iv);
Node will automatically apply PKCS#7 padding which is the same as PKCS#5 padding for AES.
A password is not a key!
A password has usually not the appropriate length to be used as a key (valid lengths are 16 byte, 24 byte and 32 byte for AES) and it is comprised of only printable characters which might make it easier for an attacker to brute force the key.
What you would need to create a key from a password is key derivation function. Popular ones are PBKDF2, bcrypt and scrypt (with increasing cost).
Random IV!
You really should be generating a new random IV for every ciphertext that you produce. If you use a static IV, an attacker that observes your ciphertexts can determine that you sent the same or even similar messages. If you use a random IV, then the ciphertexts differ so much that an attacker cannot determine whether two different ciphertexts where created from the same plaintext or not. This is called semantic security.
The random IV itself doesn't have to be secret, so you can easily prepend it to the ciphertext and slice it off before decryption.
You can even combine this with the key derivation function (KDF). Simply generate a random salt for the KDF. A KDF is usually able to derive a variable amount of output bytes, so simply let it derive key || IV (concatenation) and then split them. Now, you only need to prepend the salt to the ciphertext.
Authentication!
Depending on your system, you might be vulnerable to attacks such as the padding oracle attack. The best defense against this is to authenticate the ciphertext. So you can either use an encrypt-then-MAC scheme with a strong MAC such as HMAC-SHA256 or an authenticated mode of operation such as GCM or EAX. Java and node both support GCM, but there is a little more work involved.

Resources