Class: WOTS::Param

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/wots/param.rb,
lib/wots/param/sha256.rb,
lib/wots/param/sha512.rb,
lib/wots/param/shake256.rb

Overview

WOTS+ parameters defined in rfc8391. https://datatracker.ietf.org/doc/html/rfc8391

Constant Summary collapse

SUPPORTED_NAMES =

The parameter sets defined in RFC 8391 and the n they require.

{
  'WOTSP-SHA2_256' => 32,
  'WOTSP-SHA2_512' => 64,
  'WOTSP-SHAKE_256' => 32
}.freeze
SUPPORTED_W =

The Winternitz parameter is a member of the set 16. Other values break the checksum encoding below and allow signature forgery.

[4, 16].freeze
SHA256 =
new(
  name: 'WOTSP-SHA2_256',
  n: 32,
  w: 16
)
SHA512 =
new(
  name: 'WOTSP-SHA2_512',
  n: 64,
  w: 16
)
SHAKE256 =
new(
  name: 'WOTSP-SHAKE_256',
  n: 32,
  w: 16
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util

#bin_to_hex, #hex_string?, #hex_to_bin

Constructor Details

#initialize(opts) ⇒ Param

public key, or signature element in bytes.

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :name (String)

    the name of the parameter set; it is a member of SUPPORTED_NAMES.

  • :n (Integer)

    the message length as well as the length of a private key,

  • :w (Integer)

    the Winternitz parameter; it is a member of the set 16.

Raises:

  • ArgumentError



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/wots/param.rb', line 34

def initialize(opts)
  raise ArgumentError, 'name must be string.' unless opts[:name].is_a?(String)
  raise ArgumentError, 'n must be integer.' unless opts[:n].is_a?(Integer)
  raise ArgumentError, 'w must be integer.' unless opts[:w].is_a?(Integer)

  expected_n = SUPPORTED_NAMES[opts[:name]]
  raise ArgumentError, "name must be one of #{SUPPORTED_NAMES.keys.join(', ')}." if expected_n.nil?
  raise ArgumentError, "n must be #{expected_n} for #{opts[:name]}." unless opts[:n] == expected_n
  raise ArgumentError, "w must be one of #{SUPPORTED_W.join(', ')}." unless SUPPORTED_W.include?(opts[:w])

  @name = opts[:name]
  @n = opts[:n]
  @w = opts[:w]
end

Instance Attribute Details

#nObject (readonly)

Returns the value of attribute n.



25
26
27
# File 'lib/wots/param.rb', line 25

def n
  @n
end

#nameObject (readonly)

Returns the value of attribute name.



24
25
26
# File 'lib/wots/param.rb', line 24

def name
  @name
end

#wObject (readonly)

Returns the value of attribute w.



26
27
28
# File 'lib/wots/param.rb', line 26

def w
  @w
end

Instance Method Details

#==(other) ⇒ Object



178
179
180
181
# File 'lib/wots/param.rb', line 178

def ==(other)
  return false unless other.is_a?(Param)
  name == other.name && n == other.n && w == other.w
end

#base_w(data, out_len) ⇒ Array

Convert data as base w representation.

Parameters:

  • data (String)

    The data to be converted. Hex string.

  • out_len (Integer)

    Output length.

Returns:

  • (Array)

    An array of integer.

Raises:

  • ArgumentError If data is too short to produce out_len digits.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/wots/param.rb', line 101

def base_w(data, out_len)
  x = hex_to_bin(data)
  raise ArgumentError, 'out_len must be integer.' unless out_len.is_a?(Integer)
  raise ArgumentError, 'out_len must be positive.' unless out_len.positive?

  required = ((out_len * lg_w) / 8.0).ceil
  raise ArgumentError, "data must be at least #{required} bytes to produce #{out_len} digits." if x.bytesize < required

  basew = []
  in_idx = 0
  total = 0
  bits = 0

  out_len.times do
    if bits == 0
      total = x.getbyte(in_idx)
      in_idx += 1
      bits += 8
    end

    bits -= lg_w
    basew << ((total >> bits) & (w - 1))
  end

  basew
end

#chain(x, start_idx, steps, seed, addr) ⇒ String

WOTS+ Chaining Function.

Parameters:

  • x (String)

    Input string. Hex string.

  • start_idx (Integer)

    Start index.

  • steps (Integer)

    Number of steps.

  • seed (String)

    Seed. Hex string.

  • addr (WOTS::Address)

    Address.

Returns:

  • (String)

    Hex string.

Raises:

  • ArgumentError

See Also:



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/wots/param.rb', line 150

def chain(x, start_idx, steps, seed, addr)
  raise ArgumentError, 'x must be hex string.' unless hex_string?(x)
  raise ArgumentError, 'start_idx must be integer.' unless start_idx.is_a?(Integer)
  raise ArgumentError, 'steps must be integer.' unless steps.is_a?(Integer)
  raise ArgumentError, 'start_idx must not be negative.' if start_idx.negative?
  raise ArgumentError, 'steps must not be negative.' if steps.negative?
  raise ArgumentError, 'Invalid range' if (start_idx + steps) > (w - 1)

  return x if steps == 0

  result = x.dup

  steps.times do |i|
    addr.hash_addr = (start_idx + i)

    addr.key_and_mask = 0
    key = prf(seed, bin_to_hex(addr.to_payload))

    addr.key_and_mask = 1
    mask = prf(seed, bin_to_hex(addr.to_payload))

    masked = xor_bytes(result, mask)
    result = f(key, masked)
  end

  result
end

#compute_checksum(base_w) ⇒ String

Compute checksum for base_w.

Parameters:

  • base_w (Array)

Returns:

  • (String)

    Checksum hex string.



131
132
133
134
135
136
137
138
139
# File 'lib/wots/param.rb', line 131

def compute_checksum(base_w)
  c_sum = 0
  len1.times do |i|
    c_sum = (c_sum + w - 1 - base_w[i])
  end
  c_sum = (c_sum << (8 - ((len2 * lg_w) % 8)))
  len_2_bytes = ((len2 * lg_w) / 8.0).ceil
  bin_to_hex(to_byte(c_sum, len_2_bytes))
end

#f(k, m) ⇒ String

Returns Hex string.

Parameters:

  • k (String)

    Hex string.

  • m (String)

    Hex string.

Returns:

  • (String)

    Hex string.



70
71
72
# File 'lib/wots/param.rb', line 70

def f(k, m)
  keyed_hash(0, k, m)
end

#h(k, m) ⇒ String

Returns Hex string.

Parameters:

  • k (String)

    Hex string.

  • m (String)

    Hex string.

Returns:

  • (String)

    Hex string.



77
78
79
# File 'lib/wots/param.rb', line 77

def h(k, m)
  keyed_hash(1, k, m)
end

#h_msg(k, m) ⇒ String

Returns Hex string.

Parameters:

  • k (String)

    Hex string.

  • m (String)

    Hex string.

Returns:

  • (String)

    Hex string.



84
85
86
# File 'lib/wots/param.rb', line 84

def h_msg(k, m)
  keyed_hash(2, k, m)
end

#lenObject



63
64
65
# File 'lib/wots/param.rb', line 63

def len
  @len ||= len1 + len2
end

#len1Object



55
56
57
# File 'lib/wots/param.rb', line 55

def len1
  @len1 ||= (8.0 * n / Math.log2(w)).ceil
end

#len2Object



59
60
61
# File 'lib/wots/param.rb', line 59

def len2
  @len2 ||= (Math.log2(len1 * (w - 1)) / Math.log2(w)).floor + 1
end

#lg_wInteger

log2(w), i.e. the number of bits consumed by a single base w digit.

Returns:

  • (Integer)


51
52
53
# File 'lib/wots/param.rb', line 51

def lg_w
  @lg_w ||= Math.log2(w).to_i
end

#prf(k, m) ⇒ String

PRF function.

Parameters:

  • k (String)

    key Hex string.

  • m (String)

    message Hex string.

Returns:

  • (String)

    Hex string.



92
93
94
# File 'lib/wots/param.rb', line 92

def prf(k, m)
  keyed_hash(3, k, m)
end

#to_byte(value, length) ⇒ String

Convert value to length size binary string.

Parameters:

  • value (Integer)
  • length (Integer)

Returns:

  • (String)

    Binary string.

Raises:

  • ArgumentError If value does not fit in length bytes.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/wots/param.rb', line 188

def to_byte(value, length)
  raise ArgumentError, 'value must be integer.' unless value.is_a?(Integer)
  raise ArgumentError, 'length must be integer.' unless length.is_a?(Integer)
  raise ArgumentError, 'value must not be negative.' if value.negative?
  raise ArgumentError, "value does not fit in #{length} bytes." if value.bit_length > length * 8

  bytes = []

  length.times do
    bytes.unshift(value & 0xFF)
    value >>= 8
  end

  bytes.pack("C*")
end