Skip to content
This repository was archived by the owner on Sep 3, 2021. It is now read-only.

Use AES in GCM mode instead of CFB mode - #12

Merged
unknwon merged 5 commits into
unknwon:masterfrom
aaronjwood:master
Feb 13, 2017
Merged

Use AES in GCM mode instead of CFB mode#12
unknwon merged 5 commits into
unknwon:masterfrom
aaronjwood:master

Conversation

@aaronjwood

Copy link
Copy Markdown
Contributor

#11

Comment thread string.go
// AESEncrypt encrypts text and given key with AES.
func AESEncrypt(key, text []byte) ([]byte, error) {
// AESEncrypt encrypts plaintext with the given key and nonce using AES in GCM mode.
func AESEncrypt(key, nonce, plaintext []byte) ([]byte, error) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the signature to take in a nonce because the nonce (or IV if you want to call it that) has to be 100% unique. For example, if you go to encrypt a piece of data the encryption/decryption for that piece of data should use its own randomly generated nonce. If you go to encrypt N other pieces of data the encryption/decryption for each piece of data should use its own randomly generated nonce.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not automatically generating and appending the nonce leaves every caller with the task of generating the nonce itself, thus knowing about the actual encryption algorithm this function uses.
AES/GCM requires a 96 bit nonce (gcm.NonceSize()), other ciphers might require a different nonce length.
Please rename this function to AESGCMEncrypt.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can the nonce be automatically generated and encapsulated in here? It needs to be unique for every encryption and be passed over for decryption purposes. I guess there could be methods in here to generate and fetch the nonce, but the client would still need to call them before using encrypt/decrypt. Another option would be to change the structure of this code was so you got an instance. Then you could make use of a counter that's incremented every time encrypt is called.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encryption:

  1. Read the 96-bit nonce with crypto/rand
  2. Use it to encrypt your stuff
  3. Return nonce || ciphertext where || stands for concatenation

Decryption:

  1. Extract the nonce from the "ciphertext" (first 96 bits)
  2. Use it to decrypt the ciphertext
  3. Return the plaintext

Even though the nonce is only 12 bytes long, creating it randomly instead of using a counter is no problem unless you intend to encrypt more than 2^32 messages using the same key.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoting NIST Special Publication 800-38D, 8.3 "Constraints on the Number of Invocations":

The total number of invocations of the authenticated encryption function shall not exceed 2^32, including all IV lengths and all instances of the authenticated encryption function with the given key.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see what you mean about appending it to the ciphertext. Sounds like a good idea.

@aaronjwood

Copy link
Copy Markdown
Contributor Author

Do you have any tests anywhere for this stuff?

@unknwon

unknwon commented Feb 13, 2017

Copy link
Copy Markdown
Owner

Test file is https://github.com/Unknwon/com/blob/master/string_test.go (no tests for this AESEncrypt/AESDecrypt yet), and you got compilation error. Make sure code compiles...

@aaronjwood

Copy link
Copy Markdown
Contributor Author

Oops, my mistake...thought I caught and committed that already. Just fixed it.

@unknwon

unknwon commented Feb 13, 2017

Copy link
Copy Markdown
Owner

All good, merging.

@unknwon
unknwon merged commit 4b950c1 into unknwon:master Feb 13, 2017
Comment thread string.go
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {

b := base64.StdEncoding.EncodeToString(plaintext)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you encrypt the base64-encoded string representation of plaintext and not plaintext directly?
Base64-encoding creates a lot of unnecessary overhead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this as it was, maybe a better question for @unknwon

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronjwood hmm, not sure what's the primary purpose. Please change to whatever suit the best.

Comment thread string.go
// AESEncrypt encrypts text and given key with AES.
func AESEncrypt(key, text []byte) ([]byte, error) {
// AESEncrypt encrypts plaintext with the given key and nonce using AES in GCM mode.
func AESEncrypt(key, nonce, plaintext []byte) ([]byte, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not automatically generating and appending the nonce leaves every caller with the task of generating the nonce itself, thus knowing about the actual encryption algorithm this function uses.
AES/GCM requires a 96 bit nonce (gcm.NonceSize()), other ciphers might require a different nonce length.
Please rename this function to AESGCMEncrypt.

@unknwon

unknwon commented Feb 13, 2017

Copy link
Copy Markdown
Owner

Just realized we would have a API break here (Macaron won't compile correctly now)... maybe we should leave AESEncrypt as it was and make a new function...

@leonklingele

Copy link
Copy Markdown

What's the problem with changing the API? If one intends to use the most recent version of this library, then changes to the code base are required. There's no need to have AESEncrypt anymore as it is/was totally broken.

@unknwon

unknwon commented Feb 13, 2017

Copy link
Copy Markdown
Owner

@leonklingele it is not possible to accept/remember the nonce here: https://github.com/go-macaron/macaron/blob/master/context.go#L421-L429 without breaking the API.

@leonklingele

Copy link
Copy Markdown

That's why I call to prepend / append the nonce directly to the resulting ciphertext in AESEncrypt. That way, the caller doesn't need to care about what the function does internally.

@aaronjwood

Copy link
Copy Markdown
Contributor Author

@unknwon I'm making changes to Macaron right now that will fix the API break. Will send over a PR in a sec...

@aaronjwood

Copy link
Copy Markdown
Contributor Author

@aaronjwood

Copy link
Copy Markdown
Contributor Author

There are definitely some limitations/reduced security with keeping the API the same. For example, there is only ever one nonce generated with my PR above. Also, the key used for AES is still a basic hash of data instead of a derived key...

@aaronjwood

Copy link
Copy Markdown
Contributor Author

@leonklingele it is not possible to accept/remember the nonce here: https://github.com/go-macaron/macaron/blob/master/context.go#L421-L429 without breaking the API.

One issue with this is that the nonce used by setting a secure cookie will need to be used when getting it. I think ideally those funcs (or at least GetSuperSecureCookie) would have an extra input to take in the nonce that was used when first setting the secure cookie.

@leonklingele

Copy link
Copy Markdown

Good luck implementing this. Why make life so hard?

@aaronjwood

Copy link
Copy Markdown
Contributor Author

@leonklingele I think it would be fine if we could change the API and move away from the hashing stuff. There just needs to be coordination in place for the nonce and key, and the key needs to be generated/managed differently. Why would that be so hard?

@aaronjwood

Copy link
Copy Markdown
Contributor Author

@unknwon are you okay if I make more breaking API changes that were discussed here?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants