Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go implementation #13

Closed
burke opened this issue Nov 26, 2014 · 33 comments
Closed

Go implementation #13

burke opened this issue Nov 26, 2014 · 33 comments

Comments

@burke
Copy link
Member

burke commented Nov 26, 2014

I'd PR this, but it has no code in common with the previous ruby implementation, so it wouldn't provide a whole lot of value.

Instead, see https://github.com/Shopify/ejson

Highlights:

  • Implements EJSON v1.0 RFC except with nacl/box instead of PKCS7.
  • Elliptic Curve Crypto rather than Prime Factorization (RSA)
    • DJB code rather than OpenSSL code
    • Relative difficulty of ECC problem allows much shorter keys and ciphertexts => more tractable EJSON files
  • Makefile generates rubygem and debian package
    • Saner distribution to production nodes
  • Simplified key management
    • /opt/ejson/keys directory contains files named with full public key, containing private key.
    • ejson decrypt looks up key referenced by ejson file in /opt/ejson/keys
  • We can link this codebase into our container init script so that we don't have to shell out to ruby for decryption.
  • Improved tests

I'll keep poking at this over the next couple of days, but it's basically ready for feedback. The code is pretty well compartmentalized and documented; I don't think it'll be very hard to grok what's going on. Start with README.md, then ejson.go and cmd/ejson/*.go.

Review/cc any combination of: @Shopify/stack, @Shopify/secops. It can easily wait until BFCM if you're busy.

@mutemule
Copy link

Implements EJSON v1.0 RFC except with nacl/box instead of PKCS7

❤️

I am a huge fan of NaCl, and quite wary of the PKCS standards.

@EiNSTeiN-
Copy link

djb 💯

@burke
Copy link
Member Author

burke commented Nov 26, 2014

