Deterministically generate public/private key from a seed for asymmetric encryption - node.js

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 })

Related

Understanding Diffie-Hellman with NodeJS

// node.js 0.5 Diffie-Hellman example
var crypto = require("crypto");
// the prime is shared by everyone
var server = crypto.createDiffieHellman(512);
var prime = server.getPrime();
// sharing secret key on a pair
var alice = crypto.createDiffieHellman(prime);
alice.generateKeys();
var alicePub = alice.getPublicKey();
var bob = crypto.createDiffieHellman(prime);
bob.generateKeys();
var bobPub = bob.getPublicKey();
var bobAliceSecret = bob.computeSecret(alicePub);
var aliceBobSecret = alice.computeSecret(bobPub);
I am trying to understand how to use the NodeJS crypto library for a diffie-hellman implementation, and got the above code to compute a shared secret. The problem is both Alice and Bob generate their keys after getting the shared prime. I need them to generate their respective key pairs without having to use any shared information, later than can use shared information to compute the shared secret. I can't get to see how that can be done using the NodeJS crypto library.
I see your confusion.
The Diffie-Hellman prime represents some group of numbers (cyclic group) that you perform the DH function inside, however it's not randomly generated for each person.
Check out my answer here.
The prime/group is known prior to key generation and static.
There are only few groups used, see here for more.
To be clear, in order to generate the same keys, you just need to make sure both ppl are operating inside the same group with the same DH params.

How to perform password reset in Node.Js app using hash?

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.

Key derivation for secp256k1

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.

NodeJS Crypto createCipher('aes128') equivalent in browser?

server code:
cipher = createCipher('aes128', 'password');
str = cipher.update('message', 'utf8', 'base64');
str += cipher.final('base64')
I want the client code (the browser) have the same algorithm as above, given the same message and password, produce the same output as the server's.
I tried CryptoJS, SJCL and some other libraries, but they use iv and salt which makes the result entirely different. In my situation, that security isn't necessary.
(I don't know exactly what iv and salt is, I just hope the code can function without them.)
UPDATE: I find that without proper knowledge of the encryption itself, using the function in this way is a huge mistake.
Per doc:
password is used to derive key and IV, which must be a 'binary'
encoded string or a buffer.
I'm going to learn some basics first.

Secret vs. Non-secret Initialization Vector

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.

Resources