Red25519 Signature Scheme

Re-randomizable signature scheme for creating blinded Destinations

Overview

This document specifies a re-randomizable signature scheme suitable for creating Destinations that can be blinded. It additionally can be used to blind existing Ed25519 Destinations, with a slight reduction in efficacy.

Red25519 has been fully operational in I2P routers since version 0.9.39 (released March 21, 2019). This specification was finalized in version 0.9.47 (August 2020) after 17 months of production deployment. The signature scheme operates as signature type 11 (RedDSA_SHA512_Ed25519) in the I2P network.

Motivation

Proposal 123 (New netDB Entries) defines an encrypted LeaseSet2 format that embodies the Principle of Least Authority: each network participant is given only the information necessary for their role. In particular, an encrypted LeaseSet2 published to a floodfill does not reveal the Destination it is for, and the Leases can only be viewed by someone with prior knowledge of the Destination. However, floodfills still need to be able to authenticate the encrypted LeaseSet2s when they are published, and clients need to additionally ensure that the authentication was enforced by the Destination itself.

Proposal 123 achieves this by blinding the signing keys of Destinations. The blinded keys can be used to create signatures that are verifiable by floodfills, and clients can be certain that only the Destination could have created the signatures. It is therefore necessary to specify a signature scheme that can be used for blinding.

Note on Proposal 123 Status: Portions of Proposal 123 have been implemented and deployed progressively since version 0.9.38, with Red25519 support added in 0.9.39. Encrypted LeaseSet2 functionality is production-ready and actively used in the I2P network for privacy-enhanced hidden services.

Design

Core signature scheme

The signature scheme specified here, Red25519, is an instantiation of RedDSA as defined in Section 5.4.6 of the Zcash Protocol Specification (Sapling and later). RedDSA is a Schnorr-based signature scheme that supports key re-randomization. It has the following functions:

GENERATE_PRIVATE()
Returns a uniformly-random private key.
DERIVE_PUBLIC(sk)
Returns the public key corresponding to the given private key.
GENERATE_RANDOM()
Returns a random scalar suitable for re-randomizing a keypair.
RANDOMIZE_PRIVATE(sk, alpha)
Re-randomizes a private key, using a secret scalar alpha.
RANDOMIZE_PUBLIC(vk, alpha)
Re-randomizes a public key, using a secret scalar alpha.
SIGN(sk, m)
Returns a signature by the private key sk over the given message m.
VERIFY(vk, m, sig)
Verifies the signature sig against the public key vk and message m. Returns true if the signature is valid, false otherwise.

For a given keypair (sk, vk) the following relationship holds:

RANDOMIZE_PUBLIC(vk, alpha) == DERIVE_PUBLIC(RANDOMIZE_PRIVATE(sk, alpha))

Converting Ed25519 keys to Red25519

Ed25519 keys MAY be transiently one-way converted to Red25519 keys, in order to support re-randomization of existing Ed25519 Destinations. Other sigtypes are not compatible.

We define the following conversion functions:

CONVERT_ED25519_PRIVATE(privkey)
Returns the Red25519 private key corresponding to the given Ed25519 private key.
CONVERT_ED25519_PUBLIC(pubkey)
Returns the Red25519 public key corresponding to the given Ed25519 public key.

For a given Ed25519 keypair (privkey, pubkey) the following relationship holds:

CONVERT_ED25519_PUBLIC(pubkey) == DERIVE_PUBLIC(CONVERT_ED25519_PRIVATE(privkey))

Specification

Definitions

B
The Ed25519 basepoint as in RFC 8032.
L
The Ed25519 order 2^252 + 27742317777372353535851937790883648493 as in RFC 8032.
[s] B
Fixed-base scalar multiplication of the basepoint by s.
[s] A
Variable-base scalar multiplication of A by s.
x || y
Concatenate two byte arrays x and y.

Red25519

