
Show HN: Encrypted Communication via GitHub Using Node.js and SSH Keys - jlank
https://github.com/sadasystems/private-message
======
mmalecki_
This exists already:
[https://github.com/substack/cipherhub](https://github.com/substack/cipherhub)

~~~
jlank
That is awesome, I love substack, did not realize he had written that. Thanks
for sharing!

------
fredley
> 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
I like that, send a PR and I'll change it :)

------
tptacek
To 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?

~~~
bobbywilson0
Since 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.

~~~
tptacek
I just don't buy it. Using Github-registered SSH keys to communicate is _also_
an idiosyncratic and complicated way to exchange messages (evidence: far, far
more people use PGP than use schemes like this). It's also much less secure.

I see absolutely no win here.

------
tptacek
Is this what I think it is? An ECB-mode RSA implementation?

~~~
zrail
It 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_publicencry...](https://nodejs.org/api/crypto.html#crypto_crypto_publicencrypt_public_key_buffer)

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...](https://github.com/dominictarr/ssh-key-to-
pem/blob/master/index.js)

~~~
garrettr_
The 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
I 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
There 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
Do you have any specific resources that you can recommend for learning to
break crypto?

~~~
kaoD
\- 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.

------
Laaw
Cool, 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.

------
sarciszewski
Using 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....](http://doc.libsodium.org/bindings_for_other_languages/index.html)

For PHP developers:

[https://github.com/paragonie/pecl-libsodium-
doc/blob/master/...](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)

------
bobbywilson0
I 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.

~~~
jlank
This 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.

------
hardwaresofton
IIRC, 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
I 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
after reading more comments, I now have a better idea of how to achieve this.
thanks again!

~~~
hardwaresofton
Hey 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

------
hellbanner
Related: [https://www.agwa.name/projects/git-
crypt/](https://www.agwa.name/projects/git-crypt/)

------
philip1209
Keybase.io + Gists works well too!

