8

Finally I am posting my first question on StackOverflow. I'm using this site for years now and I always found great answers to all my questions :)

I am implementing a file encryption background daemon which is based on the official Golang cipher example:

func ExampleStreamReader() {
    key := []byte("example key 1234")

    inFile, err := os.Open("encrypted-file")
    if err != nil {
        panic(err)
    }
    defer inFile.Close()

    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    // If the key is unique for each ciphertext, then it's ok to use a zero
    // IV.
    var iv [aes.BlockSize]byte
    stream := cipher.NewOFB(block, iv[:])

    outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        panic(err)
    }
    defer outFile.Close()

    reader := &cipher.StreamReader{S: stream, R: inFile}
    // Copy the input file to the output file, decrypting as we go.
    if _, err := io.Copy(outFile, reader); err != nil {
        panic(err)
    }

    // Note that this example is simplistic in that it omits any
    // authentication of the encrypted data. If you were actually to use
    // StreamReader in this manner, an attacker could flip arbitrary bits in
    // the output.
}

func ExampleStreamWriter() {
    key := []byte("example key 1234")

    inFile, err := os.Open("plaintext-file")
    if err != nil {
        panic(err)
    }
    defer inFile.Close()

    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    // If the key is unique for each ciphertext, then it's ok to use a zero
    // IV.
    var iv [aes.BlockSize]byte
    stream := cipher.NewOFB(block, iv[:])

    outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        panic(err)
    }
    defer outFile.Close()

    writer := &cipher.StreamWriter{S: stream, W: outFile}
    // Copy the input file to the output file, encrypting as we go.
    if _, err := io.Copy(writer, inFile); err != nil {
        panic(err)
    }

    // Note that this example is simplistic in that it omits any
    // authentication of the encrypted data. If you were actually to use
    // StreamReader in this manner, an attacker could flip arbitrary bits in
    // the decrypted result.
}

What is meant with the following quote. About what should I take care to provide a secure encryption and decryption?

Note that this example is simplistic in that it omits any authentication of the encrypted data. If you were actually to use StreamReader in this manner, an attacker could flip arbitrary bits in the output.

Thanks!

M4ng0Squ4sh
  • 83
  • 1
  • 5
  • 2
    For authentication try having a look at [HMAC](http://en.wikipedia.org/wiki/Hash-based_message_authentication_code) first, If that does not suit then possibly an in-line authentication like [GCM mode](http://en.wikipedia.org/wiki/Galois/Counter_Mode) might be better for you. – rossum May 09 '15 at 10:13
  • Great! That helped me figuring it out. Thank you. – M4ng0Squ4sh May 10 '15 at 11:29

1 Answers1

4

From wikipedia:

The block cipher modes ECB, CBC, OFB, CFB, CTR, and XTS provide confidentiality, but they do not protect against accidental modification or malicious tampering.

A good explanation can be found here: https://security.stackexchange.com/a/33576.

Go has support for other modes which do support integrity and authentication checks. As rossum said you can use GCM or CCM. You can find lots of examples on godoc.org. For example HashiCorp's memberlist library.

Another library worth checking out is the NaCL port in golang.org/x/crypto/nacl:

func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool)
func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte

If you're working with small messages this API will probably be a lot easier to use.

Community
  • 1
  • 1
Caleb
  • 9,272
  • 38
  • 30
  • Great understood it now! Managed to get this working in a small test environment. However Go's implementation of GCM or CCM are not streamable. If I'll encrypt a huge file, I would have to load everything into RAM at once... The NaCL port also implements the go crypt AEAD interface. So it's also not streamable. Any suggestions? ;) – M4ng0Squ4sh May 10 '15 at 11:38
  • Take the code and make it streamable. Note that using the plaintext before authentication is dangerous (which is possibly why it isn't streamable in the first place). Otherwise you can use e.g. CTR and perform a HMAC or CMAC encryption over it *and* the IV - basically creating your own two pass (two key!) AEAD cipher. Don't forget to accept good answers. – Maarten Bodewes May 10 '15 at 15:57
  • @M4ng0Squ4sh break the file up into chunks and encrypt each one. With `secretbox` there's an `Overhead` constant which will tell you how much bigger the encrypted chunk will be than the decrypted chunk. You will have to define a format (like the memberlist example) and don't reuse nonces. `io.Reader` should be fairly straightforward... `io.Seeker` would be harder. – Caleb May 10 '15 at 21:34