Class: KeyPairs::KeyPairs

Inherits:
Object
  • Object
show all
Defined in:
lib/key-pairs/key_pairs.rb

Overview

Main entry point for XRPL key pair operations.

Instance Method Summary collapse

Constructor Details

#initializeKeyPairs

Returns a new instance of KeyPairs.



8
9
10
# File 'lib/key-pairs/key_pairs.rb', line 8

def initialize
  @address_codec = AddressCodec::AddressCodec.new
end

Instance Method Details

#derive_address(public_key) ⇒ String

Derives an XRP address from a public key.

Parameters:

  • public_key (String)

    The public key as hex.

Returns:

  • (String)

    The XRP address.



81
82
83
84
85
86
87
88
89
90
# File 'lib/key-pairs/key_pairs.rb', line 81

def derive_address(public_key)
  public_key_bytes = [public_key].pack('H*')
  # Account ID is RIPEMD160(SHA256(public_key))
  sha256 = Digest::SHA256.digest(public_key_bytes)
  # Ruby doesn't have RIPEMD160 in Digest by default sometimes, 
  # but OpenSSL has it.
  ripemd160 = OpenSSL::Digest.new('RIPEMD160').digest(sha256)
  
  @address_codec.(ripemd160.unpack('C*'))
end

#derive_key_pair(seed, options = {}) ⇒ Hash

Derives a key pair from an encoded seed.

Parameters:

  • seed (String)

    The encoded seed string.

  • options (Hash) (defaults to: {})

    Options including :account_index (for secp256k1).

Returns:

  • (Hash)

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



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/key-pairs/key_pairs.rb', line 25

def derive_key_pair(seed, options = {})
  decoded = @address_codec.decode_seed(seed)
  type = decoded[:type]
  entropy = decoded[:bytes]

  if type == 'ed25519'
    Ed25519.derive_key_pair(entropy)
  else
    # For secp256k1, we use the entropy as seed
    Secp256k1.derive_key_pair(entropy)
  end
end

#generate_seed(entropy = nil, type = 'secp256k1') ⇒ String

Generates a new seed.

Parameters:

  • entropy (Array<Integer>, nil) (defaults to: nil)

    16 bytes of entropy.

  • type (String) (defaults to: 'secp256k1')

    The seed type ('secp256k1' or 'ed25519').

Returns:

  • (String)

    The encoded seed string.



16
17
18
19
# File 'lib/key-pairs/key_pairs.rb', line 16

def generate_seed(entropy = nil, type = 'secp256k1')
  entropy ||= SecureRandom.random_bytes(16).bytes
  @address_codec.encode_seed(entropy, type)
end

#sign(message, private_key, algorithm = nil) ⇒ String

Signs a message with a private key.

Parameters:

  • message (String)

    The message to sign as hex.

  • private_key (String)

    The private key as hex.

  • algorithm (String, nil) (defaults to: nil)

    The algorithm to use ('secp256k1' or 'ed25519').

Returns:

  • (String)

    The signature as hex.



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

def sign(message, private_key, algorithm = nil)
  if algorithm == 'ed25519' || (algorithm.nil? && private_key.length == 64)
    # Heuristic: Ed25519 private keys in our lib are 32 bytes (64 hex chars).
    # Secp256k1 are also 32 bytes, but Ed25519 is often explicitly requested.
    # Actually, let's look at the prefix of the public key if we had it.
    # Since we don't have the public key here, we rely on the caller or length.
    # In XRPL-Ruby, Ed25519 private keys are 64 hex chars (32 bytes).
    # Secp256k1 are also 64 hex chars. This is ambiguous!
    # Let's try to see if it's explicitly 'ed25519'.
    begin
      return Ed25519.sign(message, private_key) if algorithm == 'ed25519'
      return Secp256k1.sign(message, private_key)
    rescue => e
      # If secp fails and we didn't specify, maybe it was ed? 
      # But that's dangerous.
      raise e
    end
  else
    Secp256k1.sign(message, private_key)
  end
end

#verify(message, signature, public_key) ⇒ Boolean

Verifies a signature.

Parameters:

  • message (String)

    The message as hex.

  • signature (String)

    The signature as hex.

  • public_key (String)

    The public key as hex.

Returns:

  • (Boolean)

    True if the signature is valid.



70
71
72
73
74
75
76
# File 'lib/key-pairs/key_pairs.rb', line 70

def verify(message, signature, public_key)
  if public_key.start_with?('ED')
    Ed25519.verify(message, signature, public_key)
  else
    Secp256k1.verify(message, signature, public_key)
  end
end