I’m trying to implement the VRF in JavaScript as described in this paper https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-06.pdf .
I use ed25519 from the elliptic.js library and the bn.js library for big integer
The ‘ecvrf_prove’ and ‘ecvrf_prove_to_hash’ function already work, but the ‘ecvrf_verify’ function can’t verify the proof.
My first idea was to look at the calculation of U and V in steps 5 and 6. But i didn’t find any error or maybe i didn’t understand the whole thing.
Here is some code, maybe can someone can help me fix this or tell me what i’m doing wrong.
Thanks
// 5.3. ECVRF Verifying
/**
* ECVRF_verify(Y, pi_string, alpha_string)
* @param {*} y public key, an EC point
* @param {*} pi_string VRF proof, octet string of length ptLen+n+qLen
* @param {*} alpha_string VRF input, octet string
* @return ("VALID", beta_string), where beta_string is the VRF hash output, octet string
of length hLen (64) bytes; or ("INVALID", []) upon failure
*/
function ecvrf_verify(y, pi_string, alpha_string) {
// 1. D = ECVRF_decode_proof(pi_string)
// 2. If D is "INVALID", output "INVALID" and stop
const d = _ecvrf_decode_proof(pi_string)
if (d == 'INVALID') {
return 'INVALID'
}
// 3. (Gamma, c, s) = D
const gamma = d[0]
const c = d[1]
const s = d[2]
// 4. H = ECVRF_hash_to_curve(suite_string, y, alpha_string)
const y_point = _string_to_point(y)
if (y_point == 'INVALID') {
return 'INVALID'
}
const h = _ecvrf_hash_to_try_and_increment(SUITE_STRING, y_point, alpha_string)
if (h == 'INVALID') {
return 'INVALID'
}
// 5. U = s*B - c*y
const s_b = BASE.mul(s)
let c_y = y_point.mul(c)
// Negate c_y
c_y = c_y.neg()
const u = s_b.add(c_y)
// 6. V = s*H - c*Gamma
const s_h = h.mul(s)
let c_g = gamma.mul(s)
// Negate c_g
c_g = c_g.neg()
const v = s_h.add(c_g)
// 7. c’ = ECVRF_hash_points(H, Gamma, U, V)
const cp = _ecvrf_hash_points(h, gamma, u, v)
// 8. If c and c’ are equal, output ("VALID", ECVRF_proof_to_hash(pi_string)); else output "INVALID"
if (c.eq(cp)) {
return {
status: 'VALID',
pi_string: ecvrf_proof_to_hash(pi_string)
}
}
else{
return 'INVALID'
}
}
// section 5.4.3. ECVRF Hash Points
/**
* ECVRF_hash_points(P1, P2, ..., PM)
* @param {*} p1 an EC points in G
* @param {*} p2 an EC points in G
* @param {*} p3 an EC points in G
* @param {*} p4 an EC points in G
* @retrun c hash value, integer between 0 and 2^(8n)-1
*/
function _ecvrf_hash_points(p1, p2, p3, p4) {
// 1. two_string = 0x02 = int_to_string(2, 1), a single octet with value 2
const two_string = Buffer.alloc(1, 2)
// 2. Initialize str = suite_string || two_string
let string = Buffer.concat([SUITE_STRING, two_string])
// 3.for PJ in [P1, P2, ... PM]:
// str = str || point_to_string(PJ)
string = Buffer.concat([string, _point_to_string(p1), _point_to_string(p2), _point_to_string(p3), _point_to_string(p4)])
// 4. c_string = Hash(str)
const c_string = _hash(string)
// 5. truncated_c_string = c_string[0]...c_string[n-1]
const truncated_c_string = c_string.slice(0, 16)
// 6. c = string_to_int(truncated_c_string)
const c = _string_to_int(truncated_c_string)
// 7. Output c
return c
}