Class: RingSig::Hasher

Inherits:
Object
  • Object
show all
Defined in:
lib/ring_sig/hasher.rb

Overview

A customized hasher specifically for Ring Signatures.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(group, algorithm) ⇒ Hasher

Note:

The byte-length of the group's order and the digest method must match, or else signatures generated from this hasher will leak the position of the true signer.

Creates a new instance of RingSig::Hasher.

Parameters:

  • group (ECDSA::Group)
  • algorithm (#digest)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ring_sig/hasher.rb', line 18

def initialize(group, algorithm)
  @group = group
  @algorithm = algorithm

  algorithm_byte_length = algorithm.digest('a').size
  if group.byte_length != algorithm_byte_length
    raise ArgumentError, "Group's byte length (#{group.byte_length}), does not match hash algorithm's byte length (#{algorithm_byte_length})"
  end

  digest_max = 2 ** (algorithm_byte_length * 8) - 1
  if digest_max < group.order
    raise ArgumentError, "Invalid ECDSA group. Group's order must be less than the hash algorithm's maximum value"
  end

  @hash_cieling = digest_max - digest_max % group.order
end

Instance Attribute Details

#algorithm#digest (readonly)

Returns:

  • (#digest)


8
9
10
# File 'lib/ring_sig/hasher.rb', line 8

def algorithm
  @algorithm
end

#groupECDSA::Group (readonly)

Returns:

  • (ECDSA::Group)


5
6
7
# File 'lib/ring_sig/hasher.rb', line 5

def group
  @group
end

Instance Method Details

#==(other) ⇒ Boolean

Returns true if the hashers are equal.

Returns:

  • (Boolean)

    true if the hashers are equal.



96
97
98
# File 'lib/ring_sig/hasher.rb', line 96

def ==(other)
  group == other.group && algorithm == other.algorithm
end

#hash_array(array) ⇒ Integer

Hashes an array. Converts the Array to an OpenSSL::ASN1::Sequence der string, and then hashes that string.

Parameters:

  • array (Array<String,Integer,ECDSA::Point>)

    The array to be hashed.

Returns:

  • (Integer)

    A number between 0 and the group's order.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ring_sig/hasher.rb', line 54

def hash_array(array)
  array = array.map do |e|
    case e
    when String
      OpenSSL::ASN1::UTF8String.new(e)
    when Integer
      OpenSSL::ASN1::Integer.new(e)
    when ECDSA::Point
      OpenSSL::ASN1::OctetString.new(ECDSA::Format::PointOctetString.encode(e, compression: true))
    else
      raise ArgumentError, "Unsupported type: #{p.inspect}"
    end
  end

  hash_string(OpenSSL::ASN1::Sequence.new(array).to_der)
end

#hash_point(point) ⇒ ECDSA::Point

Hashes a point to another point.

Parameters:

  • point (ECDSA::Point)

    The point to be hashed.

Returns:

  • (ECDSA::Point)

    A new point, deterministically computed from the input point.



76
77
78
# File 'lib/ring_sig/hasher.rb', line 76

def hash_point(point)
  @group.generator * hash_array(point.coords)
end

#hash_string(s) ⇒ Integer

Uniformly hashes a string to a number between 0 and the group's order.

Parameters:

  • s (String)

    The string to be hashed.

Returns:

  • (Integer)

    A number between 0 and the group's order.



39
40
41
42
43
44
45
46
47
# File 'lib/ring_sig/hasher.rb', line 39

def hash_string(s)
  n = nil
  loop do
    s = algorithm.digest(s)
    n = s.unpack('H*').first.to_i(16)
    break if n < @hash_cieling
  end
  n % group.order
end

#shuffle(array, seed) ⇒ Array

Shuffles an array in a deterministic manner.

Parameters:

  • array (Array)

    The array to be shuffled.

  • seed (Integer)

    A random seed which determines the outcome of the shuffle.

Returns:

  • (Array)

    The shuffled array.



86
87
88
89
90
91
92
93
# File 'lib/ring_sig/hasher.rb', line 86

def shuffle(array, seed)
  seed_array = [seed, 0]
  (array.size - 1).downto(1) do |i|
    r = next_rand(i + 1, seed_array)
    array[i], array[r] = array[r], array[i]
  end
  array
end