This document covers modern elliptic curve cryptography implementations including Curve25519, Ed25519, Poly1305, and the deprecated BN256 pairing-based cryptography.
X25519 is the Diffie-Hellman key exchange function using the Curve25519 elliptic curve, defined in RFC 7748. This package is a wrapper around crypto/ecdh for Curve25519 operations.
const (
ScalarSize = 32 // Scalar size in bytes
PointSize = 32 // Point size in bytes
)var Basepoint []byte // The canonical Curve25519 generator point// X25519 returns the result of the scalar multiplication (scalar * point).
// This is the recommended function for most use cases.
func X25519(scalar, point []byte) ([]byte, error)
// ScalarBaseMult computes the scalar multiplication of the base point.
// This is equivalent to X25519(scalar, Basepoint) and is used for generating public keys.
func ScalarBaseMult(dst, scalar *[32]byte)
// ScalarMult computes the scalar multiplication of an arbitrary point.
// Deprecated: Use X25519 instead, which has better validation and error handling.
func ScalarMult(dst, scalar, point *[32]byte)package main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/curve25519"
)
func main() {
// Generate Alice's key pair
var alicePrivate, alicePublic [32]byte
rand.Read(alicePrivate[:])
curve25519.ScalarBaseMult(&alicePublic, &alicePrivate)
// Generate Bob's key pair
var bobPrivate, bobPublic [32]byte
rand.Read(bobPrivate[:])
curve25519.ScalarBaseMult(&bobPublic, &bobPrivate)
// Alice computes shared secret
aliceShared, _ := curve25519.X25519(alicePrivate[:], bobPublic[:])
// Bob computes shared secret
bobShared, _ := curve25519.X25519(bobPrivate[:], alicePublic[:])
// Both should have the same shared secret
fmt.Printf("Alice's shared: %x\n", aliceShared)
fmt.Printf("Bob's shared: %x\n", bobShared)
fmt.Printf("Secrets match: %v\n", string(aliceShared) == string(bobShared))
}Ed25519 is a public-key signature system using twisted Edwards curves, defined in RFC 8032. This package is a wrapper around crypto/ed25519 from the standard library.
const (
PublicKeySize = 32 // Public key size in bytes
PrivateKeySize = 64 // Private key size in bytes
SignatureSize = 64 // Signature size in bytes
SeedSize = 32 // Seed size in bytes
)// PublicKey is an Ed25519 public key.
type PublicKey = ed25519.PublicKey
// PrivateKey is an Ed25519 private key.
// It implements crypto.Signer.
type PrivateKey = ed25519.PrivateKey// GenerateKey generates a public/private key pair using entropy from rand.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error)
// NewKeyFromSeed calculates a private key from a seed.
// The seed must be 32 bytes.
func NewKeyFromSeed(seed []byte) PrivateKey
// Sign signs the message with the private key.
func Sign(privateKey PrivateKey, message []byte) []byte
// Verify reports whether sig is a valid signature of message by publicKey.
func Verify(publicKey PublicKey, message, sig []byte) boolpackage main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/ed25519"
)
func main() {
// Generate key pair
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
// Sign a message
message := []byte("Hello, World!")
signature := ed25519.Sign(privateKey, message)
fmt.Printf("Public key: %x\n", publicKey)
fmt.Printf("Signature: %x\n", signature)
// Verify signature
valid := ed25519.Verify(publicKey, message, signature)
fmt.Printf("Valid: %v\n", valid)
// Verify with wrong message
wrongValid := ed25519.Verify(publicKey, []byte("Wrong message"), signature)
fmt.Printf("Wrong valid: %v\n", wrongValid)
}Poly1305 is a one-time message authentication code (MAC). Deprecated - use chacha20poly1305 or crypto/hmac instead.
For new applications, use ChaCha20-Poly1305 AEAD which combines ChaCha20 encryption with Poly1305 authentication. For message authentication without encryption, use HMAC from crypto/hmac.
const TagSize = 16// Sum generates an authenticator for m using a one-time key.
// The key must be unique for each message.
func Sum(out *[16]byte, m []byte, key *[32]byte)
// Verify reports whether mac is a valid authenticator for m with the given key.
func Verify(mac *[16]byte, m []byte, key *[32]byte) bool// MAC is an io.Writer computing a Poly1305 MAC.
type MAC struct {
// contains unexported fields
}
// New returns a new MAC instance with the given key.
// The key must be unique for each message.
func New(key *[32]byte) *MAC
// Write adds more data to the running MAC.
func (h *MAC) Write(p []byte) (n int, err error)
// Sum appends the current MAC to b and returns the resulting slice.
func (h *MAC) Sum(b []byte) []byte
// Size returns the MAC size in bytes (always 16).
func (h *MAC) Size() int
// Verify returns true if the MAC of all data written is equal to expected.
func (h *MAC) Verify(expected []byte) boolpackage main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/poly1305"
)
func main() {
// Generate a one-time key
var key [32]byte
rand.Read(key[:])
message := []byte("authenticate this message")
// Generate MAC
var mac [16]byte
poly1305.Sum(&mac, message, &key)
fmt.Printf("MAC: %x\n", mac)
// Verify MAC
valid := poly1305.Verify(&mac, message, &key)
fmt.Printf("Valid: %v\n", valid)
// Using MAC writer
h := poly1305.New(&key)
h.Write(message)
mac2 := h.Sum(nil)
fmt.Printf("MAC2: %x\n", mac2)
}Bilinear group operations over a 256-bit Barreto-Naehrig curve. Deprecated - security has been weakened.
This package implements bilinear groups used in pairing-based cryptography. However, recent cryptanalytic advances have significantly reduced the security level of BN curves at this size. It should not be used in new applications.
var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969")// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G1 struct {
// contains unexported fields
}
// RandomG1 returns x and g₁ˣ where x is a random, non-zero number read from r.
func RandomG1(r io.Reader) (*big.Int, *G1, error)
// Add sets e to a+b and returns e.
func (e *G1) Add(a, b *G1) *G1
// Neg sets e to -a and returns e.
func (e *G1) Neg(a *G1) *G1
// ScalarMult sets e to a*k and returns e.
func (e *G1) ScalarMult(a *G1, k *big.Int) *G1
// ScalarBaseMult sets e to g*k where g is the generator and returns e.
func (e *G1) ScalarBaseMult(k *big.Int) *G1
// Marshal converts e to a byte slice.
func (e *G1) Marshal() []byte
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and returns e and true, or nil and false if the input is invalid.
func (e *G1) Unmarshal(m []byte) (*G1, bool)
// String returns the string representation of e.
func (e *G1) String() string
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type G2 struct {
// contains unexported fields
}
// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
func RandomG2(r io.Reader) (*big.Int, *G2, error)
// Add sets e to a+b and returns e.
func (e *G2) Add(a, b *G2) *G2
// ScalarMult sets e to a*k and returns e.
func (e *G2) ScalarMult(a *G2, k *big.Int) *G2
// ScalarBaseMult sets e to g*k where g is the generator and returns e.
func (e *G2) ScalarBaseMult(k *big.Int) *G2
// Marshal converts e to a byte slice.
func (n *G2) Marshal() []byte
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and returns e and true, or nil and false if the input is invalid.
func (e *G2) Unmarshal(m []byte) (*G2, bool)
// String returns the string representation of e.
func (e *G2) String() string
// GT is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
type GT struct {
// contains unexported fields
}
// Pair calculates the Optimal Ate pairing.
func Pair(g1 *G1, g2 *G2) *GT
// Add sets e to a+b and returns e.
func (e *GT) Add(a, b *GT) *GT
// Neg sets e to -a and returns e.
func (e *GT) Neg(a *GT) *GT
// ScalarMult sets e to a*k and returns e.
func (e *GT) ScalarMult(a *GT, k *big.Int) *GT
// Marshal converts e to a byte slice.
func (n *GT) Marshal() []byte
// Unmarshal sets e to the result of converting the output of Marshal back into
// a group element and returns e and true, or nil and false if the input is invalid.
func (e *GT) Unmarshal(m []byte) (*GT, bool)
// String returns the string representation of e.
func (e *GT) String() stringpackage main
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/bn256"
)
func main() {
// Generate random elements
_, g1, _ := bn256.RandomG1(rand.Reader)
_, g2, _ := bn256.RandomG2(rand.Reader)
// Calculate pairing
gt := bn256.Pair(g1, g2)
fmt.Printf("G1: %s\n", g1.String())
fmt.Printf("G2: %s\n", g2.String())
fmt.Printf("GT: %s\n", gt.String())
// Bilinearity property: e(g1^a, g2^b) = e(g1, g2)^(ab)
a := big.NewInt(7)
b := big.NewInt(11)
g1a := new(bn256.G1).ScalarBaseMult(a)
g2b := new(bn256.G2).ScalarBaseMult(b)
pairAB := bn256.Pair(g1a, g2b)
gtBase := bn256.Pair(new(bn256.G1).ScalarBaseMult(big.NewInt(1)),
new(bn256.G2).ScalarBaseMult(big.NewInt(1)))
ab := new(big.Int).Mul(a, b)
gtAB := new(bn256.GT).ScalarMult(gtBase, ab)
fmt.Printf("Pairing property holds: %v\n", pairAB.String() == gtAB.String())
}BN256 deprecation: The 256-bit Barreto-Naehrig curve provides significantly less than 128 bits of security. Use standard library alternatives.
Poly1305 usage: Never reuse a Poly1305 key. Each key must be used only once. Use ChaCha20-Poly1305 AEAD which handles key derivation automatically.
Public key validation: X25519 automatically validates points. For other curves, ensure proper validation to prevent invalid curve attacks.
Nonce/IV management: For encryption with ECDH shared secrets, derive unique keys using HKDF (see hashing.md).
If using deprecated packages:
Always use crypto/rand for key generation:
import "crypto/rand"
// For X25519
var private [32]byte
rand.Read(private[:])
// For Ed25519
pub, priv, _ := ed25519.GenerateKey(rand.Reader)Never use predictable random number generators or derive keys from passwords without a proper KDF.