or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

acme.mdencryption.mdhashing.mdindex.mdnacl.mdopenpgp.mdpublic-key.mdssh.mdutilities.md
tile.json

public-key.mddocs/

Public Key Cryptography

This document covers modern elliptic curve cryptography implementations including Curve25519, Ed25519, Poly1305, and the deprecated BN256 pairing-based cryptography.

Modern Elliptic Curves

Package: golang.org/x/crypto/curve25519

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.

Constants

const (
    ScalarSize = 32 // Scalar size in bytes
    PointSize  = 32 // Point size in bytes
)

Variables

var Basepoint []byte // The canonical Curve25519 generator point

Functions

// 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)

Usage Example

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))
}

Package: golang.org/x/crypto/ed25519

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.

Constants

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
)

Types

// PublicKey is an Ed25519 public key.
type PublicKey = ed25519.PublicKey

// PrivateKey is an Ed25519 private key.
// It implements crypto.Signer.
type PrivateKey = ed25519.PrivateKey

Functions

// 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) bool

Usage Example

package 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)
}

Package: golang.org/x/crypto/poly1305

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.

Constants

const TagSize = 16

Functions

// 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

Types

// 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) bool

Usage Example (for compatibility only)

package 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)
}

Pairing-Based Cryptography (Deprecated)

Package: golang.org/x/crypto/bn256

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.

Variables

var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969")

Types

// 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() string

Usage Example (for compatibility only)

package 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())
}

Best Practices

Key Exchange

  1. Recommended: Use X25519 (curve25519.X25519)
  2. Alternative: ECDH with P-256 from crypto/ecdh (standard library)
  3. Avoid: RSA key exchange, deprecated custom implementations

Digital Signatures

  1. Recommended: Ed25519 for most applications
  2. Alternative: ECDSA with P-256 from crypto/ecdsa (standard library)
  3. Legacy compatibility: RSA with PSS padding from crypto/rsa
  4. Avoid: DSA (broken), RSA with PKCS#1 v1.5 padding

Message Authentication

  1. With encryption: Use ChaCha20-Poly1305 AEAD (see encryption.md)
  2. Without encryption: Use HMAC from crypto/hmac (standard library)
  3. Avoid: Raw Poly1305 (requires one-time keys, easy to misuse)

Algorithm Comparison

Curve25519 vs Ed25519

  • Curve25519 (X25519): Diffie-Hellman key exchange
  • Ed25519: Digital signatures
  • Both use the same underlying curve but with different point formats
  • Cannot directly convert between X25519 and Ed25519 keys

Performance

  • Ed25519: ~50,000 signatures/sec, ~20,000 verifications/sec (typical)
  • X25519: ~100,000 key exchanges/sec (typical)
  • Both are significantly faster than RSA
  • Both have small key sizes (32 bytes public, 32-64 bytes private)

Security Considerations

  1. BN256 deprecation: The 256-bit Barreto-Naehrig curve provides significantly less than 128 bits of security. Use standard library alternatives.

  2. Poly1305 usage: Never reuse a Poly1305 key. Each key must be used only once. Use ChaCha20-Poly1305 AEAD which handles key derivation automatically.

  3. Public key validation: X25519 automatically validates points. For other curves, ensure proper validation to prevent invalid curve attacks.

  4. Nonce/IV management: For encryption with ECDH shared secrets, derive unique keys using HKDF (see hashing.md).

Migration Guide

If using deprecated packages:

  • From BN256: Migrate to standard library elliptic curves or consider specialized pairing libraries with higher security levels
  • From raw Poly1305: Migrate to ChaCha20-Poly1305 AEAD or HMAC
  • From custom ECDH: Migrate to X25519 or standard library crypto/ecdh

Key Generation

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.