The scheme Red25519 specializes RedDSA with:

  • G := the group of points on the Edwards form of Curve25519. In particular, this means that Red25519 uses the prime-order subgroup of order L, and the cofactor h_G is 8.
  • P_G := the Ed25519 basepoint B.
  • l_H := 512
  • H(x) := SHA-512(“I2P_Red25519H(x)” || x)

Note on Hash Function Choice: Red25519 uses SHA-512 instead of BLAKE2b-512 (as used in Zcash RedDSA). This design choice is compensated by the length-prefixing protection described below. I2P Proposal 148 suggests future migration to BLAKE2b-512 for enhanced protection against Duplicate Message Identification (DMI) and Length Extension Attacks (LEA), plus improved performance.

RedDSA assumes that H(x) is instantiated with a cryptographic hash function that is secure against length extension attacks. SHA-512 does not satisfy this on its own. To remedy this, we require that messages are prefixed with a prefix-free encoding of their length:

len_u16(M) || M

where len_u16(M) is the 2-byte representation of the length of M, in little-endian (to be consistent with the little-endian encoding of scalars and points).

Messages must be no longer than 65534 bytes. A length of 65535 is reserved for possible future extensions.

Security Note: The inclusion of the public key (vk) in the hash function, combined with 80 random bytes in signing, ensures protection against SURK-CMA (Strong Unforgeability with Re-randomized Keys under Chosen Message Attack) vulnerabilities discovered in early RedDSA designs. This implementation incorporates the security fixes from the NCC Group Zcash audit (Finding NCC-Zcash2018-009).

Encoding and decoding

Red25519 private keys are scalars mod L, encoded in little-endian representation. We define the functions DECODE_SCALAR and ENCODE_SCALAR for swapping between the byte array and integer forms of a scalar.

Red25519 public keys are points on the Edwards form of Curve25519. They are encoded as the 255-bit little-endian representation of the y-coordinate, followed by a single bit indicating the sign of the x-coordinate. This is the same encoding as for Ed25519. We define the functions DECODE_POINT and ENCODE_POINT for swapping between the byte array and coordinate forms of a point.

RedDSA functions

For ease of implementation, we explicitly write out below the RedDSA functions (as well as several helper functions) already specialized for Red25519. Implementors should refer to section 5.4.6 of the Zcash Protocol Specification for the general specification of the RedDSA functions.

HStar(prefix1, prefix2, m) :=
    h = SHA-512()
    h.input("I2P_Red25519H(x)")
    h.input(prefix1)
    h.input(prefix2)

    h.input(len(m) & 0xff)
    h.input((len(m) >> 8) & 0xff)
    h.input(m)

    s = h.digest()
    return s mod L

GENERATE_PRIVATE :=
    s = 64 random bytes
    return s mod L

DERIVE_PUBLIC(sk) := [sk] B

GENERATE_RANDOM :=
    s = 64 random bytes
    return s mod L

RANDOMIZE_PRIVATE(sk, alpha) := (sk + alpha) mod L

RANDOMIZE_PUBLIC(vk, alpha) := vk + [alpha] B

SIGN(sk, m) :=
    T = 80 random bytes
    vkBytes = ENCODE_POINT(DERIVE_PUBLIC(sk))
    r = HStar(T, vkBytes, m)

    R = [r] B
    Rbytes = ENCODE_POINT(R)

    c = HStar(Rbytes, vkBytes, m)
    S = (r + (c * sk)) mod L

    return Rbytes || ENCODE_SCALAR(S)

VERIFY(vk, m, sig) :=
    Rbytes = sig[0..32]
    Sbytes = sig[32..64]

    R = DECODE_POINT(Rbytes)
    if R is invalid:
        return false

    S = DECODE_SCALAR(Sbytes)
    if S >= L:
        return false

    vkBytes = ENCODE_POINT(vk)
    c = HStar(Rbytes, vkBytes, m)
    return ((-[S] B) + R + ([c] vk)).multiplyByCofactor().isIdentity()

