rsa-accumulator 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +9 -4
- data/.ruby-version +1 -1
- data/README.md +19 -5
- data/lib/rsa/acc/functions.rb +80 -15
- data/lib/rsa/acc/poe.rb +11 -2
- data/lib/rsa/acc/poke2.rb +19 -6
- data/lib/rsa/acc/proof.rb +5 -4
- data/lib/rsa/acc/version.rb +1 -1
- data/lib/rsa/accumulator.rb +86 -27
- metadata +3 -7
- data/.travis.yml +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cee011044852d3cda974e2a3b92cf0f762cf08982fc12abd153fe762841f0902
|
|
4
|
+
data.tar.gz: 4db2c478ce578bd09a0a5afad6248f616145116577390cb95a5446fd7eca3081
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad00946ab3cc16554b8013d166807f7265604519373288b49b1ce401037da0f7352215e98a93ef7b180751eb4d6aa3768782da4faf2c814a24c819c6a54fb9a9
|
|
7
|
+
data.tar.gz: d16fb0cab0cc0c715d590eba67a6a18a9bfef80ebb83cdc51632677a2644819570c60a7e80e82672c98635d33fe9015cbbb970f4641ec7e60c432334eb314c7e
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
name: Ruby
|
|
2
2
|
|
|
3
|
-
on:
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
4
9
|
|
|
5
10
|
jobs:
|
|
6
11
|
build:
|
|
@@ -8,11 +13,11 @@ jobs:
|
|
|
8
13
|
runs-on: ubuntu-latest
|
|
9
14
|
strategy:
|
|
10
15
|
matrix:
|
|
11
|
-
ruby: ['
|
|
16
|
+
ruby: ['3.0', '3.1', '3.2', '3.3']
|
|
12
17
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
18
|
+
- uses: actions/checkout@v2
|
|
14
19
|
- name: Set up Ruby environment
|
|
15
|
-
uses:
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
16
21
|
with:
|
|
17
22
|
ruby-version: ${{ matrix.ruby }}
|
|
18
23
|
- name: Build and test with Rake
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ruby-3.0.0
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# RSA Accumulator for Ruby [](https://github.com/chaintope/rsa-accumulatorrb/actions/workflows/ruby.yml) [](https://badge.fury.io/rb/rsa-accumulator) [](LICENSE)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
Cryptographic accumulator based on the strong RSA assumption [BBF18](https://eprint.iacr.org/2018/1188.pdf) in Ruby.
|
|
@@ -23,16 +23,30 @@ Or install it yourself as:
|
|
|
23
23
|
|
|
24
24
|
### Setup accumulator
|
|
25
25
|
|
|
26
|
-
First, initialize the accumulator.
|
|
26
|
+
First, initialize the accumulator. The accumulator works in a group of unknown order, so its
|
|
27
|
+
security depends on **nobody knowing the factorization of the modulus**. There are three ways
|
|
28
|
+
to obtain one:
|
|
27
29
|
|
|
28
30
|
require 'rsa-accumulator'
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
+
|
|
32
|
+
# 1. RSA-2048, the modulus published by RSA Laboratories. Its factorization has never
|
|
33
|
+
# been found, so no trusted setup is required. This is the recommended default.
|
|
31
34
|
acc = RSA::Accumulator.generate_rsa2048
|
|
32
35
|
|
|
33
|
-
#
|
|
36
|
+
# 2. A modulus you obtained elsewhere, e.g. from a multi-party trusted setup ceremony.
|
|
37
|
+
acc = RSA::Accumulator.generate_with_modulus(n)
|
|
38
|
+
|
|
39
|
+
# 3. A freshly generated random RSA modulus with the given bit length (default: 3072).
|
|
34
40
|
acc = RSA::Accumulator.generate_random(2048)
|
|
35
41
|
|
|
42
|
+
> [!WARNING]
|
|
43
|
+
> `generate_random` is a **trusted setup**. It derives the modulus from a newly generated RSA
|
|
44
|
+
> key, so the process that calls it learns `p` and `q`. Anyone holding the factorization can
|
|
45
|
+
> compute an x-th root of any value, and can therefore forge a membership proof for an element
|
|
46
|
+
> that was never added, as well as a non-membership proof for an element that was. Use it only
|
|
47
|
+
> when every verifier of the resulting proofs trusts the party that created the accumulator.
|
|
48
|
+
> Otherwise use `generate_rsa2048` or `generate_with_modulus`.
|
|
49
|
+
|
|
36
50
|
### Adding elements and membership proof
|
|
37
51
|
|
|
38
52
|
You can add arbitrary String data to the accumulator.
|
data/lib/rsa/acc/functions.rb
CHANGED
|
@@ -6,29 +6,54 @@ module RSA
|
|
|
6
6
|
|
|
7
7
|
using RSA::ACC::Ext
|
|
8
8
|
|
|
9
|
+
# Domain separation tags. Elements, Fiat-Shamir challenges and plain hashes are hashed
|
|
10
|
+
# with a distinct tag each, so that a caller supplied element can never be made to
|
|
11
|
+
# produce the same digest as a challenge over group elements.
|
|
12
|
+
ELEMENT_DST = 'RSA-ACC/v1/element'.freeze
|
|
13
|
+
CHALLENGE_DST = 'RSA-ACC/v1/challenge'.freeze
|
|
14
|
+
HASH_DST = 'RSA-ACC/v1/hash'.freeze
|
|
15
|
+
|
|
16
|
+
# Computes the canonical representative of +elem+ in the quotient group Z_n^* / {+-1}.
|
|
17
|
+
# Z_n^* always contains the known order-2 element -1, and the Adaptive Root Assumption
|
|
18
|
+
# which NI-PoE and NI-PoKE2 rely on does not hold in a group with a known low order
|
|
19
|
+
# element. Identifying y with -y removes it, so every group element MUST be normalized
|
|
20
|
+
# before it is hashed into a challenge, compared, or published as part of a proof.
|
|
21
|
+
# @param [Integer] elem an element of Z_n^*.
|
|
22
|
+
# @param [Integer] modulus modulus of the group.
|
|
23
|
+
# @return [Integer] the smaller of +elem+ and -+elem+ modulo +modulus+.
|
|
24
|
+
def normalize(elem, modulus)
|
|
25
|
+
y = elem % modulus
|
|
26
|
+
neg = modulus - y
|
|
27
|
+
y < neg ? y : neg
|
|
28
|
+
end
|
|
29
|
+
|
|
9
30
|
# Convert element to prime number.
|
|
10
|
-
# @param [
|
|
31
|
+
# @param [String] element an element to be converted.
|
|
11
32
|
# @return [Integer] prime number.
|
|
12
33
|
def hash_to_prime(element)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
candidate = candidate.pack('c*').unpack("H*").first.to_i(16)
|
|
18
|
-
if candidate.to_bn.prime?
|
|
19
|
-
return candidate
|
|
20
|
-
end
|
|
21
|
-
nonce += 1
|
|
22
|
-
end
|
|
34
|
+
# Not String#to_s: that would map nil and other objects onto the prime of their
|
|
35
|
+
# string form, so nil and "" would become the same element.
|
|
36
|
+
raise TypeError, "element must be a String, got #{element.class}." unless element.is_a?(String)
|
|
37
|
+
prime_from(ELEMENT_DST, [element])
|
|
23
38
|
end
|
|
24
39
|
|
|
25
40
|
# Converts a list of elements to an product of prime numbers.
|
|
26
41
|
# @param [Array[String]] elements a list of element.
|
|
27
42
|
# @return [Integer] an product of prime numbers
|
|
43
|
+
# @raise [ArgumentError] If +elements+ is not a non-empty list of String.
|
|
28
44
|
def elements_to_prime(elements)
|
|
45
|
+
raise ArgumentError, 'elements must be a non-empty Array of String.' unless valid_elements?(elements)
|
|
29
46
|
elements.map{|e|hash_to_prime(e)}.inject(:*)
|
|
30
47
|
end
|
|
31
48
|
|
|
49
|
+
# Check whether +elements+ can be converted to a product of primes. Verifiers use this
|
|
50
|
+
# to turn a malformed proof into a false result instead of an exception.
|
|
51
|
+
# @param [Object] elements
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def valid_elements?(elements)
|
|
54
|
+
elements.is_a?(Array) && !elements.empty? && elements.all? { |e| e.is_a?(String) }
|
|
55
|
+
end
|
|
56
|
+
|
|
32
57
|
# Computes (xy) th root of g given xth and yth roots of g. x and y is co-prime.
|
|
33
58
|
# (a, b) ← Bezout(x, y)
|
|
34
59
|
#
|
|
@@ -38,10 +63,14 @@ module RSA
|
|
|
38
63
|
# @param [Integer] y
|
|
39
64
|
# @return [Integer] w1^b * w2^a
|
|
40
65
|
def shamir_trick(w1, w2, x, y, modulus)
|
|
41
|
-
|
|
66
|
+
w1 = normalize(w1, modulus)
|
|
67
|
+
w2 = normalize(w2, modulus)
|
|
68
|
+
unless normalize(w1.pow(x, modulus), modulus) == normalize(w2.pow(y, modulus), modulus)
|
|
69
|
+
raise ArgumentError, 'w1^x != w2^y'
|
|
70
|
+
end
|
|
42
71
|
a, b = egcd(x, y)
|
|
43
72
|
raise ArgumentError, 'Inputs does not co-prime.' unless a * x + b * y == 1
|
|
44
|
-
(w1.pow(b, modulus) * w2.pow(a, modulus)
|
|
73
|
+
normalize(w1.pow(b, modulus) * w2.pow(a, modulus), modulus)
|
|
45
74
|
end
|
|
46
75
|
|
|
47
76
|
# Computes Bezout coefficients.
|
|
@@ -59,18 +88,54 @@ module RSA
|
|
|
59
88
|
# @param [Array[Integer]] params
|
|
60
89
|
# @return [Integer] prime number of challenge.
|
|
61
90
|
def compute_challenge(*params)
|
|
62
|
-
|
|
91
|
+
prime_from(CHALLENGE_DST, params.map { |p| even_hex(p) })
|
|
63
92
|
end
|
|
64
93
|
|
|
65
94
|
# Computes hash value from +params+.
|
|
66
95
|
# @param [Array[Integer]] params
|
|
67
96
|
# @return [Integer] hash value.
|
|
68
97
|
def blake2_hash(*params)
|
|
69
|
-
RbNaCl::Hash.blake2b(params.map{|p|even_hex(p)}
|
|
98
|
+
digest = RbNaCl::Hash.blake2b(encode_fields(HASH_DST, params.map { |p| even_hex(p) }))
|
|
99
|
+
digest.unpack("H*").first.to_i(16)
|
|
70
100
|
end
|
|
71
101
|
|
|
72
102
|
private
|
|
73
103
|
|
|
104
|
+
# Serializes +dst+ and +fields+ by prefixing each of them with its byte length, so that
|
|
105
|
+
# the resulting byte string can only be produced by this exact sequence of inputs.
|
|
106
|
+
# A plain concatenation is ambiguous: even_hex is variable length, so for example
|
|
107
|
+
# (0x01, 0x2345) and (0x0123, 0x45) both join to "012345".
|
|
108
|
+
# @param [String] dst domain separation tag.
|
|
109
|
+
# @param [Array[String]] fields fields to be serialized.
|
|
110
|
+
# @return [String] a binary string.
|
|
111
|
+
def encode_fields(dst, fields)
|
|
112
|
+
fields.inject(length_prefixed(dst)) { |buf, field| buf << length_prefixed(field) }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @param [String] data
|
|
116
|
+
# @return [String] +data+ prefixed with its byte length as a 64 bit big endian integer.
|
|
117
|
+
def length_prefixed(data)
|
|
118
|
+
bytes = data.b
|
|
119
|
+
[bytes.bytesize].pack('Q>') << bytes
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Hashes +fields+ under the domain +dst+ repeatedly, incrementing a nonce, until the
|
|
123
|
+
# digest is prime.
|
|
124
|
+
# @param [String] dst domain separation tag.
|
|
125
|
+
# @param [Array[String]] fields fields to be hashed.
|
|
126
|
+
# @return [Integer] prime number.
|
|
127
|
+
def prime_from(dst, fields)
|
|
128
|
+
nonce = 0
|
|
129
|
+
loop do
|
|
130
|
+
digest = RbNaCl::Hash.blake2b(encode_fields(dst, fields + [even_hex(nonce)])).unpack("C*")
|
|
131
|
+
digest[0] |= 0x80 # keep the output width fixed, a leading zero byte would shorten it
|
|
132
|
+
digest[-1] |= 1 # 2 is the only even prime, so only odd candidates are worth testing
|
|
133
|
+
candidate = digest.pack('C*').unpack("H*").first.to_i(16)
|
|
134
|
+
return candidate if candidate.to_bn.prime?
|
|
135
|
+
nonce += 1
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
74
139
|
# Convert +num+ to even hex string.
|
|
75
140
|
# @param [Integer] num
|
|
76
141
|
# @return [String] hex string.
|
data/lib/rsa/acc/poe.rb
CHANGED
|
@@ -17,9 +17,11 @@ module RSA
|
|
|
17
17
|
# @param [Integer] result such as result = base^exp.
|
|
18
18
|
# @param [Integer] modulus modulus using computation.
|
|
19
19
|
def prove(base, exp, result, modulus)
|
|
20
|
+
base = normalize(base, modulus)
|
|
21
|
+
result = normalize(result, modulus)
|
|
20
22
|
l = compute_challenge(base, exp, result)
|
|
21
23
|
q = exp / l
|
|
22
|
-
base.pow(q, modulus)
|
|
24
|
+
normalize(base.pow(q, modulus), modulus)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# Verifies that base^exp = result using the given proof to avoid computation.
|
|
@@ -29,9 +31,16 @@ module RSA
|
|
|
29
31
|
# @param [Integer] proof an proof.
|
|
30
32
|
# @param [Integer] modulus modulus using computation.
|
|
31
33
|
def verify(base, exp, result, proof, modulus)
|
|
34
|
+
# A proof comes from an untrusted prover, so a malformed one is a false result and
|
|
35
|
+
# not an exception.
|
|
36
|
+
return false unless [base, exp, result, proof, modulus].all? { |v| v.is_a?(Integer) }
|
|
37
|
+
return false unless exp.positive? && modulus > 1
|
|
38
|
+
base = normalize(base, modulus)
|
|
39
|
+
result = normalize(result, modulus)
|
|
40
|
+
proof = normalize(proof, modulus)
|
|
32
41
|
l = compute_challenge(base, exp, result)
|
|
33
42
|
r = exp % l
|
|
34
|
-
w = (proof.pow(l, modulus) * base.pow(r, modulus)
|
|
43
|
+
w = normalize(proof.pow(l, modulus) * base.pow(r, modulus), modulus)
|
|
35
44
|
w == result
|
|
36
45
|
end
|
|
37
46
|
|
data/lib/rsa/acc/poke2.rb
CHANGED
|
@@ -43,11 +43,14 @@ module RSA
|
|
|
43
43
|
# @return [RSA::ACC::PoKE2Proof] a proof.
|
|
44
44
|
def prove(base, exp, result, modulus)
|
|
45
45
|
g = RSA::Accumulator::RSA2048_UNKNOWN_ELEM
|
|
46
|
-
|
|
46
|
+
base = normalize(base, modulus)
|
|
47
|
+
result = normalize(result, modulus)
|
|
48
|
+
z = normalize(g.pow(exp, modulus), modulus)
|
|
47
49
|
l = compute_challenge(base, result, z)
|
|
48
50
|
alpha = blake2_hash(base, result, z, l)
|
|
49
51
|
q, r = exp.divmod(l)
|
|
50
|
-
|
|
52
|
+
u = (base * g.pow(alpha, modulus)) % modulus
|
|
53
|
+
RSA::ACC::PoKE2Proof.new(z, normalize(u.pow(q, modulus), modulus), r)
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
# Verifies that the prover knows +exp+ s.t. +base+ ^ +exp+ = +result+
|
|
@@ -57,11 +60,21 @@ module RSA
|
|
|
57
60
|
# @param [Integer] modulus
|
|
58
61
|
# @return [Boolean] Returns true for successful verification, false otherwise.
|
|
59
62
|
def verify(base, result, proof, modulus)
|
|
63
|
+
# A proof comes from an untrusted prover, so a malformed one is a false result and
|
|
64
|
+
# not an exception.
|
|
65
|
+
return false unless proof.is_a?(RSA::ACC::PoKE2Proof)
|
|
66
|
+
return false unless [base, result, modulus, proof.z, proof.q, proof.r].all? { |v| v.is_a?(Integer) }
|
|
67
|
+
return false unless modulus > 1 && !proof.r.negative?
|
|
60
68
|
g = RSA::Accumulator::RSA2048_UNKNOWN_ELEM
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
base = normalize(base, modulus)
|
|
70
|
+
result = normalize(result, modulus)
|
|
71
|
+
z = normalize(proof.z, modulus)
|
|
72
|
+
q = normalize(proof.q, modulus)
|
|
73
|
+
l = compute_challenge(base, result, z)
|
|
74
|
+
alpha = blake2_hash(base, result, z, l)
|
|
75
|
+
u = (base * g.pow(alpha, modulus)) % modulus
|
|
76
|
+
lhs = normalize(q.pow(l, modulus) * u.pow(proof.r, modulus), modulus)
|
|
77
|
+
rhs = normalize(result * z.pow(alpha, modulus), modulus)
|
|
65
78
|
lhs == rhs
|
|
66
79
|
end
|
|
67
80
|
|
data/lib/rsa/acc/proof.rb
CHANGED
|
@@ -20,10 +20,11 @@ module RSA
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Convert element to prime number.
|
|
23
|
-
# @return [Integer] prime number of element.
|
|
23
|
+
# @return [Integer] prime number of element, or nil if the element can not be converted.
|
|
24
24
|
def element_prime
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
elements = element.is_a?(Array) ? element : [element]
|
|
26
|
+
return nil unless valid_elements?(elements)
|
|
27
|
+
elements_to_prime(elements)
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
end
|
|
@@ -33,7 +34,7 @@ module RSA
|
|
|
33
34
|
|
|
34
35
|
attr_reader :d # d = g^b
|
|
35
36
|
attr_reader :v # v = A(current acc)^a
|
|
36
|
-
attr_reader :gv_inv # gv_inv = v^{-1}
|
|
37
|
+
attr_reader :gv_inv # gv_inv = g * v^{-1}. Advisory only: the verifier re-derives this value and does not trust it.
|
|
37
38
|
attr_reader :poke2_proof # NI-PoKE2(A, v, a)
|
|
38
39
|
attr_reader :poe_proof # NI-PoE(d, x, g*v^{-1})
|
|
39
40
|
|
data/lib/rsa/acc/version.rb
CHANGED
data/lib/rsa/accumulator.rb
CHANGED
|
@@ -15,37 +15,73 @@ module RSA
|
|
|
15
15
|
RSA2048_UNKNOWN_ELEM = 2
|
|
16
16
|
|
|
17
17
|
attr_reader :n
|
|
18
|
-
|
|
18
|
+
attr_reader :value
|
|
19
19
|
attr_reader :g # Initial value
|
|
20
20
|
attr_reader :hold_elements # tha flag which indicate hold product of all elements.
|
|
21
21
|
attr_accessor :products # (Optional) product of all elements in Accumulator
|
|
22
22
|
|
|
23
|
-
# Generate accumulator using
|
|
23
|
+
# Generate accumulator using the RSA-2048 challenge modulus.
|
|
24
|
+
# Nobody is known to hold its factorization, so this requires no trusted setup and is
|
|
25
|
+
# the right choice whenever the party running the accumulator is not trusted by the
|
|
26
|
+
# party verifying its proofs.
|
|
24
27
|
# @return [RSA::Accumulator]
|
|
25
28
|
def self.generate_rsa2048(hold_elements: false)
|
|
26
|
-
new(RSA2048_MODULUS, RSA2048_UNKNOWN_ELEM, hold_elements)
|
|
29
|
+
new(RSA2048_MODULUS, RSA2048_UNKNOWN_ELEM, RSA2048_UNKNOWN_ELEM, hold_elements)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Generate accumulator with an externally supplied modulus +n+ of unknown factorization.
|
|
33
|
+
# Use this with a modulus produced by a trusted setup ceremony(e.g. a multi-party
|
|
34
|
+
# computation) when the RSA-2048 modulus is not desired.
|
|
35
|
+
# @param [Integer] n modulus whose factorization is unknown to every participant.
|
|
36
|
+
# @return [RSA::Accumulator]
|
|
37
|
+
def self.generate_with_modulus(n, hold_elements: false)
|
|
38
|
+
raise ArgumentError, 'modulus must be an odd integer greater than 4.' unless n.is_a?(Integer) && n > 4 && n.odd?
|
|
39
|
+
new(n, RSA2048_UNKNOWN_ELEM, RSA2048_UNKNOWN_ELEM, hold_elements)
|
|
27
40
|
end
|
|
28
41
|
|
|
29
42
|
# Generate accumulator with random modulus.
|
|
43
|
+
#
|
|
44
|
+
# WARNING: this is a TRUSTED SETUP. The modulus comes from a freshly generated RSA key,
|
|
45
|
+
# so the caller learns its factorization. Anyone who knows p and q can compute an x-th
|
|
46
|
+
# root of any value and therefore forge a membership proof for an element that was never
|
|
47
|
+
# added, and forge a non-membership proof for an element that was. The factorization is
|
|
48
|
+
# not retained by this method, but it existed in this process, so the resulting
|
|
49
|
+
# accumulator is only sound for verifiers who trust the caller. Use
|
|
50
|
+
# +generate_rsa2048+ or +generate_with_modulus+ when that trust does not hold.
|
|
51
|
+
#
|
|
30
52
|
# @param [Integer] bit_length bit length of accumulator. Default: 3072 bits.
|
|
31
53
|
# @return [RSA::Accumulator]
|
|
32
54
|
def self.generate_random(bit_length = 3072, hold_elements: false)
|
|
33
55
|
n = OpenSSL::PKey::RSA.generate(bit_length).n.to_i
|
|
34
|
-
|
|
56
|
+
initial_value = SecureRandom.random_number(n)
|
|
57
|
+
# Reject the degenerate elements 0, 1 and n - 1, and anything sharing a factor with n.
|
|
58
|
+
until initial_value > 1 && initial_value < n - 1 && initial_value.gcd(n) == 1
|
|
59
|
+
initial_value = SecureRandom.random_number(n)
|
|
60
|
+
end
|
|
61
|
+
new(n, initial_value, initial_value, hold_elements)
|
|
35
62
|
end
|
|
36
63
|
|
|
37
64
|
# Initialize accumulator
|
|
38
65
|
# @param [Integer] n modulus
|
|
39
|
-
# @param [Integer] value
|
|
66
|
+
# @param [Integer] value a value of acc.
|
|
67
|
+
# @param [Integer] initial_acc a value of initial acc.
|
|
40
68
|
# @param [Boolean] hold_elements
|
|
69
|
+
# @param [Integer] products product of all elements in acc, this param is enable only +hold_elements+ set true.
|
|
41
70
|
# @return [RSA::Accumulator]
|
|
42
|
-
def initialize(n, value, hold_elements)
|
|
71
|
+
def initialize(n, value, initial_acc, hold_elements, products = 1)
|
|
43
72
|
@n = n
|
|
44
|
-
|
|
45
|
-
@g =
|
|
73
|
+
self.value = value
|
|
74
|
+
@g = normalize(initial_acc, n)
|
|
46
75
|
@hold_elements = hold_elements
|
|
47
|
-
@products =
|
|
48
|
-
|
|
76
|
+
@products = products if hold_elements
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Set the accumulator value. The value is stored as the canonical representative of the
|
|
80
|
+
# quotient group Z_n^* / {+-1}, so that the known order-2 element -1 cannot be used to
|
|
81
|
+
# break the soundness of NI-PoE and NI-PoKE2.
|
|
82
|
+
# @param [Integer] value a value of acc.
|
|
83
|
+
def value=(value)
|
|
84
|
+
@value = normalize(value, n)
|
|
49
85
|
end
|
|
50
86
|
|
|
51
87
|
# Add element to accumulator and get inclusion proof.
|
|
@@ -55,12 +91,7 @@ module RSA
|
|
|
55
91
|
current_acc = value
|
|
56
92
|
p = elements_to_prime(elements)
|
|
57
93
|
self.value = value.pow(p, n)
|
|
58
|
-
if hold_elements
|
|
59
|
-
elements.each do |e|
|
|
60
|
-
p = hash_to_prime(e)
|
|
61
|
-
self.products *= p unless products.modulo(p) == 0
|
|
62
|
-
end
|
|
63
|
-
end
|
|
94
|
+
self.products *= p if hold_elements
|
|
64
95
|
RSA::ACC::MembershipProof.new(elements, current_acc, value, RSA::ACC::PoE.prove(current_acc, p, value, n))
|
|
65
96
|
end
|
|
66
97
|
|
|
@@ -76,7 +107,10 @@ module RSA
|
|
|
76
107
|
# @param [RSA::ACC::MembershipProof] proof inclusion proof.
|
|
77
108
|
# @return [Boolean] If element exist in acc return true, otherwise false.
|
|
78
109
|
def member?(proof)
|
|
79
|
-
|
|
110
|
+
return false unless proof.is_a?(RSA::ACC::MembershipProof)
|
|
111
|
+
x = proof.element_prime
|
|
112
|
+
return false if x.nil?
|
|
113
|
+
RSA::ACC::PoE.verify(proof.witness, x, value, proof.proof, n)
|
|
80
114
|
end
|
|
81
115
|
|
|
82
116
|
# Verifies a non-membership proof against the current accumulator and +elements+ whose non-inclusion is being proven.
|
|
@@ -84,9 +118,18 @@ module RSA
|
|
|
84
118
|
# @param [RSA::ACC::NonMembershipProof] proof non-membership proof.
|
|
85
119
|
# @return [Boolean]
|
|
86
120
|
def non_member?(elements, proof)
|
|
121
|
+
return false unless proof.is_a?(RSA::ACC::NonMembershipProof)
|
|
122
|
+
return false unless valid_elements?(elements)
|
|
87
123
|
x = elements_to_prime(elements)
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
return false unless valid_group_elem?(proof.v)
|
|
125
|
+
return false unless valid_group_elem?(proof.d)
|
|
126
|
+
v = normalize(proof.v, n)
|
|
127
|
+
d = normalize(proof.d, n)
|
|
128
|
+
# g * v^{-1} must be derived by the verifier. If proof#gv_inv were used as-is,
|
|
129
|
+
# a prover could pick an arbitrary d and claim gv_inv = d^x, which passes for any x.
|
|
130
|
+
gv_inv = normalize(g * v.pow(-1, n), n)
|
|
131
|
+
RSA::ACC::PoKE2.verify(value, v, proof.poke2_proof, n) &&
|
|
132
|
+
RSA::ACC::PoE.verify(d, x, gv_inv, proof.poe_proof, n)
|
|
90
133
|
end
|
|
91
134
|
|
|
92
135
|
# Generate membership proof for +elements+.
|
|
@@ -98,7 +141,7 @@ module RSA
|
|
|
98
141
|
raise RSA::ACC::Error.new 'This accumulator does not hold the product of the elements.' unless hold_elements
|
|
99
142
|
x = elements_to_prime(elements)
|
|
100
143
|
return nil unless products.modulo(x) == 0
|
|
101
|
-
witness = g.pow(products / x, n)
|
|
144
|
+
witness = normalize(g.pow(products / x, n), n)
|
|
102
145
|
RSA::ACC::MembershipProof.new(elements, witness, value, RSA::ACC::PoE.prove(witness, x, value, n))
|
|
103
146
|
end
|
|
104
147
|
|
|
@@ -113,9 +156,9 @@ module RSA
|
|
|
113
156
|
a, b = egcd(s, x)
|
|
114
157
|
raise ArgumentError, "Inputs not co-prime." unless a * s + b * x == 1
|
|
115
158
|
|
|
116
|
-
v = value.pow(a, n)
|
|
117
|
-
d = g.pow(b, n)
|
|
118
|
-
gv_inv = (g * v.pow(-1, n)
|
|
159
|
+
v = normalize(value.pow(a, n), n)
|
|
160
|
+
d = normalize(g.pow(b, n), n)
|
|
161
|
+
gv_inv = normalize(g * v.pow(-1, n), n)
|
|
119
162
|
|
|
120
163
|
poke2_proof = RSA::ACC::PoKE2.prove(value, a, v, n)
|
|
121
164
|
poe_proof = RSA::ACC::PoE.prove(d, x, gv_inv, n)
|
|
@@ -130,9 +173,12 @@ module RSA
|
|
|
130
173
|
return RSA::ACC::MembershipProof.new(proofs.map(&:element).flatten, value, value, RSA::ACC::PoE.prove(value, 1, value, n)) if proofs.empty?
|
|
131
174
|
|
|
132
175
|
witnesses = proofs.map do |proof|
|
|
176
|
+
raise RSA::ACC::Error, 'Invalid proof.' unless proof.is_a?(RSA::ACC::MembershipProof)
|
|
133
177
|
p = proof.element_prime
|
|
134
|
-
raise RSA::ACC::Error, '
|
|
135
|
-
|
|
178
|
+
raise RSA::ACC::Error, 'Invalid element.' if p.nil?
|
|
179
|
+
raise RSA::ACC::Error, 'Bad witness.' unless proof.witness.is_a?(Integer)
|
|
180
|
+
raise RSA::ACC::Error, 'Bad witness.' unless normalize(proof.witness.pow(p, n), n) == value
|
|
181
|
+
[p, normalize(proof.witness, n)]
|
|
136
182
|
end
|
|
137
183
|
|
|
138
184
|
current_value = value
|
|
@@ -153,14 +199,27 @@ module RSA
|
|
|
153
199
|
# @param [Array[Integer]] f factorizations of the exponent x = x1, ..., xn.
|
|
154
200
|
# @return [Array{Integer}] array of xi-th root
|
|
155
201
|
def root_factor(*f)
|
|
202
|
+
raise ArgumentError, 'factorization must not be empty.' if f.empty?
|
|
156
203
|
return [value] if f.size == 1
|
|
157
204
|
half_n = f.size / 2
|
|
158
|
-
g_l = RSA::Accumulator.new(n, value.pow(f[0...half_n].map.inject(:*), n), false)
|
|
159
|
-
g_r = RSA::Accumulator.new(n, value.pow(f[half_n..-1].map.inject(:*), n), false)
|
|
205
|
+
g_l = RSA::Accumulator.new(n, value.pow(f[0...half_n].map.inject(:*), n), g, false)
|
|
206
|
+
g_r = RSA::Accumulator.new(n, value.pow(f[half_n..-1].map.inject(:*), n), g, false)
|
|
160
207
|
l = g_r.root_factor(*f[0...half_n])
|
|
161
208
|
r = g_l.root_factor(*f[half_n..-1])
|
|
162
209
|
[l, r].flatten
|
|
163
210
|
end
|
|
164
211
|
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
# Check whether +elem+ is a non-degenerate element of the group Z_n^*.
|
|
215
|
+
# @param [Integer] elem an element supplied by an untrusted prover.
|
|
216
|
+
# @return [Boolean]
|
|
217
|
+
def valid_group_elem?(elem)
|
|
218
|
+
return false unless elem.is_a?(Integer)
|
|
219
|
+
y = normalize(elem, n)
|
|
220
|
+
# y == 1 covers both 1 and n - 1, which are the identity and the order-2 element.
|
|
221
|
+
y > 1 && y.gcd(n) == 1
|
|
222
|
+
end
|
|
223
|
+
|
|
165
224
|
end
|
|
166
225
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rsa-accumulator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- azuchi
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rbnacl
|
|
@@ -78,7 +77,6 @@ files:
|
|
|
78
77
|
- ".rspec"
|
|
79
78
|
- ".ruby-gemset"
|
|
80
79
|
- ".ruby-version"
|
|
81
|
-
- ".travis.yml"
|
|
82
80
|
- CODE_OF_CONDUCT.md
|
|
83
81
|
- Gemfile
|
|
84
82
|
- LICENSE.txt
|
|
@@ -99,7 +97,6 @@ homepage: https://github.com/chaintope/rsa-accumulatorrb
|
|
|
99
97
|
licenses:
|
|
100
98
|
- MIT
|
|
101
99
|
metadata: {}
|
|
102
|
-
post_install_message:
|
|
103
100
|
rdoc_options: []
|
|
104
101
|
require_paths:
|
|
105
102
|
- lib
|
|
@@ -114,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
114
111
|
- !ruby/object:Gem::Version
|
|
115
112
|
version: '0'
|
|
116
113
|
requirements: []
|
|
117
|
-
rubygems_version: 3.
|
|
118
|
-
signing_key:
|
|
114
|
+
rubygems_version: 3.6.9
|
|
119
115
|
specification_version: 4
|
|
120
116
|
summary: RSA accumulator implementation for Ruby.
|
|
121
117
|
test_files: []
|