Module: Bitcoin::BIP324

Defined in:
lib/bitcoin/bip324.rb,
lib/bitcoin/bip324/cipher.rb,
lib/bitcoin/bip324/fs_chacha20.rb,
lib/bitcoin/bip324/ell_swift_pubkey.rb,
lib/bitcoin/bip324/fs_chacha_poly1305.rb

Overview

Defined Under Namespace

Modules: ChaCha20 Classes: Cipher, EllSwiftPubkey, Error, FSChaCha20, FSChaCha20Poly1305, InvalidEllSwiftKey, InvalidPaketLength, Poly1305, TooLargeContent

Constant Summary collapse

FIELD_SIZE =
2**256 - 2**32 - 977
FIELD =
ECDSA::PrimeField.new(FIELD_SIZE)
REKEY_INTERVAL =

packets

224
MINUS_3_SQRT =
sqrt(FIELD.mod(-3))

Class Method Summary collapse

Class Method Details

.ellswift_ecdh_xonly(ellswift_theirs, priv_key) ⇒ String

Compute x coordinate of shared ECDH point between ellswift_theirs and priv_key.

Parameters:

Returns:

  • (String)

    x coordinate of shared ECDH point with hex format.

Raises:

  • ArgumentError



112
113
114
115
116
117
118
119
120
121
# File 'lib/bitcoin/bip324.rb', line 112

def ellswift_ecdh_xonly(ellswift_theirs, priv_key)
  raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
  d = priv_key.hex
  x = ellswift_theirs.decode.to_point.x
  field = BIP324::FIELD
  y = BIP324.sqrt(field.mod(field.power(x, 3) + 7))
  return nil unless y
  point = ECDSA::Point.new(ECDSA::Group::Secp256k1, x, y) * d
  ECDSA::Format::FieldElementOctetString.encode(point.x, point.group.field).bth
end

.sqrt(n) ⇒ Object



23
24
25
26
27
# File 'lib/bitcoin/bip324.rb', line 23

def sqrt(n)
  candidate = FIELD.power(n, (FIELD.prime + 1) / 4)
  return nil unless FIELD.square(candidate) == n
  candidate
end

.v2_ecdh(priv_key, ellswift_theirs, ellswift_ours, initiating) ⇒ String

Compute BIP324 shared secret.

Parameters:

Returns:

  • (String)

    Shared secret with hex format.

Raises:

  • ArgumentError



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/bitcoin/bip324.rb', line 130

def v2_ecdh(priv_key, ellswift_theirs, ellswift_ours, initiating)
  raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
  raise ArgumentError, "ellswift_ours must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_ours.is_a?(Bitcoin::BIP324::EllSwiftPubkey)

  if Bitcoin.secp_impl.is_a?(Bitcoin::Secp256k1::Native)
    Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs, ellswift_ours, priv_key, initiating)
  else
    ecdh_point_x32 = ellswift_ecdh_xonly(ellswift_theirs, priv_key).htb
    content = initiating ? ellswift_ours.key + ellswift_theirs.key + ecdh_point_x32 :
                ellswift_theirs.key + ellswift_ours.key + ecdh_point_x32
    Bitcoin.tagged_hash('bip324_ellswift_xonly_ecdh', content).bth
  end
end

.xelligatorswift(x) ⇒ String

Given a field element X on the curve, find (u, t) that encode them.

Parameters:

  • x (String)

    coordinate with hex format.

Returns:

  • (String)

    ElligatorSwift public key with hex format.



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bitcoin/bip324.rb', line 95

def xelligatorswift(x)
  loop do
    u = SecureRandom.random_number(1..ECDSA::Group::Secp256k1.order).to_s(16)
    c = Random.rand(0..8)
    t = xswiftec_inv(x, u, c)
    unless t.nil?
      return (ECDSA::Format::IntegerOctetString.encode(u.hex, 32) +
        ECDSA::Format::IntegerOctetString.encode(t.hex, 32)).bth
    end
  end
end

.xswiftec(u, t) ⇒ String

Decode field elements (u, t) to an X coordinate on the curve.

Parameters:

  • u (String)

    u of ElligatorSwift encoding with hex format.

  • t (String)

    t of ElligatorSwift encoding with hex format.

Returns:

  • (String)

    x coordinate with hex format.

Raises:

  • (ArgumentError)


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/bitcoin/bip324.rb', line 35

def xswiftec(u, t)
  u = FIELD.mod(u.hex)
  t = FIELD.mod(t.hex)
  u = 1 if u == 0
  t = 1 if t == 0
  t = FIELD.mod(2 * t) if FIELD.mod(FIELD.power(u, 3) + FIELD.power(t, 2) + 7) == 0
  x = FIELD.mod(FIELD.mod(FIELD.power(u, 3) + 7 - FIELD.power(t, 2)) * FIELD.inverse(2 * t))
  y = FIELD.mod((x + t) * FIELD.inverse(MINUS_3_SQRT * u))
  x1 = FIELD.mod(u + 4 * FIELD.power(y, 2))
  x2 = FIELD.mod(FIELD.mod(FIELD.mod(-x) * FIELD.inverse(y) - u) * FIELD.inverse(2))
  x3 = FIELD.mod(FIELD.mod(x * FIELD.inverse(y) - u) * FIELD.inverse(2))
  [x1, x2, x3].each do |x|
    unless ECDSA::Group::Secp256k1.solve_for_y(x).empty?
      return ECDSA::Format::IntegerOctetString.encode(x, 32).bth
    end
  end
  raise ArgumentError, 'Decode failed.'
end

.xswiftec_inv(x, u, c) ⇒ String

Inverse map for ElligatorSwift. Given x and u, find t such that xswiftec(u, t) = x, or return nil.

Parameters:

  • x (String)

    x coordinate with hex format

  • u (String)

    u of ElligatorSwift encoding with hex format

  • c (Integer)

    Case selects which of the up to 8 results to return.

Returns:

  • (String)

    Inverse of xswiftec(u, t) with hex format or nil.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/bitcoin/bip324.rb', line 59

def xswiftec_inv(x, u, c)
  x = FIELD.mod(x.hex)
  u = FIELD.mod(u.hex)
  if c & 2 == 0
    return nil unless ECDSA::Group::Secp256k1.solve_for_y(FIELD.mod(-x - u)).empty?
    v = x
    s = FIELD.mod(
      -FIELD.mod(FIELD.power(u, 3) + 7) *
        FIELD.inverse(FIELD.mod(FIELD.power(u, 2) + u * v + FIELD.power(v, 2))))
  else
    s = FIELD.mod(x - u)
    return nil if s == 0
    r = sqrt(FIELD.mod(-s * (4 * (FIELD.power(u, 3) + 7) + 3 * s * FIELD.power(u, 2))))
    return nil if r.nil?
    return nil if c & 1 == 1 && r == 0
    v = FIELD.mod(FIELD.mod(-u + r * FIELD.inverse(s)) * FIELD.inverse(2))
  end
  w = sqrt(s)
  return nil if w.nil?
  result = if c & 5 == 0
             FIELD.mod(-w * FIELD.mod(u * (1 - MINUS_3_SQRT) * FIELD.inverse(2) + v))
           elsif c & 5 == 1
             FIELD.mod(w * FIELD.mod(u * (1 + MINUS_3_SQRT) * FIELD.inverse(2) + v))
           elsif c & 5 == 4
             FIELD.mod(w * FIELD.mod(u * (1 - MINUS_3_SQRT) * FIELD.inverse(2) + v))
           elsif c & 5 == 5
             FIELD.mod(-w * FIELD.mod(u * (1 + MINUS_3_SQRT) * FIELD.inverse(2) + v))
           else
             return nil
             end
  ECDSA::Format::IntegerOctetString.encode(result, 32).bth
end