I added manpages which are installed with the debian package and vendored with the rubygem (unfortunately rubygems can't install manpages globally). ejson help, ejson help encrypt, etc., will now launch man for the relevant manpage.

@eapache
Copy link

eapache commented Nov 26, 2014

What happens if you try to encrypt:

"_starts_with_an_underscore": {
  "no_leading_underscore": "123123123123123123123"
}

I'm assuming it does the simple thing, and encrypts the value anyways, I'm just wondering if that's the nicest behaviour or if it should at least be documented?

@burke
Copy link
Member Author

burke commented Nov 26, 2014

@eapache it's not in the manpage, but it is in README.md: https://github.com/Shopify/ejson/tree/go-impl#format (see point 6). I'll add it to ejson.5.

@burke
Copy link
Member Author

burke commented Nov 26, 2014

(I want to err on the side of encrypting too much rather than too little, so this is the behaviour I want, though I'm open to argument on that point)

@eapache
Copy link

eapache commented Nov 26, 2014

👍 to explanation and documenting it

@burke
Copy link
Member Author

burke commented Nov 26, 2014

61c5d48

@burke
Copy link
Member Author

burke commented Nov 26, 2014

Also fun: http://shopify.github.io/ejson/

@sirupsen
Copy link
Contributor

@burke any plans to add schemas to the Go version?

@burke
Copy link
Member Author

burke commented Dec 1, 2014

Yeah, but I plan to do that in a separate codebase, probably just a gem that plugs in to rails. There's no reason that bit has to clutter the ejson codebase.

@eapache
Copy link

eapache commented Dec 1, 2014

@burke
Copy link
Member Author

burke commented Dec 1, 2014

  1. 👍, will change.
  2. That's just there because Go doesn't really support implicitly rewriting a file with the current mode. I have to fetch the current mode and pass it to ioutil.WriteFile, that's all.
  3. I hadn't really factored that into my threat model, but it's probably worth thinking about. I'll do something a little smarter.
  4. That error is directly displayed to CLI users so I wanted the message to be specific, but it would be nice to embed the old err.Error() into the new error string. Will do.

@burke
Copy link
Member Author

burke commented Dec 1, 2014

Actually, I think it's unreasonable to prevent traversal here: The only input value is the keydir, which is allowed to be anywhere on the system.

I made the other two changes.

@eapache
Copy link

eapache commented Dec 1, 2014

The only input value is the keydir, which is allowed to be anywhere on the system.

The other input is the key; is there an escalation where a user can generate a fake public key to escape the key-dir?

@burke
Copy link
Member Author

burke commented Dec 1, 2014

I don't think so; if they do, they've found a bug in printf's %x formatter, not so much in ejson. I don't personally feel we need to be quite that defensive. I like the train of thought though, that hadn't occurred to me.

@eapache
Copy link

eapache commented Dec 1, 2014

@burke
Copy link
Member Author

burke commented Dec 1, 2014

  1. Done; I just moved that printf up to the cmd/ejson package.
  2. Surprisingly, with all the type assertions you need to accomplish it the read-manipulate-dump way in Go's type system, this method is actually simpler -- though I'll take another run at trying to simplify that code this afternoon.

Also, I really like that the current system preserves the input formatting. Explicit ordering would interfere with semantic grouping, might put _public_key in a weird place in the file, and would just generally violate user expectations.

I'll try to make the json stuff better, but I don't think there's a better option than the scanner.

@eapache
Copy link

eapache commented Dec 1, 2014

@eapache
Copy link

eapache commented Dec 1, 2014

@eapache
Copy link

eapache commented Dec 1, 2014

Lots of minor comments, but on the whole I really like this, 👍

@burke
Copy link
Member Author

burke commented Dec 1, 2014

  • \A \z
  • regex strictness
  • depointerize nonce and key (the reason I did this is that nacl takes pointers as args, and this is just what I refactored into, but you're right, it would be nicer to just & them when I pass them)
  • In Go 1.4+, crypto.rand uses /dev/urandom (really, getrandom without GRND_RANDOM). I don't love the idea of using a PRNG for key generation, especially when reading from /dev/random is so trivial. Thoughts?
  • private/public typo
  • The version file is actually because of this
  • os.Exit(1) after manpage: It's because that will only ever happen if the syscall.Exec fails. (syscall.Exec is a true exec). So the Exit(1) here is appropriate, as it indicates the manpage wasn't found or something.
  • Rename API to EncryptFileInPlace

@eapache
Copy link

eapache commented Dec 1, 2014

especially when reading from /dev/random is so trivial. Thoughts?

It is, I think, the only thing keeping the library part here from being cross-platform (making the whole thing cross-platform would also require ripping out the man-page bits). Security-specific discussion has been taken offline.

@burke
Copy link
Member Author

burke commented Dec 1, 2014

I replied to your email, but TL;DR: It's not the hugest of deals but I figured I'd do it right.

@burke
Copy link
Member Author

burke commented Dec 1, 2014

I have no real desire to support this on Windows, so I don't feel too bad about reading from /dev/* directly in that regard.

@sirupsen
Copy link
Contributor

sirupsen commented Dec 1, 2014

I'll take a look at this, this is cool.

@burke
Copy link
Member Author

burke commented Dec 1, 2014

Back to crypto/rand: 629edb0

@burke
Copy link
Member Author

burke commented Dec 1, 2014

To summarize our discussion offline, /dev/urandom is fine but Go's PRNG is not. Go will use /dev/urandom on linux, and probably only falls back to the bad implenetation on Windows and maybe plan9.

@sirupsen
Copy link
Contributor

sirupsen commented Dec 1, 2014

First of all, it's a nicely commented and structured project. Good job!

Few comments reading through it:

  • I don't think you need to supply valid permissions to writeFile from a stat, open(2) only uses permissions if O_CREAT is set iirc.
  • We should take an -o flag, otherwise someone redirecting output to a file might run into nasty bugs as they try to parse an error as JSON.
  • What's the reason for the Ruby gem? Shouldn't we just have people install the package? Is it due to OS X etc?
  • Are public keys really always 32 bytes?
  • I assume the reason for gojson because of the map randomization? And we don't want to sort it for people either, so we can't do that hack either.
  • What happens when the key is an array? Why's it allowed?

With this, I'm not sure I'm seeing how schemas could be built on top of it in a reasonable way where everything isn't encrypted?

@burke
Copy link
Member Author

burke commented Dec 1, 2014

  • permissions/writeFile: correct me if I'm missing something, but ioutil.WriteFile requires a mode: http://golang.org/pkg/io/ioutil/#WriteFile
  • yeah, ok, that's fair. I'll add an -o flag.
  • gem-vs-package: It makes distributing updates much easier, since we can stick it in Gemfiles. I could build an OS X package very easily though.
  • 32-byte keys: Yes. ECC is much safer per key-bit than RSA. Nacl/box in particular always uses 256-bit public and private keys.
  • gojson: Yup. I also like that the user can structure their JSON however they want and we don't interfere with that structure.
  • keys are never arrays, but you probably meant values. It's allowed because:
    • It would be more work to disallow it
    • It could make sense if you wanted to have multiple API keys or something: "tokens": ["token1", "token2"] for rotation or load-balancing or whatever.
  • schemas: I was thinking we could have it assert that, for example, either of {,_}description is present, so for each metadata attribute you could choose whether or not to encrypt it. I'm going to write the validator on the plane tomorrow I think.

@burke
Copy link
Member Author

burke commented Dec 1, 2014

Also, it's probably worth pointing out that gojson is actually the builtin encoding/json library, only with some lowercase letters made uppercase to expose the scanner API.

@eapache
Copy link

eapache commented Dec 1, 2014

Also, it's probably worth pointing out that gojson is actually the builtin encoding/json library

...from some unspecified point in the past. Better than something hand-rolled I guess, but still a bit icky. Alas.

@burke
Copy link
Member Author

burke commented Dec 2, 2014

...from some unspecified point in the past. Better than something hand-rolled I guess, but still a bit icky. Alas.

Agree on all counts.

@sirupsen sirupsen closed this as completed Jan 8, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants