class Ciri::P2P::RLPX::EncryptionHandshake

handle key exchange handshake

Attributes

initiator_nonce[R]
private_key[R]
receiver_nonce[R]
remote_id[R]
remote_key[R]
remote_random_key[R]

Public Class Methods

new(private_key:, remote_id:) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 48
def initialize(private_key:, remote_id:)
  @private_key = private_key
  @remote_id = remote_id
end

Public Instance Methods

auth_ack_msg() click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 85
def auth_ack_msg
  # make nonce bytes
  nonce = random_nonce(SHA_LENGTH)
  @receiver_nonce = nonce
  random_pubkey = random_key.raw_public_key[1..-1]
  AuthRespV4.new(random_pubkey: random_pubkey, nonce: nonce, version: 4)
end
auth_msg() click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 61
def auth_msg
  # make nonce bytes
  nonce = random_nonce(SHA_LENGTH)
  @initiator_nonce = nonce
  # remote first byte tag
  token = dh_compute_key(private_key, remote_key)
  raise StandardError.new("token size #{token.size} not correct") if token.size != nonce.size
  # xor
  signed = xor(token, nonce)

  signature = random_key.ecdsa_signature(signed).signature
  initiator_pubkey = private_key.raw_public_key[1..-1]
  AuthMsgV4.new(signature: signature, initiator_pubkey: initiator_pubkey, nonce: nonce, version: 4)
end
extract_secrets(auth_packet, auth_ack_packet, initiator:) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 99
def extract_secrets(auth_packet, auth_ack_packet, initiator:)
  secret = dh_compute_key(random_key, remote_random_key)
  shared_secret = Ciri::Utils.keccak(secret, Ciri::Utils.keccak(receiver_nonce, initiator_nonce))
  aes_secret = Ciri::Utils.keccak(secret, shared_secret)
  mac = Ciri::Utils.keccak(secret, aes_secret)
  secrets = Secrets.new(remote_id: remote_id, aes: aes_secret, mac: mac)

  # initial secrets macs
  mac1 = Digest::SHA3.new(256)
  mac1.update xor(mac, receiver_nonce)
  mac1.update auth_packet

  mac2 = Digest::SHA3.new(256)
  mac2.update xor(mac, initiator_nonce)
  mac2.update auth_ack_packet

  if initiator
    secrets.egress_mac = mac1
    secrets.ingress_mac = mac2
  else
    secrets.egress_mac = mac2
    secrets.ingress_mac = mac1
  end
  secrets
end
handle_auth_ack_msg(msg) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 93
def handle_auth_ack_msg(msg)
  # make nonce bytes
  @receiver_nonce = msg.nonce
  @remote_random_key = Ciri::Key.new(raw_public_key: "\x04" + msg.random_pubkey)
end
handle_auth_msg(msg) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 76
def handle_auth_msg(msg)
  @remote_key = Ciri::Key.new(raw_public_key: "\x04" + msg.initiator_pubkey)
  @initiator_nonce = msg.nonce

  token = dh_compute_key(private_key, @remote_key)
  signed = xor(token, msg.nonce)
  @remote_random_key = Ciri::Key.ecdsa_recover(signed, msg.signature)
end
random_key() click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 57
def random_key
  @random_key ||= Ciri::Key.random
end

Private Instance Methods

dh_compute_key(private_key, public_key) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 127
def dh_compute_key(private_key, public_key)
  private_key.ec_key.dh_compute_key(public_key.ec_key.public_key)
end
random_nonce(size) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 135
def random_nonce(size)
  size.times.map {rand(8)}.pack('c*')
end
xor(b1, b2) click to toggle source
# File lib/ciri/p2p/rlpx/encryption_handshake.rb, line 131
def xor(b1, b2)
  b1.each_byte.with_index.map {|b, i| b ^ b2[i].ord}.pack('c*')
end