Conversion functions

CONVERT_ED25519_PRIVATE(privkey) :=
    s = SHA-512(privkey)[0..32]
    s[0] = s[0] & 248
    s[31] = (s[31] & 63) | 64
    return s

CONVERT_ED25519_PUBLIC(pubkey) := pubkey

Note that the implementation of CONVERT_ED25519_PRIVATE is equivalent to the computation of the secret scalar s when deriving an Ed25519 public key from an Ed25519 private key, as specified in steps 1-3 from section 5.1.5 of RFC 8032.

Security implications

Re-randomizing a Red25519 Destination and then creating signatures with it does not leak any information about the Destination, because the distribution of Red25519 private keys generated via RANDOMIZE_PRIVATE is identical to the distribution of private keys generated via GENERATE_PRIVATE, and DERIVE_PUBLIC is deterministic.

Converting Ed25519 private keys to Red25519 via CONVERT_ED25519_PRIVATE does not result in the same distribution. However, we consider the reduction in security acceptable for the following reasons:

  • The space of Ed25519 scalars is roughly half the size of the space of Red25519 scalars (there are 2^251 possible Ed25519 scalars, and L ~= 2^252 possible Red25519 scalars). Thus the security loss is at most a factor of roughly 2, or roughly 1 bit (because we could have coincidentally chosen a Red25519 scalar that is also a valid Ed25519 scalar).
  • Existing Ed25519 Destinations have already been historically exposed on the network, and it should be assumed that malicious floodfills have already enumerated them.

Important: Users who are concerned about this security reduction should use Red25519 (signature type 11) as the sigtype for their Destinations instead of Ed25519 (signature type 7).

Note that the above argument does not apply to the re-randomization scalar alpha; information about the key is leaked each time a biased alpha is chosen, because additive re-randomization behaves like a one-time pad.

Security audit status

Important Disclosure: I2P, including Red25519, has never undergone a formal third-party security audit. The underlying RedDSA design underwent security review by NCC Group as part of the Zcash Sapling protocol audit in 2018, where security issues were identified and fixed. However, I2P’s specific instantiation with SHA-512 (instead of BLAKE2b-512) and I2P domain separation has not been independently analyzed.

The ed25519-java reference implementation by str4d underwent one independent third-party audit, but this preceded Red25519 implementation and covered only Ed25519 functionality, not Red25519 extensions.

Users deploying Red25519 should understand this limitation and evaluate the security trade-offs based on their threat model.

Compatibility

I2P versions that support Red25519 (version 0.9.39 and later) will be able to verify network datastructures that are signed with it. I2P versions that do not support Red25519 will treat it as an unknown signature, and MAY drop the datastructures.

Deployment Timeline:

  • 0.9.39 (March 2019): First implementation with Encrypted LS2 support in floodfills
  • 0.9.40 (May 2019): Per-client authorization for Encrypted LS2
  • 0.9.41 (August 2019): Meta LS2 and Encrypted LS2 with offline keys
  • 0.9.43 (February 2020): b32 support for Encrypted LS2
  • 0.9.47 (August 2020): Specification finalization
  • 2.10.0 (October 2025): Current network version (equivalent to 0.9.67+)

Users should expect the reliability of Red25519-signed datastructures to be good for Encrypted LeaseSet2 use cases, as the network has had over six years to upgrade since initial implementation. However, adoption metrics for general destination usage are not publicly available.

Primary Use Case: Red25519 is primarily used for Encrypted LeaseSet2 functionality where key blinding is required. For standard destinations without encrypted leaseset requirements, Ed25519 (signature type 7) remains the recommended choice due to wider compatibility and longer proven track record.

Implementation notes

