4 ms·
If you're interested in doing AEAD with the current best-practice algorithms in golang, you might get inspiration from my work-in-progress symcrypt package. I'm
by peterldowns 1y ago
If you're interested in doing AEAD with the current best-practice algorithms in golang, you might get inspiration from my work-in-progress symcrypt package. I'm not a cryptographer and you shouldn't trust me when I say it works correctly — but it's basically just a small, correct, wrapper around the chacha20poly1305 code in the golang standard library. It has the slight advantage of using different types for the plaintext and the associated data (here called Owner, because I use it to store API keys owned by specific
If you squint at the example usage in the tests, it's basically the API that the blogpost describes.
https://github.com/peterldowns/symcrypt/blob/main/symcrypt_test.go https://github.com/peterldowns/symcrypt/blob/main/symcrypt_t...
As an aside, I'm always curious to understand why the encryption people say "never roll your own crypto" but then also ship confusing APIs without clear usage examples. For instance, check out the golang chacha20poly1305 docs:
https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
- tptacek 1y agoI don't understand what you're finding unclear about the Chapoly docs there. AEAD encryption is a first-class abstraction in the standard Go crypto library; in the same sense that crypto/sha256 functions return a crypto.Hash, chacha20poly1305 returns a crypto.AEAD. AEAD itself includes clear usage examples. Your `symcrypt` interface lands in a pretty weird place? AEADs in Go export "Seal" and "Unseal" --- with deliberately different names than crypto/cipher/Block's "Encrypt" and "Decrypt", because they're doing something different. The "Owner" thing in your package is kind of odd too. You're exposing an interface over Go's AEAD primitives, but not letting users actually provide authenticated data. I don't much care except the whole point of this post is why that matters.
- gerdesj 1y agoWhen it comes to this stuff you generally have to follow advice from someone who understands what is going on. For me, personally, I'm going to side with tptacek - he has a track record that I have seen over at least a decade if not two. I don't know the other bloke but this is a bit of a worry: "I'm not a cryptographer".
- peterldowns 1y agoAgreed; I posted here with the goal of receiving advice.
- peterldowns 1y agoWhat I'm finding unclear is how to use the chapoly primitives in a secure way to accomplish my goal. I want to use AEAD encryption to store API keys, per customer, using a single app secret that my app will read from my secret manager when it starts up. AEAD seems like the right way to do that. What's the right way to do that, with AEAD, in golang? The examples/docs for cipher.AEAD at https://pkg.go.dev/crypto/cipher#AEAD https://pkg.go.dev/crypto/cipher#AEAD don't mention chapoly, but a security reseracher friend of mine recommended I use it. The docs/examples for the chapoly library have two methods — New and NewX, and only NewX has an example. In that example, no associated data is actually used. > Your `symcrypt` interface lands in a pretty weird place? AEADs in Go export "Seal" and "Unseal" --- with deliberately different names than crypto/cipher/Block's "Encrypt" and "Decrypt", because they're doing something different. What should I use? I'd be extremely happy to do the Right Thing. I linked symcrypt and posted here because I am hoping someone can point me to it. > You're exposing an interface over Go's AEAD primitives, but not letting users actually provide authenticated data. I really don't understand what you mean by "not letting users actually provide authenticated data". Here, in this test, I show how if you encrypt some secret for one user (the associated data is the Owner), you can only decrypt it if you provide the same associated data (the same Owner). https://github.com/peterldowns/symcrypt/blob/c220f7767fa6c1a1abb0874c2c540a29ae7fea77/symcrypt_test.go#L32 https://github.com/peterldowns/symcrypt/blob/c220f7767fa6c1a...
- tptacek 1y agoI feel like you haven't really gotten your head around what Authenticated Data is. That's OK! Look at 'stavros upthread --- lots of clueful people have trouble with this concept. What you should do is just take the examples from cipher#AEAD, but where they do: block, err := aes.NewCipher(key) if err != nil { panic(err.Error()) } aesgcm, err := cipher.NewGCM(block) if err != nil { panic(err.Error()) } Instead you just do chapoly, err := chacha20poly1305.NewX(key) if err != nil { panic(err.Error()) } The rest of the code is the same, except that where they write "Never use more than 2^32 random nonces with a given key because of the risk of a repeat", you can ignore that and use a long nonce (like in the example for chacha20poly1305.NewX). Your "Owner" looks like what cryptographers would call a "domain separation constant". Domain separation is good! It's another application of authenticated data, too. But not the only one. The Go standard library's AEAD "Seal" and "Unseal" is a better interface than what you've got now.
- hxtk 1y agoA lot of the hardest problems in practical cryptography come down less to the abstractions around literally encrypting and decrypting things and more around the secure management of key material to ensure that the application supports things like online key rotation and makes it easy to verify that keys are being generated, serialized, and stored securely, and addressing the "First Secret" problem. If you're wanting to learn to use cryptography by developing an abstraction over stdlib cryptographic APIs, I would encourage you to find solutions to those problems. Another source of inspiration (and something I use in production) is the Tink family of cryptographic libraries by Google [1]. Their Go implementation [2] is not without its warts, but it's very difficult to run into any of those security bugs that exist around cryptography. Where the Go documentation lacks, there are some examples in the developer docs that help fill some of the gaps [3] [4]. The documentation isn't 100% complete, but I find it more discoverable than the standard library because while the standard library requires you to read both `crypto/cipher` and `crypto/aes` or `golang.org/x/crypto/chacha20poly1305` depending on what kind of cipher you want, Tink organizes it by use cases [5] and generally groups together all the things you need to do cryptographic operations under the use-case-named interfaces in the `tink` package [6], with the corresponding key generation templates located under the top-level packages of the same name [7]. [1]: https://developers.google.com/tink https://developers.google.com/tink [2]: https://github.com/tink-crypto/tink-go/ https://github.com/tink-crypto/tink-go/ [3]: https://developers.google.com/tink/key-management-overview#go https://developers.google.com/tink/key-management-overview#g... [4]: https://developers.google.com/tink/encrypt-data#go https://developers.google.com/tink/encrypt-data#go [5]: https://developers.google.com/tink/choose-primitive https://developers.google.com/tink/choose-primitive [6]: https://pkg.go.dev/github.com/tink-crypto/tink-go/v2/tink#AEAD https://pkg.go.dev/github.com/tink-crypto/tink-go/v2/tink#AE... [7]: https://pkg.go.dev/github.com/tink-crypto/tink-go/v2@v2.4.0/aead https://pkg.go.dev/github.com/tink-crypto/tink-go/v2@v2.4.0/...
- zorgmonkey 1y agoit is worth pointing out that tink has binding for a bunch languages (C++, objective-c, rust, python, go and java) and has support for a bunch key management systems (GCP, AWS and Hashicorp)