Documentation
¶
Overview ¶
Package red25519 implements RedDSA (randomized, re-randomizable) signatures built on Ed25519. It extends standard Ed25519 with key blinding: a 32-byte scalar blinding factor is multiplied into both the private and public key, producing a new keypair that is unlinkable to the original yet fully functional for signing and verification.
The API mirrors crypto/ed25519, so callers can treat it as a near drop-in replacement with additional BlindPublicKey, BlindPrivateKey, and BlindingFactor primitives (see blind.go).
Verify is intentionally stricter than crypto/ed25519.Verify: it rejects all small-order public keys (points whose order divides the cofactor 8), which would allow trivial or near-trivial signature forgery. Normal Ed25519 keypairs are unaffected.
This package does not implement VerifyWithOptions for Ed25519ctx or Ed25519ph (context strings and pre-hashing). These modes are outside the scope of I2P RedDSA and are not needed for destination blinding. Callers requiring Ed25519ctx/Ed25519ph should use crypto/ed25519.VerifyWithOptions directly.
Index ¶
- Constants
- func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error)
- func Sign(privateKey PrivateKey, message []byte) []byte
- func Verify(publicKey PublicKey, message []byte, sig []byte) bool
- type BlindingFactor
- type PrivateKey
- func (priv PrivateKey) Equal(x crypto.PrivateKey) bool
- func (priv PrivateKey) IsBlinded() bool
- func (priv PrivateKey) Public() crypto.PublicKey
- func (priv PrivateKey) Scalar() []byte
- func (priv PrivateKey) Seed() []byte
- func (priv PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error)
- type PublicKey
Constants ¶
const ( // PublicKeySize is the size, in bytes, of public keys. PublicKeySize = 32 // PrivateKeySize is the size, in bytes, of private keys. PrivateKeySize = 64 // SignatureSize is the size, in bytes, of signatures. SignatureSize = 64 // SeedSize is the size, in bytes, of private key seeds, // which are used to derive the full private key. SeedSize = 32 )
const (
// BlindingFactorSize is the size, in bytes, of blinding factors.
BlindingFactorSize = 32
)
Variables ¶
This section is empty.
Functions ¶
func GenerateKey ¶
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error)
GenerateKey generates a public/private key pair using entropy from rand. If rand is nil, crypto/rand.Reader will be used.
func Sign ¶
func Sign(privateKey PrivateKey, message []byte) []byte
Sign signs the message with privateKey and returns a 64-byte signature. It works with both normal keys (from GenerateKey/NewKeyFromSeed) and blinded keys (from BlindPrivateKey). For blinded keys, the pre-computed scalar is used directly and the nonce is derived from the embedded domain-separated prefix.
func Verify ¶
Verify reports whether sig is a valid signature of message by publicKey. It returns false for malformed inputs.
Unlike crypto/ed25519.Verify, this function rejects all small-order public keys (points whose order divides the cofactor 8), not just the identity. Small-order points allow trivial or near-trivial forgery because k·A collapses to the identity (or a predictable torsion point) for all challenges k. Normal Ed25519 keypairs are unaffected.
Types ¶
type BlindingFactor ¶
type BlindingFactor []byte
BlindingFactor is a 32-byte clamped Ed25519 scalar used to blind (re-randomize) keypairs. Blinding produces a new keypair that is unlinkable to the original yet algebraically related, enabling destination blinding in I2P encrypted leasesets.
func ComposeBlindingFactors ¶
func ComposeBlindingFactors(bf1, bf2 BlindingFactor) (BlindingFactor, error)
ComposeBlindingFactors computes the scalar product of two blinding factors: result = bf1 · bf2 mod ℓ. When used with BlindPublicKey, the composed factor produces the same blinded public key as sequential blinding with bf1 then bf2:
composed, _ := ComposeBlindingFactors(bf1, bf2) BlindPublicKey(pub, composed) == BlindPublicKey(BlindPublicKey(pub, bf1), bf2)
For private key blinding, the composed factor yields the same scalar and public key but a different deterministic nonce prefix, so signatures will differ from those produced by sequential blinding. Both are valid.
The returned BlindingFactor is a canonical scalar (reduced mod ℓ), not a clamped byte string. When passed to BlindPublicKey or BlindPrivateKey, it takes the canonical decoding path in scalarFromBlind (SetCanonicalBytes), bypassing clamping. This is correct because the Multiply output is already a valid scalar.
func GenerateBlindingFactor ¶
func GenerateBlindingFactor(rand io.Reader) (BlindingFactor, error)
GenerateBlindingFactor generates a random blinding factor using entropy from rand. If rand is nil, crypto/rand.Reader will be used. The output is clamped per Ed25519 convention: low 3 bits cleared, bit 254 set, bit 255 cleared.
type PrivateKey ¶
type PrivateKey []byte
PrivateKey is the type of Ed25519 private keys. It has the same layout as crypto/ed25519: 32-byte seed followed by 32-byte public key (64 bytes total).
Blinded private keys use an extended 96-byte format: scalar(32) || nonce_prefix(32) || pubkey(32). These must only be produced by BlindPrivateKey; manually constructing a 96-byte PrivateKey with invalid scalar bytes will cause Sign to panic.
func BlindPrivateKey ¶
func BlindPrivateKey(priv PrivateKey, blind BlindingFactor) (PrivateKey, error)
BlindPrivateKey derives a blinded private key by multiplying the private scalar a by the blinding factor b: a' = a · b mod ℓ. The resulting key can be used with Sign to produce signatures verifiable against the corresponding blinded public key (from BlindPublicKey).
Invariant: BlindPublicKey(priv.Public(), bf) == BlindPrivateKey(priv, bf).Public()
The blinded key uses an extended 96-byte internal format so that Sign can detect it and use the pre-computed scalar directly (no SHA-512 re-expansion). The nonce prefix for deterministic signing is derived as SHA-512(0xFF || blind || original_prefix) for domain separation.
func NewKeyFromSeed ¶
func NewKeyFromSeed(seed []byte) PrivateKey
NewKeyFromSeed calculates a private key from a seed. Panics if len(seed) is not SeedSize. This function is provided for interoperability with RFC 8032; for most uses, GenerateKey should be preferred.
func (PrivateKey) Equal ¶
func (priv PrivateKey) Equal(x crypto.PrivateKey) bool
Equal reports whether priv and x have the same value. x must be of type PrivateKey; if not, Equal returns false. The comparison is constant-time.
func (PrivateKey) IsBlinded ¶
func (priv PrivateKey) IsBlinded() bool
IsBlinded reports whether priv is a blinded key (produced by BlindPrivateKey). Blinded keys use a 96-byte internal format and have different semantics for PrivateKey.Seed and PrivateKey.Scalar.
func (PrivateKey) Public ¶
func (priv PrivateKey) Public() crypto.PublicKey
Public returns the PublicKey corresponding to priv. Works for both normal (64-byte) and blinded (96-byte) private keys. The returned value has underlying type PublicKey.
func (PrivateKey) Scalar ¶
func (priv PrivateKey) Scalar() []byte
Scalar returns the private scalar as a 32-byte canonical encoding. For blinded keys (produced by BlindPrivateKey), this is the stored blinded scalar a' = a·b mod ℓ. For normal keys, this is derived by expanding the seed via SHA-512 and clamping, matching the Ed25519 key derivation procedure.
The returned scalar is the secret signing exponent. Handle it with the same care as the private key itself.
func (PrivateKey) Seed ¶
func (priv PrivateKey) Seed() []byte
Seed returns the private key seed (the first 32 bytes). For normal keys, this can be used with NewKeyFromSeed to regenerate the key.
WARNING: For blinded keys (produced by BlindPrivateKey), the first 32 bytes contain the raw scalar, not a seed. Calling NewKeyFromSeed with a blinded key's Seed will NOT recreate the blinded key — it will produce an unrelated normal key. Use PrivateKey.IsBlinded to check, and PrivateKey.Scalar to retrieve the blinded scalar explicitly.
func (PrivateKey) Sign ¶
func (priv PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error)
Sign signs the given message with priv, implementing crypto.Signer. Ed25519 performs two passes over messages to be signed and therefore cannot handle pre-hashed messages. Thus opts.HashFunc() must return zero to indicate the message hasn't been hashed. This can be achieved by passing crypto.Hash(0) as the value for opts.
type PublicKey ¶
type PublicKey []byte
PublicKey is the type of Ed25519 public keys (32-byte compressed point).
func BlindPublicKey ¶
func BlindPublicKey(pub PublicKey, blind BlindingFactor) (PublicKey, error)
BlindPublicKey derives a blinded public key by multiplying the public key point A by the blinding factor scalar b: A' = b · A. The result is unlinkable to the original public key without knowledge of the blinding factor.
BlindPublicKey rejects pure-torsion (small-order) input points but accepts mixed-order points (points with both a prime-order and a torsion component). For a mixed-order public key, the invariant
BlindPublicKey(pub, bf) == BlindPrivateKey(priv, bf).Public()
may not hold because BlindPrivateKey always produces a pure prime-order public key (via ScalarBaseMult). This is not a practical concern because all legitimate Ed25519 public keys (from GenerateKey, NewKeyFromSeed, or standard crypto/ed25519) are pure prime-order points.