Go ChaCha20-Poly1305 Code Example (Online Runner)
Go ChaCha20-Poly1305 examples with key/nonce/AAD encoding and ciphertext+tag handling.
Online calculator: use the site ChaCha20-Poly1305 text tool.
Note: This snippet requires locally installed dependencies and will not run in the online runner. Run it locally with:
go mod init chacha-demo && go get golang.org/x/crypto/chacha20poly1305 && go run chacha20_poly1305_basic.go
Calculation method
ChaCha20-Poly1305 uses a 32-byte key, 12-byte nonce, optional AAD, and outputs ciphertext plus a 16-byte tag. The tool concatenates ciphertext + tag; this helper matches that format.
Implementation notes
- Package:
golang.org/x/crypto/chacha20poly1305. - Implementation:
Sealreturns ciphertext+tag, matching the UI output. - Notes: never reuse a nonce with the same key; AAD must match on decrypt.
Text encryption example
go
package main
import (
"encoding/hex"
"fmt"
"strings"
"golang.org/x/crypto/chacha20poly1305"
)
func chachaEncryptHex(plaintext string, keyHex string, nonceHex string) (string, error) {
key, err := hex.DecodeString(keyHex)
if err != nil {
return "", err
}
nonce, err := hex.DecodeString(nonceHex)
if err != nil {
return "", err
}
aead, err := chacha20poly1305.New(key)
if err != nil {
return "", err
}
out := aead.Seal(nil, nonce, []byte(plaintext), nil)
return hex.EncodeToString(out), nil
}
func main() {
key := strings.Repeat("00", 32)
nonce := strings.Repeat("01", 12)
value, err := chachaEncryptHex("hello", key, nonce)
if err != nil {
panic(err)
}
fmt.Println(value)
}File encryption example
go
package main
import (
"encoding/hex"
"fmt"
"os"
"strings"
"golang.org/x/crypto/chacha20poly1305"
)
func chachaFile(path string, keyHex string, nonceHex string) (string, error) {
key, err := hex.DecodeString(keyHex)
if err != nil {
return "", err
}
nonce, err := hex.DecodeString(nonceHex)
if err != nil {
return "", err
}
aead, err := chacha20poly1305.New(key)
if err != nil {
return "", err
}
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
out := aead.Seal(nil, nonce, data, nil)
return hex.EncodeToString(out), nil
}
func main() {
file, err := os.CreateTemp("", "chacha-example-*.bin")
if err != nil {
panic(err)
}
defer os.Remove(file.Name())
file.WriteString("hello")
file.Close()
key := strings.Repeat("00", 32)
nonce := strings.Repeat("01", 12)
value, err := chachaFile(file.Name(), key, nonce)
if err != nil {
panic(err)
}
fmt.Println(value)
}Complete script (implementation + tests)
go
package main
import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strings"
"golang.org/x/crypto/chacha20poly1305"
)
type KeyEncoding string
type CipherEncoding string
const (
EncUTF8 KeyEncoding = "utf8"
EncHex KeyEncoding = "hex"
)
const (
CipherHex CipherEncoding = "hex"
CipherBase64 CipherEncoding = "base64"
)
func decodeValue(value string, encoding KeyEncoding) ([]byte, error) {
if encoding == EncHex {
return hex.DecodeString(value)
}
return []byte(value), nil
}
func normalizeKey(key []byte) []byte {
out := make([]byte, chacha20poly1305.KeySize)
copy(out, key)
return out
}
func encodeCiphertext(data []byte, encoding CipherEncoding) string {
if encoding == CipherHex {
return hex.EncodeToString(data)
}
return base64.StdEncoding.EncodeToString(data)
}
func decodeCiphertext(value string, encoding CipherEncoding) ([]byte, error) {
if encoding == CipherHex {
return hex.DecodeString(value)
}
return base64.StdEncoding.DecodeString(value)
}
func chachaEncrypt(plaintext []byte, key, nonce string, keyEnc, nonceEnc KeyEncoding, aad string, aadEnc KeyEncoding, outEnc CipherEncoding) (string, error) {
keyBytes, err := decodeValue(key, keyEnc)
if err != nil {
return "", err
}
nonceBytes, err := decodeValue(nonce, nonceEnc)
if err != nil {
return "", err
}
if len(nonceBytes) != chacha20poly1305.NonceSize {
return "", errors.New("nonce must be 12 bytes")
}
aead, err := chacha20poly1305.New(normalizeKey(keyBytes))
if err != nil {
return "", err
}
var aadBytes []byte
if aad != "" {
aadBytes, err = decodeValue(aad, aadEnc)
if err != nil {
return "", err
}
}
ciphertext := aead.Seal(nil, nonceBytes, plaintext, aadBytes)
return encodeCiphertext(ciphertext, outEnc), nil
}
func chachaDecrypt(combined, key, nonce string, keyEnc, nonceEnc KeyEncoding, aad string, aadEnc KeyEncoding, inEnc CipherEncoding) ([]byte, error) {
keyBytes, err := decodeValue(key, keyEnc)
if err != nil {
return nil, err
}
nonceBytes, err := decodeValue(nonce, nonceEnc)
if err != nil {
return nil, err
}
if len(nonceBytes) != chacha20poly1305.NonceSize {
return nil, errors.New("nonce must be 12 bytes")
}
aead, err := chacha20poly1305.New(normalizeKey(keyBytes))
if err != nil {
return nil, err
}
data, err := decodeCiphertext(combined, inEnc)
if err != nil {
return nil, err
}
var aadBytes []byte
if aad != "" {
aadBytes, err = decodeValue(aad, aadEnc)
if err != nil {
return nil, err
}
}
return aead.Open(nil, nonceBytes, data, aadBytes)
}
func main() {
key := strings.Repeat("00", 32)
nonce := strings.Repeat("01", 12)
ciphertext, err := chachaEncrypt([]byte("hello"), key, nonce, EncHex, EncHex, "context", EncUTF8, CipherHex)
if err != nil {
panic(err)
}
fmt.Println("cipher=", ciphertext)
plain, err := chachaDecrypt(ciphertext, key, nonce, EncHex, EncHex, "context", EncUTF8, CipherHex)
if err != nil {
panic(err)
}
fmt.Println("plain=", string(plain))
}