Skip to content

Commit e6e2673

Browse files
Merge pull request #98 from logical-mechanism/adding-bls12-381-types
Adding bls12 381 types
2 parents efa2060 + 84e188d commit e6e2673

File tree

2 files changed

+241
-0
lines changed

2 files changed

+241
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
- Changing sha3_256 to blake2b_256, it is cheaper to compute
77
- value.unique_token_name has been changed to the v3 version, no more hashing and has a personal tag feature
88
- Updated aiken to v1.1.2 and stdlib to v2.1.0
9+
- Added the registry type and related function
10+
- stdlib 2.1.0 forces blake2b_224 upon us, this may change in the future
911

1012
# v0.5.0
1113

lib/types/registry.ak

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
use aiken/crypto
2+
use aiken/crypto/bls12_381/g1
3+
use aiken/crypto/bls12_381/scalar.{Scalar}
4+
use aiken/primitive/bytearray
5+
6+
/// Alpha is the generator and beta is the public value. The pair is formed from
7+
/// the relationship g^x = u, where g is the generator and u is the public value.
8+
/// The value x is a secret integer used to create the public value from the
9+
/// generator.
10+
pub type Register {
11+
// the generator, #<Bls12_381, G1>
12+
generator: ByteArray,
13+
// the public value, #<Bls12_381, G1>
14+
public_value: ByteArray,
15+
}
16+
17+
/// This simulates randomizing a register. It is used for testing purposes only.
18+
///
19+
/// ```aiken
20+
/// registry.randomize(register, random_scalar)
21+
/// ```
22+
fn randomize(datum: Register, s: Scalar) -> Register {
23+
// decompress the generator and public value
24+
let g: G1Element = g1.decompress(datum.generator)
25+
let u: G1Element = g1.decompress(datum.public_value)
26+
// now randomize the register elements
27+
let g_s: G1Element = g1.scale(g, s)
28+
let u_s: G1Element = g1.scale(u, s)
29+
// recompress the new randomized elements
30+
Register { generator: g_s |> g1.compress, public_value: u_s |> g1.compress }
31+
}
32+
33+
test cheapest_hash() {
34+
// PASS [mem: 805, cpu: 2467639] expensive hash
35+
// crypto.keccak_256(#"") == #"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
36+
//
37+
// PASS [mem: 805, cpu: 1663641] cheap hash
38+
// crypto.sha3_256(#"") == #"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"
39+
//
40+
// PASS [mem: 805, cpu: 434990] cheap hash
41+
// crypto.sha2_256(#"") == #"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
42+
//
43+
// PASS [mem: 805, cpu: 357676] cheaper hash
44+
// crypto.blake2b_224(#"") == #"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07"
45+
//
46+
// PASS [mem: 805, cpu: 351411] cheapest hash
47+
crypto.blake2b_256(#"") == #"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"
48+
}
49+
50+
/// A bytearray of a value for the challenge c. This process should act like a
51+
/// random oracle providing a large challenge value for the user. The inputs
52+
/// should be compressed g1 elements but they can also be compressed integers.
53+
///
54+
/// ```aiken
55+
/// registry.fiat_shamir_heuristic(g_b, g_r_b, u_b, b)
56+
/// ```
57+
fn fiat_shamir_heuristic(
58+
// compressed g element
59+
g_b: ByteArray,
60+
// compressed g^r element
61+
g_r_b: ByteArray,
62+
// compressed g^x element
63+
u_b: ByteArray,
64+
// a bound used to create one time transforms
65+
b: ByteArray,
66+
) -> ByteArray {
67+
// concat g_b, g_r_b, u_b, and b together then hash the result using the
68+
// blake2b_256 hash function as it is the cheapest on chain
69+
// as of right now 2.1.0 stdlib we are forced to use blake2b_224
70+
g_b
71+
|> bytearray.concat(g_r_b)
72+
|> bytearray.concat(u_b)
73+
|> bytearray.concat(b)
74+
|> crypto.blake2b_224()
75+
// |> crypto.blake2b_256()
76+
}
77+
78+
test empty_fiat_shamir_transform() {
79+
// fiat_shamir_heuristic(#"", #"", #"", #"") == #"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"
80+
fiat_shamir_heuristic(#"", #"", #"", #"") == #"836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07"
81+
}
82+
83+
test real_fiat_shamir_transform() {
84+
// fiat_shamir_heuristic(
85+
// #"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb",
86+
// #"81b223cea171a87feba9b7749a2df7601c5a75ae01155fadc124a2ac49099a514cf1e7d9cdc769dceab14a95bd6cb0bd",
87+
// #"a09d99e02f7200526dc55ef722cc171e7aa14fc732614c02ac58d59d7026a7eb18d8798f6928ea2b513f3a4feb0c94d1",
88+
// #"acab",
89+
// ) == #"8f9409d05727322c9f2d1d0adf817b8d0e3a681977edc9ab866879a4a4f8f5b9"
90+
fiat_shamir_heuristic(
91+
#"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb",
92+
#"81b223cea171a87feba9b7749a2df7601c5a75ae01155fadc124a2ac49099a514cf1e7d9cdc769dceab14a95bd6cb0bd",
93+
#"a09d99e02f7200526dc55ef722cc171e7aa14fc732614c02ac58d59d7026a7eb18d8798f6928ea2b513f3a4feb0c94d1",
94+
#"acab",
95+
) == #"1b556f7bb6a26d00a7c79468794858ba6aa0e41a2c3af0754ec4a11d"
96+
}
97+
98+
/// A Schnorr proof to prove knowledge of the secret value x without revealing
99+
/// the value in the process. The proof uses, in multiplicative form,
100+
/// g^z = g^r * u^c, where z = r + c*x and u = g^x. This function uses the
101+
/// Fiat-Shamir heuristic for non-interactivity.
102+
///
103+
/// ```aiken
104+
/// registry.prove(datum, z_b, g_r_b, bound)
105+
/// ```
106+
pub fn prove(
107+
datum: Register,
108+
z_b: ByteArray,
109+
g_r_b: ByteArray,
110+
bound: ByteArray,
111+
) -> Bool {
112+
//
113+
// the z computation: g^z = g^(r + c * x) = g^r * g^(c * x) = g^r * (g^x)^c
114+
expect Some(z): Option<Scalar> = scalar.from_bytearray_big_endian(z_b)
115+
let g_z: G1Element = g1.decompress(datum.generator) |> g1.scale(z)
116+
//
117+
// use the fiat-shamir heuristic to calculate the challenge then convert it to an integer
118+
expect Some(c): Option<Scalar> =
119+
fiat_shamir_heuristic(datum.generator, g_r_b, datum.public_value, bound)
120+
|> scalar.from_bytearray_big_endian()
121+
//
122+
// the u^c computation: u^c = (g^x)^c = g^(x * c)
123+
let u_c: G1Element = g1.decompress(datum.public_value) |> g1.scale(c)
124+
//
125+
// check if equation: g^z = g^r * u^c is true
126+
//
127+
g_z |> g1.equal(g1.add(g1.decompress(g_r_b), u_c))
128+
}
129+
130+
test valid_schnorr_proof() {
131+
// some secret x
132+
expect Some(x): Option<Scalar> =
133+
scalar.new(
134+
42435875175126190479447740508185965837690552500527637822603658699938581184513,
135+
)
136+
// the datum register using the g1 generator and the public value for x
137+
let datum: Register =
138+
Register {
139+
generator: g1.generator |> g1.compress,
140+
public_value: g1.generator |> g1.scale(x) |> g1.compress,
141+
}
142+
// a random number
143+
expect Some(r): Option<Scalar> =
144+
scalar.new(
145+
32435875175126190479447740508185965837690552500527637822603658699938581184513,
146+
)
147+
// the bound, something unique from the tx
148+
let bound: ByteArray = #"acab"
149+
// calculate the g^r term
150+
let g: G1Element = g1.generator
151+
let g_r: G1Element = g1.scale(g, r)
152+
let g_r_b: ByteArray = g_r |> g1.compress
153+
// the challenge number using a fiat shamir transform
154+
let c_b: ByteArray =
155+
fiat_shamir_heuristic(datum.generator, g_r_b, datum.public_value, bound)
156+
expect Some(c): Option<Scalar> = scalar.from_bytearray_big_endian(c_b)
157+
// the z value
158+
let z: Scalar = scalar.mul(c, x) |> scalar.add(r)
159+
let z_b: ByteArray = scalar.to_bytearray_big_endian(z, 0)
160+
prove(datum, z_b, g_r_b, bound)
161+
}
162+
163+
test randomized_valid_schnorr_proof() {
164+
// some secret x
165+
expect Some(x): Option<Scalar> =
166+
scalar.new(
167+
12435875175126190479447740508185965837690552500527637822603658699938581184513,
168+
)
169+
// the datum register using the g1 generator and the public value for x
170+
let datum: Register =
171+
Register {
172+
generator: g1.generator |> g1.compress,
173+
public_value: g1.generator |> g1.scale(x) |> g1.compress,
174+
}
175+
// a random number
176+
expect Some(r): Option<Scalar> =
177+
scalar.new(
178+
32435875175126190479447740508185965837690552500527637822603658699938581184513,
179+
)
180+
// another random number
181+
expect Some(d): Option<Scalar> =
182+
scalar.new(
183+
12435875175126190479447740508185965837690552500527637822603658699938581184513,
184+
)
185+
// rerandomize the a0 register
186+
let datum_rng: Register = randomize(datum, d)
187+
// the bound, something unique from the tx
188+
let bound: ByteArray = #"acabface"
189+
// calculate the g^r term
190+
let g: G1Element = datum_rng.generator |> g1.decompress
191+
let g_r: G1Element = g1.scale(g, r)
192+
let g_r_b: ByteArray = g_r |> g1.compress
193+
// the challenge number using a fiat shamir transform
194+
let c_b: ByteArray =
195+
fiat_shamir_heuristic(
196+
datum_rng.generator,
197+
g_r_b,
198+
datum_rng.public_value,
199+
bound,
200+
)
201+
expect Some(c): Option<Scalar> = scalar.from_bytearray_big_endian(c_b)
202+
// the z value
203+
let z: Scalar = scalar.mul(c, x) |> scalar.add(r)
204+
let z_b: ByteArray = scalar.to_bytearray_big_endian(z, 0)
205+
prove(datum_rng, z_b, g_r_b, bound)
206+
}
207+
208+
test invalid_schnorr_proof() fail {
209+
// some secret x
210+
expect Some(x): Option<Scalar> =
211+
scalar.new(
212+
42435875175126190479447740508185965837690552500527637822603658699938581184513,
213+
)
214+
// the datum register using the g1 generator and the public value for x
215+
let datum: Register =
216+
Register {
217+
generator: g1.generator |> g1.compress,
218+
public_value: g1.generator |> g1.scale(x) |> g1.compress,
219+
}
220+
// a random number
221+
expect Some(r): Option<Scalar> =
222+
scalar.new(
223+
32435875175126190479447740508185965837690552500527637822603658699938581184513,
224+
)
225+
// the bound, something unique from the tx
226+
let bound: ByteArray = #""
227+
// calculate the g^r term
228+
let g: G1Element = g1.generator
229+
let g_r: G1Element = g1.scale(g, r)
230+
let g_r_b: ByteArray = g_r |> g1.compress
231+
// the challenge number using a fiat shamir transform
232+
let c_b: ByteArray =
233+
fiat_shamir_heuristic(datum.generator, g_r_b, datum.public_value, bound)
234+
expect Some(c): Option<Scalar> = scalar.from_bytearray_big_endian(c_b)
235+
// the bad z value, it assumes the secret is the challenge
236+
let z: Scalar = scalar.mul(c, c) |> scalar.add(r)
237+
let z_b: ByteArray = scalar.to_bytearray_big_endian(z, 0)
238+
prove(datum, z_b, g_r_b, bound)
239+
}

0 commit comments

Comments
 (0)