Module: KeyPairs::Ed25519

Defined in:
lib/key-pairs/ed25519.rb

Overview

Ed25519 implementation for XRPL key pairs.

Class Method Summary collapse

Class Method Details

.derive_key_pair(seed) ⇒ Hash

Derives a key pair from a seed.

Parameters:

  • seed (Array<Integer>)

    16 bytes of seed entropy.

Returns:

  • (Hash)

    A hash containing :public_key and :private_key (hex strings).



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/key-pairs/ed25519.rb', line 11

def self.derive_key_pair(seed)
  # XRPL Ed25519 uses the SHA512 of the 16-byte seed as the 32-byte entropy for the signing key.
  seed_bytes = seed.is_a?(Array) ? seed.pack('C*') : seed
  hash = Digest::SHA512.digest(seed_bytes)[0...32]
  signing_key = ::Ed25519::SigningKey.new(hash)
  
  # Public key in XRPL is 0xED followed by 32 bytes of public key.
  public_key = [0xED].pack('C') + signing_key.verify_key.to_bytes
  
  # Private key in XRPL is the 32-byte hash.
  {
    public_key: public_key.unpack1('H*').upcase,
    private_key: hash.unpack1('H*').upcase
  }
end

.sign(message, private_key) ⇒ String

Signs a message with a private key.

Parameters:

  • message (Array<Integer>, String)

    The message to sign.

  • private_key (String)

    The private key (32-byte hash as hex).

Returns:

  • (String)

    The signature (hex string).



31
32
33
34
35
36
37
38
# File 'lib/key-pairs/ed25519.rb', line 31

def self.sign(message, private_key)
  msg_bytes = message.is_a?(String) ? [message].pack('H*') : message.pack('C*')
  key_bytes = [private_key].pack('H*')
  signing_key = ::Ed25519::SigningKey.new(key_bytes)
  
  signature = signing_key.sign(msg_bytes)
  signature.unpack1('H*').upcase
end

.verify(message, signature, public_key) ⇒ Boolean

Verifies a signature.

Parameters:

  • message (Array<Integer>, String)

    The message.

  • signature (String)

    The signature (hex string).

  • public_key (String)

    The public key (33-byte hex, starts with ED).

Returns:

  • (Boolean)

    True if the signature is valid.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/key-pairs/ed25519.rb', line 45

def self.verify(message, signature, public_key)
  msg_bytes = message.is_a?(String) ? hex_to_bin(message) : message.pack('C*')
  sig_bytes = [signature].pack('H*')
  
  # Strip the 0xED prefix from the public key
  pub_bytes = [public_key].pack('H*')
  if pub_bytes[0].ord != 0xED
    raise ArgumentError, "Invalid Ed25519 public key prefix"
  end
  
  verify_key = ::Ed25519::VerifyKey.new(pub_bytes[1..-1])
  begin
    verify_key.verify(sig_bytes, msg_bytes)
    true
  rescue ::Ed25519::VerifyError
    false
  end
end