crypto: golang.org/x/crypto/chacha20poly1305 Index | Examples | Files

package chacha20poly1305

import "golang.org/x/crypto/chacha20poly1305"

Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01.

Index

Examples

Package Files

chacha20poly1305.go chacha20poly1305_amd64.go chacha20poly1305_generic.go xchacha20poly1305.go

Constants

const (
    // KeySize is the size of the key used by this AEAD, in bytes.
    KeySize = 32

    // NonceSize is the size of the nonce used with the standard variant of this
    // AEAD, in bytes.
    //
    // Note that this is too short to be safely generated at random if the same
    // key is reused more than 2³² times.
    NonceSize = 12

    // NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305
    // variant of this AEAD, in bytes.
    NonceSizeX = 24
)

func New Uses

func New(key []byte) (cipher.AEAD, error)

New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.

func NewX Uses

func NewX(key []byte) (cipher.AEAD, error)

NewX returns a XChaCha20-Poly1305 AEAD that uses the given 256-bit key.

XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce, suitable to be generated randomly without risk of collisions. It should be preferred when nonce uniqueness cannot be trivially ensured, or whenever nonces are randomly generated.

Code:

package main

import (
    "bytes"
    "crypto/cipher"
    cryptorand "crypto/rand"
    "encoding/hex"
    "fmt"
    "log"
    mathrand "math/rand"
    "strconv"
    "testing"
)

func TestVectors(t *testing.T) {
    for i, test := range chacha20Poly1305Tests {
        key, _ := hex.DecodeString(test.key)
        nonce, _ := hex.DecodeString(test.nonce)
        ad, _ := hex.DecodeString(test.aad)
        plaintext, _ := hex.DecodeString(test.plaintext)

        var (
            aead cipher.AEAD
            err  error
        )
        switch len(nonce) {
        case NonceSize:
            aead, err = New(key)
        case NonceSizeX:
            aead, err = NewX(key)
        default:
            t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
        }
        if err != nil {
            t.Fatal(err)
        }

        ct := aead.Seal(nil, nonce, plaintext, ad)
        if ctHex := hex.EncodeToString(ct); ctHex != test.out {
            t.Errorf("#%d: got %s, want %s", i, ctHex, test.out)
            continue
        }

        plaintext2, err := aead.Open(nil, nonce, ct, ad)
        if err != nil {
            t.Errorf("#%d: Open failed", i)
            continue
        }

        if !bytes.Equal(plaintext, plaintext2) {
            t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
            continue
        }

        if len(ad) > 0 {
            alterAdIdx := mathrand.Intn(len(ad))
            ad[alterAdIdx] ^= 0x80
            if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
                t.Errorf("#%d: Open was successful after altering additional data", i)
            }
            ad[alterAdIdx] ^= 0x80
        }

        alterNonceIdx := mathrand.Intn(aead.NonceSize())
        nonce[alterNonceIdx] ^= 0x80
        if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
            t.Errorf("#%d: Open was successful after altering nonce", i)
        }
        nonce[alterNonceIdx] ^= 0x80

        alterCtIdx := mathrand.Intn(len(ct))
        ct[alterCtIdx] ^= 0x80
        if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
            t.Errorf("#%d: Open was successful after altering ciphertext", i)
        }
        ct[alterCtIdx] ^= 0x80
    }
}

func TestRandom(t *testing.T) {
    // Some random tests to verify Open(Seal) == Plaintext
    f := func(t *testing.T, nonceSize int) {
        for i := 0; i < 256; i++ {
            var nonce = make([]byte, nonceSize)
            var key [32]byte

            al := mathrand.Intn(128)
            pl := mathrand.Intn(16384)
            ad := make([]byte, al)
            plaintext := make([]byte, pl)
            cryptorand.Read(key[:])
            cryptorand.Read(nonce[:])
            cryptorand.Read(ad)
            cryptorand.Read(plaintext)

            var (
                aead cipher.AEAD
                err  error
            )
            switch len(nonce) {
            case NonceSize:
                aead, err = New(key[:])
            case NonceSizeX:
                aead, err = NewX(key[:])
            default:
                t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
            }
            if err != nil {
                t.Fatal(err)
            }

            ct := aead.Seal(nil, nonce[:], plaintext, ad)

            plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
            if err != nil {
                t.Errorf("Random #%d: Open failed", i)
                continue
            }

            if !bytes.Equal(plaintext, plaintext2) {
                t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
                continue
            }

            if len(ad) > 0 {
                alterAdIdx := mathrand.Intn(len(ad))
                ad[alterAdIdx] ^= 0x80
                if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
                    t.Errorf("Random #%d: Open was successful after altering additional data", i)
                }
                ad[alterAdIdx] ^= 0x80
            }

            alterNonceIdx := mathrand.Intn(aead.NonceSize())
            nonce[alterNonceIdx] ^= 0x80
            if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
                t.Errorf("Random #%d: Open was successful after altering nonce", i)
            }
            nonce[alterNonceIdx] ^= 0x80

            alterCtIdx := mathrand.Intn(len(ct))
            ct[alterCtIdx] ^= 0x80
            if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
                t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
            }
            ct[alterCtIdx] ^= 0x80
        }
    }
    t.Run("Standard", func(t *testing.T) { f(t, NonceSize) })
    t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
}

func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
    b.ReportAllocs()
    b.SetBytes(int64(len(buf)))

    var key [32]byte
    var nonce = make([]byte, nonceSize)
    var ad [13]byte
    var out []byte

    var aead cipher.AEAD
    switch len(nonce) {
    case NonceSize:
        aead, _ = New(key[:])
    case NonceSizeX:
        aead, _ = NewX(key[:])
    }

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
    }
}

func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
    b.ReportAllocs()
    b.SetBytes(int64(len(buf)))

    var key [32]byte
    var nonce = make([]byte, nonceSize)
    var ad [13]byte
    var ct []byte
    var out []byte

    var aead cipher.AEAD
    switch len(nonce) {
    case NonceSize:
        aead, _ = New(key[:])
    case NonceSizeX:
        aead, _ = NewX(key[:])
    }
    ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        out, _ = aead.Open(out[:0], nonce[:], ct[:], ad[:])
    }
}

func BenchmarkChacha20Poly1305(b *testing.B) {
    for _, length := range []int{64, 1350, 8 * 1024} {
        b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
            benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
        })
        b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
            benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
        })

        b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
            benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
        })
        b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
            benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
        })
    }
}

var key = make([]byte, KeySize)

func main() {
    aead, err := NewX(key)
    if err != nil {
        log.Fatalln("Failed to instantiate XChaCha20-Poly1305:", err)
    }

    for _, msg := range []string{
        "Attack at dawn.",
        "The eagle has landed.",
        "Gophers, gophers, gophers everywhere!",
    } {
        // Encryption.
        nonce := make([]byte, NonceSizeX)
        if _, err := cryptorand.Read(nonce); err != nil {
            panic(err)
        }
        ciphertext := aead.Seal(nil, nonce, []byte(msg), nil)

        // Decryption.
        plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
        if err != nil {
            log.Fatalln("Failed to decrypt or authenticate message:", err)
        }

        fmt.Printf("%s\n", plaintext)
    }

}

Package chacha20poly1305 imports 7 packages (graph) and is imported by 292 packages. Updated 2020-03-24. Refresh now. Tools for package owners.