In order to deterministically generate a new secp256k1 key from existing key, is it safe to just take the sha256 result from old key and use it as entropy to generate a new secp256k1 key? Using this method, I can get the derived key as long as I have oldPrivKeyHex.
const ec = new EC('secp256k1');
const keyHash = shajs('sha256')
.update(oldPrivKeyHex)
.digest();
var newKey = ec.genKeyPair({ entropy: keyHash });
lib src:
https://github.com/indutny/elliptic
It is possible, but I would do it differently. I'd use the hash over the other key (or even better, the result of a key derivation function or KDF) and then directly use it for the fromPrivate method or to use the KeyPair constructor with the private key as parameter.
The reason is that the random number generator hasn't been precisely specified. If anything happens with the handling of the random number generation then you might suddenly get a different key.
The KDF or hash method will already make sure that the entropy is compressed, so the random number generator isn't required anymore. I'd of course rather use SHA-512 or SHA-256 (in that order) than a hash algorithm that provides less than 256 bits.
Related
I have a file that has been encrypted using openssl using the following command:
openssl enc -in data -out encrypted -e -aes256 -k myverystrongpassword
Where data is the original file and encrypted is the encrypted file.
I tried various ways using crypto library but nothing seems to work. I understand that the password needs to be converted into a key so maybe I am doing something wrong there. Looked all over for a solution but nothing seems to work.
The posted OpenSSL statement uses a key derivation function EVP_BytesToKey() to derive a 32 bytes key and a 16 bytes IV from the password in combination with a random 8 bytes salt.
The ciphertext corresponds to the concatenation of the ASCII encoding of Salted__, followed by the salt and finally by the actual ciphertext.
As you already know according to your comment, EVP_BytesToKey() uses a digest for which OpenSSL applied MD5 by default in earlier versions and SHA-256 as of version v1.1.0 (the default value can be overridden in the OpenSSL statement with the -md option).
Decryption is possible e.g. with CryptoJS: Due to its OpenSSL compatibility (s. sec. Interoperability) CryptoJS has a built-in implementation of an accessible EVP_BytesToKey() function and additionally allows to explicitly set the digest in the internal EVP_BytesToKey() call during key derivation. This makes it possible to decrypt encryptions that used SHA-256 or MD5 in key derivation.
The following data is the Base64 encoding of a ciphertext generated with the posted OpenSSL statement. The plaintext used was The quick brown fox jumps over the lazy dog. The OpenSSL version applied is v1.1.1i (i.e. SHA-256 is implicitly used in the key derivation):
U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==
This ciphertext can be successfully decrypted using the following CryptoJS implementation:
const password = 'myverystrongpassword';
const saltCiphertextB64 = 'U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==';
CryptoJS.algo.EvpKDF.cfg.hasher = CryptoJS.algo.SHA256.create(); // default: MD5
const decryptedData = CryptoJS.AES.decrypt(saltCiphertextB64, password);
console.log(decryptedData.toString(CryptoJS.enc.Utf8)); // The quick brown fox jumps over the lazy dog
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Note that the digest in the code must be explicitly specified as SHA-256 since OpenSSL v1.1.1i was used for encryption.
If the encryption was done with an OpenSSL version that uses MD5, the digest in the code must be modified accordingly.
Edit: As noted in the comment, the crypto functions createCipher()/createDecipher() also use EVP_BytesToKey() as key derivation.
However, the following should be noted:
Unlike CryptoJS, it is not possible to specify the digest, i.e. MD5 is used unchangeably. Thus, encryptions that applied SHA-256 for key derivation cannot be decrypted (what applies to the encryptions here).
In contrast to CryptoJS, no salt is used by default. Therefore, salt creation and concatenation (Salted__|<salt>|<cipherext>) during encryption and separation during decryption would have to be implemented additionally. createCipher()/createDecipher() then has to be passed the concatenation of passphrase and salt.
Both functions are deprecated since version 10.0.0 and should actually not be used.
A more robust approach to decrypt encryptions (with arbitrary digests in key derivation) using the crypto module is to apply createCipheriv()/createDecipheriv() and a port of the required functionality of EVP_BytesToKey() to derive key and IV (various implementations can be found on the net).
Security: EVP_BytesToKey() is deemed to be a vulnerability these days. This is worsened by a low iteration count (like 1, which is used by OpenSSL), a broken digest (like MD5) or a missing salt (as is the default for crypto). Ultimately, this is why createCipher()/createDecipher() are deprecated. Instead of EVP_BytesToKey(), a more reliable key derivation function such as PBKDF2 or the more modern scrypt or Argon2 should be used.
I am trying to implement a client side method to encrypt data in transit, and the key will be generated from client side with node.js
Right now, i am stuck in a situation that, it seems only RSA key can satisfy the asymmetric encryption for my use case, but the key can only be generated randomly but not deterministically. However, I want the client to be able to regenerate the key from a seed (e.g. a long hash) every time they log on the system, and the key must not be stored on a server.
Is that any workaround or other encryption method that suits my use case? Did some research and came across the npm package below, which said to be able to go the above with RSA.
is there some potential risk for the implementation?
As I pointed out in another answer, you can use node-forge to create a "rigged" prng that consistently returns an appropriately formatted seed rather than a pseudo-random value.
Code could look like the following, grabbing user's input and generating a long hash from it:
import forge from "node-forge"
const userInput = 'The quick brown fox jumps over the lazy dog'
const md = forge.md.sha256.create();
md.update(userInput);
const seed = md.digest().toHex());
console.log(seed) // output: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
Then creating the PRNG and generating the keys deterministically:
// "Rigged" PRNG
const prng = forge.random.createInstance()
prng.seedFileSync = () => seed
// Deterministic key generation
const { privateKey, publicKey } = forge.pki.rsa.generateKeyPair({ bits: 4096, prng })
I need to have a limit in the output length of my cipher. I know it depends on which algorithm you use, but I didnt find how do do this.
const crypto = require('crypto');
const options = {secret : 'SECRET', algorithm : 'CAST-cbc'};
exports.encrypt = (url) => {
const encrypt = crypto.createCipher(options.algorithm, options.secret);
let cipher = encrypt.update(url, 'utf8', 'hex');
cipher += encrypt.final('hex');
return cipher;
};
This is how im generating the cipher. Thanks.
limit in the output length of my cipher
...
'm trying to build a url shortener without having to have a database behind it
Encryption will be always longer than the original plaintext. You will need to have some IV (initialization vector), encrypted information, optionally an authentication code and then all encoded into an url safe format.
In theory you may sacrifice some level of security and have some sort of format-preserving encryption, but it will be at least as long as the original source.
final hash example: 1d6ca5252ff0e6fe1fa8477f6e8359b4
You won't be able to reconstruct original value from a hash. That's why for serious url shortening service you will need a database with key-value pair, where the key can be id or hash
You may still properly encrypt and encode the original data, just the output won't be shorter.
The output depends on input, always. What you could do is pad the input or output to the desired length to generate a consistent final hash. This requires there be a maximum length to the input, however.
Beyond that, there is no way to force a length and still have something decryptable. If you simply need a hash for validation, that's a bit different. If you clarify the purpose we can help more.
Given the updates that you need a hash, not encryption, then here is how this works.
You hash the original URL and save a file to disk (or wherever, S3). That file has the name that corresponds to the hash. So if the URL is 'http://google.com' the hash may be 'C7B920F57E553DF2BB68272F61570210' (MD5 hash), so you would save 'C7B920F57E553DF2BB68272F61570210'.txt or something on the server with 'http://google.com' as the file contents.
Now when someone goes to http://yourURLShortnener.com/C7B920F57E553DF2BB68272F61570210 you just look on disk for that file and load the contents, then issue a redirect for that URL.
Obviously you'd prefer a shorter hash, but you can just take a 10 digit substring from the original hash. It increases chances of hash collisions, but that's just the risk you take.
I want to implement password reset in my Node.Js app using a very good advice from here https://stackoverflow.com/a/27580553/712347 where I would not have to record any tokens into my database.
Instead #airtonix suggested to use a hash function based on the user's login, email, password, timestamp, secret and salt.
What I don't understand is how do hash functions actually work — let's say I get a certain sequence from the data above — what would be the algorithm (and the library) to use to check if it was generated from the same data using a different salt?
Or am I misunderstanding the whole thing?
How do Hash functions generally actually work -
Hash Algorithms create a digital fingerprint of data usually called Digest or Hash. You primarily see Hash Algorithms used for comparison purposes rather than encryption.
Secure Hash Algorithms have some fundamental characteristics such as:
Non-reversible (one way function). You cannot determine the original set of data from the Digest.
The Digest will be a fixed size regardless of the original data's size.
Unique. Two different data sets cannot produce the same Digest.
What would be the algorithm and the library to use?
I would recommend SHA-2 (SHA-256 or SHA-512) as the hashing algorithm and utilize the Crypto module. It provides cryptographic functionality and a set of wrappers for OpenSSL's hash, HMAC, cipher, decipher, sign, and verify functions.
So lets say we have the following information (user.id, user.email, user.password, timestamp), concatenate it and pass it as the data parameter.
const hash = function hash(data){
// Returns a buffer containing raw bytes and converts to string
const salt = crypto.randomBytes(128).toString('base64')
// Creates and returns a Hmac object that uses the given algorithm
const hmac = crypto.createHmac('sha512', salt)
// Updates the Hmac object content with the given data
hmac.update(data)
// Calculates the digest of all of the data passed to be hashed
const digest = hmac.digest('hex')
return {
'salt' : salt,
'digest': digest
}
}
Running the above function with the same data but a different salt would result in a completely different Digest.
Today I was doing some leisurely reading and stumbled upon Section 5.8 (on page 45) of Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography (Revised) (NIST Special Publication 800-56A). I was very confused by this:
An Approved key derivation function
(KDF) shall be used to derive secret
keying material from a shared secret.
The output from a KDF shall only be
used for secret keying material, such
as a symmetric key used for data
encryption or message integrity, a
secret initialization vector, or a
master key that will be used to
generate other keys (possibly using a
different process). Nonsecret keying
material (such as a non-secret
initialization vector) shall not be
generated using the shared secret.
Now I'm no Alan Turing, but I thought that initialization vectors need not be kept secret. Under what circumstances would one want a "secret initialization vector?"
Thomas Pornin says that IVs are public and he seems well-versed in cryptography. Likewise with caf.
An initialization vector needs not be secret (it is not a key) but it needs not be public either (sender and receiver must know it, but it is not necessary that the Queen of England also knows it).
A typical key establishment protocol will result in both involve parties computing a piece of data which they, but only they, both know. With Diffie-Hellman (or any Elliptic Curve variant thereof), the said shared piece of data has a fixed length and they have no control over its value (they just both get the same seemingly random sequence of bits). In order to use that shared secret for symmetric encryption, they must derive that shared data into a sequence of bits of the appropriate length for whatever symmetric encryption algorithm they are about to use.
In a protocol in which you use a key establishment algorithm to obtain a shared secret between the sender and the receiver, and will use that secret to symmetrically encrypt a message (possibly a very long streamed message), it is possible to use the KDF to produce the key and the IV in one go. This is how it goes in, for instance, SSL: from the shared secret (called "pre-master secret" in the SSL spec) is computed a big block of derived secret data, which is then split into symmetric keys and initialization vectors for both directions of encryption. You could do otherwise, and, for instance, generate random IV and send them along with the encrypted data, instead of using an IV obtained through the KDF (that's how it goes in recent versions of TLS, the successor to SSL). Both strategies are equally valid (TLS uses external random IV because they want a fresh random IV for each "record" -- a packet of data within a TLS connection -- which is why using the KDF was not deemed appropriate anymore).
Well, consider that if two parties have the same cryptographic function, but don't have the same IV, they won't get the same results. So then, it seems like the proposal there is that the two parties get the same shared secret, and each generate, deterministically, an IV (that will be the same) and then they can communicate. That's just how I read it; but I've not actually read the document, and I'm not completely sure that my description is accurate; but it's how I'd start investigating.
IV is public or private, it doesn't matter
let's consider IV is known to attacker, now by looking at encrypted packet/data,
and knowledge of IV and no knowledge on encryption key, can he/she can guess about input data ? (think for a while)
let's go slightly backwards, let's say there is no IV in used in encryption
AES (input, K)= E1
Same input will always produce the same encrypted text.
Attacker can guess Key "K" by looking at encrypted text and some prior knowledge of input data(i.e. initial exchange of some protocols)
So, here is what IV helps. its added with input value , your encrypted text changes even for same input data.
i.e. AES (input, IV, K)= E1
Hence, attacker sees encrypted packets are different (even with same input data) and can't guess easily. (even having IV knowledge)
The starting value of the counter in CTR mode encryption can be thought of as an IV. If you make it secret, you end up with some amount of added security over the security granted by the key length of the cipher you're using. How much extra is hard to say, but not knowing it does increase the work required to figure out how to decrypt a given message.