5 ms·
Show HN: Encrypted Communication via GitHub Using Node.js and SSH Keys
- tptacek 11y agoIs this what I think it is? An ECB-mode RSA implementation?
- sdevlin 11y agoThis is unfortunately sort of common. Java's crypto package exposes a similar interface.
- zrail 11y agoIt doesn't appear to be a new implementation. It looks like it uses Node's crypto lib: https://nodejs.org/api/crypto.html#crypto_crypto_publicencrypt_public_key_buffer 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/index.js https://github.com/dominictarr/ssh-key-to-pem/blob/master/in...
- tptacek 11y agoIt appears to be using Node's crypto library to apply the RSA transform directly to the plaintext in modulus-size chunks. The problem isn't the quality of the RSA implementation.
- sarciszewski 11y agoWhich means for an arbitrary length message, you can rearrange blocks and it will decrypt (and the victim is none the wiser).
- garrettr_ 11y agoThe point is that it's using ECB mode with RSA, which indicates the developer has no real knowledge of crypto and is just blindly using pairs of "encrypt/decrypt" functions from Node's built-in crypto lib (which is essentially a thing wrapper around OpenSSL and thus joins its illustrious legacy of encouraging developers to make catastrophic cryptographic implementation mistakes). 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.
- jlank 11y agoI love this comment. Thank you for putting the time in to proposing a more thorough solution. I will take this (and other) comments into consideration, and make some much needed improvements :)
- tptacek 11y agoThere are a whole bunch of things you're likely to get wrong trying to design your own "hybrid" encryption system. It's not easy. Why not spend some time learning how to break crypto before you start building it?
- jps359 11y agoDo you have any specific resources that you can recommend for learning to break crypto?
- kaoD 11y ago- Matasano crypto challenges: http://cryptopals.com/ http://cryptopals.com/ - https://www.crypto101.io/ https://www.crypto101.io/ both the presentation and the book. I think those are good to begin with.
- ryan-c 11y agoI agree. There are whole classes of problems that are not at all obvious until one has spent time learning how to break bad crypto. Matasano's challenges[0] (which I think tptacek helped create) are a good start. 0. http://cryptopals.com/ http://cryptopals.com/
- ryan-c 11y agofunction 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.
- tptacek 11y agoOAEP doesn't allow you to encrypt variable-length data. They may very well be using OAEP, but that's not my point.
- ryan-c 11y agoIt's awful crypto and it made me throw up in my mouth a little bit. They should be using RSA to encrypt a random key then encrypting the rest of the message with some sort of authenticated encryption.
- jlank 11y agoSorry I made you barf ryan-c. If you have any interest in making this better I'd be willing to convert your constructive criticism into code. Issues are open on GH :)
- tptacek 11y agoCan you take this as a learning experience? 1. Tools to encrypt messages using Github SSH keys are probably not a good idea. They're no more usable than real message encryption solutions, but have far more constraints. 2. You cannot safely use RSA like a normal cipher. RSA is a tool for building crypto protocols. The way you've deployed it here has a serious vulnerability. If you want to build things that use cryptography, I think you really need to work with a high-level library. Nacl (or libsodium) is a great example of a package that goes out of its way to bulletproof itself.
- baby 11y agoWhat is an ECB-mode RSA? Is it using RSA as a block cipher of blocksize order-1 ? And padding each block and concatenate them?
- tptacek 11y agohttps://news.ycombinator.com/item?id=10186372 https://news.ycombinator.com/item?id=10186372
- mmalecki_ 11y agoThis exists already: https://github.com/substack/cipherhub https://github.com/substack/cipherhub
- jlank 11y agoThat is awesome, I love substack, did not realize he had written that. Thanks for sharing!
- fredley 11y ago> Your private key is not being stolen, read the code! I'd change this to: > Is your private key being stolen? Read the code!
- jlank 11y agoI like that, send a PR and I'll change it :)
- hardwaresofton 11y agoIIRC, asymmetric key encryption is not preferred for large message lengths -- maybe the author could consider embedding an randomly generated AES key, and using that to encrypt the message instead?
- jlank 11y agoI ran into this issue, I couldn't encrypt really large strings so I chunked the plain text. Not sure why that is the case. I would consider doing something like what you suggest, though I'm not sure exactly how I'd implement it. If you're interested in showing me how, I'd love to collaborate on some code with you (start an issue! https://github.com/sadasystems/private-message/issues https://github.com/sadasystems/private-message/issues)
- jlank 11y agoafter reading more comments, I now have a better idea of how to achieve this. thanks again!
- hardwaresofton 11y agoHey no problem! Should I still open that issue? Seems like you have one made already (https://github.com/sadasystems/private-message/issues/5 https://github.com/sadasystems/private-message/issues/5). I ended up doing this for a project that I wanted to have use RSA for large chunks of data, for sending it was: 1. Generate random AES cipher key (I used a 16 byte key) using any available secure rng (it all depends on where the thing gets it's entropy, I think node's crypto.getRandomBytes is supposed to be strong) 2. Pad & Encrypt data with AES 3. Encrypt randomly-generated key with RSA 4. Send the message in an envelope like: {key: <RSA encrypted AES key>, data: <AES encrypted data>} For me, the devil was in the details -- padding took an especially long time for me to understand and solve (the thing I was working on was cross platform, so ruby->js or python->ruby, and of course not all implementations pad the same way), but once that was solved, most other things were easy. The node part was also particularly troubling because I had to deal with the way to specify encodings in node, which was kind of confusing (I spent a lot of time messing with base64/binary encoding and having my terminal start showing gibberish when I tried to print binary data) I don't have access to that code now (I actually wrote it in order to get around the fact that internal networks at a certain company I used to work at didn't have a custom rootCA/support TLS properly), otherwise I'd just post it. Would love to help with the implementation though
- tptacek 11y agoTo function, both the reader and writer have to download special software. If you're going to do that, why not just have both sides download PGP?
- schoen 11y agoMaybe GitHub is being treated as a trusted key exchange intermediary? ... but I guess if people want to do that, they can already accomplish it with Keybase. And PGP.
- bobbywilson0 11y agoSince I have worked on something similar, in my opinion, it isn't the downloading software that is necessarily a hurdle (although I agree that it is a bit of one); it is around the general difficulty and pain around your local setup and finding the user you are trying to contact's pgp key. This has been discussed at length, but I think it comes down to pgp being enough of a hassle that people who aren't focused on privacy/security don't bother using it. 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.
- hellbanner 11y agoRelated: https://www.agwa.name/projects/git-crypt/ https://www.agwa.name/projects/git-crypt/
- Laaw 11y agoCool, but what I love more about this (post) is how helpful the comments are! 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.
- bobbywilson0 11y agoI have also built something similar, I knew of the existence of cipherhub, but my goal was to focus on the ease of use, with the browser (https://mailbeam.io https://mailbeam.io and https://github.com/bobbywilson0/gh-message https://github.com/bobbywilson0/gh-message). I do admit that my solution is not as easy as it should be yet. 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.
- devy 11y agoYup! Last time I've tried to use the 2048bit Github SSH key to encrypt some message, gpg would complain if the message is larger than 226 characters.
- jlank 11y agoThis is great. Thanks for sharing! I could definitely see building out something similar on top of private-message once I firm up the scripts with a more secure block mode.
- sarciszewski 11y agoUsing libsodium, there are two routes you can go: - 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.html http://doc.libsodium.org/bindings_for_other_languages/index.... For PHP developers: https://github.com/paragonie/pecl-libsodium-doc/blob/master/chapters/05-publickey-crypto.md#crypto-box https://github.com/paragonie/pecl-libsodium-doc/blob/master/... https://github.com/paragonie/pecl-libsodium-doc/blob/master/chapters/08-advanced.md#crypto-box-seal https://github.com/paragonie/pecl-libsodium-doc/blob/master/...
- philip1209 11y agoKeybase.io + Gists works well too!