Red25519 is implemented in the I2P Java router at net.i2p.crypto.eddsa.RedDSAEngine and uses the ed25519-java library by str4d (Jack Grigg) as Maven dependency net.i2p.crypto:eddsa (versions 0.1.0 through 0.3.0).

The i2pd C++ implementation also supports Red25519 (signaturetype=11) for encrypted LeaseSet publishing.

Configuration example for i2pd:

signaturetype=11
i2cp.leaseSetType=5

Encrypted LeaseSet2 Compatibility: The Encrypted LeaseSet specification requires that the signing public key in the unblinded destination must be Ed25519 (signature type 7) or Red25519 (signature type 11). No other signature types are supported for encrypted leaseset functionality.

Test vectors

The following test vectors are provided for implementation validation. Each vector includes:

  • edsk: Ed25519 private key (random)
  • edpk: Ed25519 public key corresponding to edsk
  • sk: CONVERT_ED25519_PRIVATE(edsk)
  • vk: CONVERT_ED25519_PUBLIC(edpk)
  • msg: Message to sign
  • sig: SIGN(sk, msg)
  • alpha: GENERATE_RANDOM()
  • rsk: RANDOMIZE_PRIVATE(sk, alpha)
  • rvk: RANDOMIZE_PUBLIC(vk, alpha)
  • rsig: SIGN(rsk, msg)

Implementation Note: These test vectors should be integrated into automated unit test suites for continuous validation. Implementers should verify all conversions, signatures, and re-randomization operations match these expected values.

Test vector 1

edsk:  0101010101010101010101010101010101010101010101010101010101010101
edpk:  8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c
sk:    58e86efb75fa4e2c410f46e16de9f6acae1a1703528651b69bc176c088bef36e
vk:    8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c
msg:   0202020202020202020202020202020202020202020202020202020202020202
sig:   61f5527f4d3b46de4b2c234390370bf715ae9098907a0d191ba1b44b23a8ac1a
       6a40437a5294e9503faaf9bd2b7f2fe7ba44dec487b3185aba7ff7d7a17cd40f
alpha: ae9ba9cbbc047c442448fca7c9f4e288a202ed520bfad0c784b792b7773cee08
rsk:   8bb85f3c7a494a08890d7d142109c1a3501d04565d80227e2079097800fbe107
rvk:   6fe128737b8e76fa66698a748b0dc0a89168dd8a0601c2b1c0b26835d323e9b3
rsig:  533053074d3b44f08723aab988ede9880a001b7a684d4a98f2d1b88fabee07a5
       b5c9430c69a690321e0cb8365d7aeb6688bcbad2c0780e0c69e8a1b4a45f3001

Test vector 2

edsk:  0202020202020202020202020202020202020202020202020202020202020202
edpk:  8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394
sk:    a83c626bc9c38c8c201878ebb1d5b0b50ac40e8986c78793db1d4ef369fca14e
vk:    8139770ea87d175f56a35466c34c7ecccb8d8a91b4ee37a25df60f5b8fc9b394
msg:   0303030303030303030303030303030303030303030303030303030303030303
sig:   0829e58eb5399870f009bd1f0270264e556424bda7a93fbcec99f6d9d75db46d
       5c3cb546d9947ca7c1200876c8775a90c357a2aef3d2f16388242ee1914b1a0a
alpha: 98b615d9027e996cc2796c019d9c8beb46aa7d2b6eea2e5d98eb29eb1584c203
rsk:   9fcfaa734852ca40b3810ebef590e138516e8cb4f4b1b6f0730978de7f806402
rvk:   527e121090158419609e4a0d8de6f7d3271b353a8cd0b8172fe41468ea1e9177
rsig:  9a6961f35ed264a946cd6214b2326a6e6caa426c2a61bc14367fd278e0b5fb51
       3ac065a69210a457f17d12ba8a496cfd835002691affa8efcdecae48135c090f

Note: Additional test vectors 3-10 follow the same format and can be found in the reference implementation.

References

Was this page helpful?