-
Notifications
You must be signed in to change notification settings - Fork 0
Secure Key Generation
var onecrypt = require('onecrypt');
var key = onecrypt.gen_key();
var mackey = onecrypt.gen_key();
Notes:
- There is no asynchronous way to call this method. It's intended to be used on an infrequent basis, potentially manually on the command line.
- It produces an appropriate key for the
onecrypt
encipher/decipher methods, which uses aes256. - For use with the
onecrypt
encipher/decipher methods, generating a second key for producing an HMAC is strongly recommended. - If you plan on storing your keys, you must store them securely. All of your encryption is worthless if someone gets your key.
See below for an example with generating a key from a password
Those of us not immersed in security and cryptography think of a key sort of like a password. That's true in the sense that it's a secret that, when you enter it, provides you access to restricted content. With passwords we try to find some compromise between something we think will be hard for someone else to figure out and easy for us to remember(*), and perhaps we assume that we should use the same approach to create a cryptographic key that we use to encrypt secret data. However, cryptographic keys are a somewhat different beast with certain restrictions on how they can be constructed and certain characteristics that they must have.
(*) - Using passwords we can remember is becoming more and more foolhardy. See Password Encryption for more info.
Keys, as used in cryptography, aren't secrets that we use to unlock a door to a secret place like passwords are. Cryptographic keys are more like a disguise we use to transform things we want hidden so that we can hide them in plain sight. If that sounds like a much more tenuous concept than a lock on a door, that's because it is. But we're in luck. Cryptographers have developed ways to use these disguises in such a way that, as long as you create and apply them correctly, it's impossible(*) to figure out what's hiding under that disguise. But be wary. If you don't create the key in the right way, then that's one way you can open up ways to see through the disguise. Womp womp.
(*) - impossible using any mathematical or computational techniques that we currently understand. We don't yet fully understand quantum computers, which could change things significantly.
A common concept in security is that in order to successfully secure something, you need to do everything 100% correctly; an attacker only needs to find one hole to get through.
With that in mind, we've used the following parameters to generate keys with the default options:
- We're using aes256, so generate 256-bit keys. This is pretty much a no-brainer, the key length is what determines which version of AES you're using. If you'd like to use this tool to generate keys for aes128, then you can either lop off half the bits (a technique in use at 1Password - see "2. Tougher key derivation", and fairly safe given the next point) or request fewer, though since you can't perform 128-bit encryption with onecrypt it's perhaps a dubious choice.
- We're using a CSPRNG built into node's crypto library to generate random bytes suitable for cryptographic purposes.
The end result of default key generation is virtually identical to this:
var key = require('crypto').randomBytes(32).toString('base64');
... a couple things to note, just in case this is the first time you've worked with code like this: a) 32 bytes = 256 bits, and b) I base64
encode the result with the intention to make storing the key as straight forward as possible, rather than returning a node Buffer.
A few options you can pass in, and what they do:
var params = {
// byte_length, bit_length are used to adjust the output key length. choose how you'd like to count
// you could use this to generate a double length key and split it for key & mackey
byte_length: 32, // default - null
bit_length: 256, // default - null - ignored if byte_length is set
// requesting raw output will return a node Buffer object instead of an encoded string
raw: true, // default - false
// wraps the call in a try/catch to prevent throwing errors, you'll have to check for null return
safe: true, // default - false
// if you'd like to generate a key from a password that you provide, then you'll need the following options
// details are below
password: 'this is my password', // default: null
// salt should be randomly generated, if this is the first time you're creating your key then leave this blank
salt: 'this should be random', // default: null
iterations: 262144, // default: 262144
// if you want to generate this same key from this password again and are letting the system determine salt and
// iterations, then pass those values back so we can store and use them again
return_params: true // default: false
}
var onecrypt = require('onecrypt');
// if you just want to create and save a key from a password, you can pass a string
var save_this_key = onecrypt.gen_key('one time password');
// otherwise, you'll need to do a little more
var details = onecrypt.gen_key({password: 'Mele Kalikimaka', return_params: true});
var use_and_discard_key = details[0];
var generate_same_key = onecrypt.gen_key({password: 'Mele Kalikimaka', salt: details[1].salt, iterations: details[1].iterations});
var generate_different_key({password: 'Mele Kalikimaka'});
- Onecrypt uses pbkdf2 to derive an appropriate key from your password. There is a built in pbkdf2 function in node (which is both performant and asynchronous), which uses sha1 as the underlying hash. I've ported a pbkdf2 function into pure javascript which allows user selection of the underlying hash, and use sha256 to generate a key from your password in this
gen_key()
function, but it is slo-o-o-o-ow, and I don't suggest using it at this time. If you want to generate a password, I suggest you check out the code forgen_key()
and replace it with a call to the built in pbkdf2 function. - As a side note, sha1 is theoretically just as strong as sha256 when used in pbkdf2, this is mainly to satisfy the "bigger is better" mindset, when in this case bigger might just be bigger. Should a weakness be found in pbkdf2, there's a chance that sha1 hashes might be more susceptible than sha256, but at this point it's all speculation.
- The first time you generate a key from a password you should leave
salt
blank, and if you want to regenerate the same key from your password at a later point, then you'll want to setreturn_params
to true, and save your salt and iterations. - Theoretically it would be stronger to allow the function to generate a key from purely random bytes, rather than to construct it from a password (as a password can be guessed), but practically speaking cryptographically strong key derivation functions should be sufficient for any usage. If your application requires generating (and discarding after use!) a key from a password, there's no particular reason to change that.