I'd change this to:
> Is your private key being stolen? Read the code!
With ssh keys, at least we can assume that if someone has a github account they have a private ssh key, and it is accessible through the github api. With pgp there isn't a guarantee that they even have a pgp key, and accessibility is on the users themselves to publish it in some way. I think that keybase.io has tried to become the go-to spot for pgp keys, but the adoption is nowhere near what github has, and again, someone has to be interested in privacy/security to want to do this as well.
I mean with all do respect that you are correct in terms of a better protocol, and that there are tools that exist that already do this. The concern that I think OP and myself are interested in solving is creating something that is quick, easy, and piggie-backs on top of the huge github userbase and provides a base level of encryption.
I see absolutely no win here.
function encrypt(public_key, file) {
var pem_pub_key = sshKeyToPEM(public_key); // convert rsa to pem
var chunks = [];
var buffer = new Buffer(fs.readFileSync(file, 'utf8'));
// work around for 214 character limit for encrypting
// text with small openssh rsa pub key
for (var i = 0; i <= (buffer.length / 214); i++) {
chunks.push(buffer.slice(i * 214, (i * 214) + 214));
}
chunks.forEach(function(chunk) {
var encrypted = crypto.publicEncrypt(pem_pub_key, new Buffer(chunk));
console.log(encrypted.toString('base64'));
});
}
According to the docs, crypto.publicEncrypt uses OAEP by default, so the bulk of the terribleness should mainly be how horribly slow this is. It does clearly indicate that the author has no idea what they're doing, though.Edit: For some reason I thought OAEP included randomness. It does not, which should mean you can guess-and-check the plaintext.
https://nodejs.org/api/crypto.html#crypto_crypto_publicencry...
Not sure why it says DSA is supported, the crypto library only supports RSA.
It uses this library that stitches together a PEM from an ssh public key:
https://github.com/dominictarr/ssh-key-to-pem/blob/master/in...
In encryptMessage.js, the plaintext is split into chunks, and then chunks.forEach encrypts each chunk (via crypto.publicEncrypt) independently, and concatenates the chunks to form the ciphertext, aka ECB mode. You shouldn't use ECB mode with any cipher because it is not semantically secure. This is Crypto 101.
In addition, it's a bit wacky to use RSA encryption on your entire message because RSA operations are slow and you are limited to encrypting messages that are the length of your RSA key (well, minus the padding, which is how this developer arrived at the "split plaintext into 214 byte chunks" workaround).
A better solution (in every way) would be to use a "hybrid" encryption scheme, similar to TLS or GPG. To do this, you would:
1. Generate a random key for use with a symmetric cipher (e.g. AES-256) 2. Encrypt the plaintext with the random key, using a secure block mode (e.g. CBC, CTR) 3. Encrypt the random key with the RSA public key 4. Package those things together and share it on Github
Efficient and secure. Also totally unnecessary (you basically just reinvented a subset of GPG) but that's neither here nor there.
It reminds me of the old bash.org quote that basically said the best way to get help from the Internet is not to ask, but to assert an answer, and let people correct you.
- crypto_box() for authenticated public-key encryption
- crypto_box_seal() for anonymous public-key encryption
(with message authentication)
I know for a fact that there are JS bindings for libsodium.http://doc.libsodium.org/bindings_for_other_languages/index....
For PHP developers:
https://github.com/paragonie/pecl-libsodium-doc/blob/master/...
https://github.com/paragonie/pecl-libsodium-doc/blob/master/...
You should consider with RSA keys have a limited size message that can be encrypted (e.g. for 2048 bit keys you are limited to 256 bytes in your message). My solution was to use the SSH key to encrypt the secret I used to encrypt the message with.