# VRF upgrade (v6 -> v9) # What's the difference 1. `ECVRF_hash_to_curve_elligator2_25519` -> `ECVRF_hash_to_curve_h2c_suite` 2. `hash_to_curve` no longer takes a suite_string as explicit input 3. `decode_proof` takes an extra 0-byte as input for domain separation 4. `hash_points` has minor differences ## 1. encode_to_curve #### Implementation steps ``` encode_to_curve(msg) Input: msg, an arbitrary-length byte string. Output: P, a point in G. Steps: 1. u = hash_to_field(msg, 1) 2. Q = map_to_curve(u[0]) 3. P = clear_cofactor(Q) 4. return P ``` #### `hash_to_field(msg, count=1)` ``` Parameters: - DST ("ECVRF_edwards25519_XMD:SHA-512_ELL2_NU_0x04") - F, a finite field of characteristic p and order q = p^m. (?) - q, p^m = (2^255 - 19)^1 - p, (2^255 - 19) - m, (1) - L = ceil((ceil(log2(p)) + k) / 8) = (48) - expand_message, a function that expands a byte string and domain separation tag into a uniformly random byte string ("expand_message_xmd") Inputs: - msg, a byte string containing the message to hash. - count, the number of elements of F to output. (1) Outputs: - (u_0, ..., u_(count - 1)), a list of field elements. Steps: 1. len_in_bytes = count * m * L 2. uniform_bytes = expand_message(msg, DST, len_in_bytes) // count = 1 -> Do 1 time -> elm_offset = 0 -> return u_0 3. for i in (0, ..., count - 1): 4. for j in (0, ..., m - 1): 5. elm_offset = L * (j + i * m) 6. tv = substr(uniform_bytes, elm_offset, L) 7. e_j = OS2IP(tv) mod p 8. u_i = (e_0, ..., e_(m - 1)) 9. return (u_0, ..., u_(count - 1)) ``` ##### `expand_message_xmd(msg, DST, len_in_bytes)` ``` Parameters: - H, a hash function (sha512) - b_in_bytes, b / 8 for b the output size of H in bits. (64) For example, for b = 256, b_in_bytes = 32. - r_in_bytes, the input block size of H, measured in bytes (see discussion above). For example, for SHA-256, r_in_bytes = 64. Input: - msg, a byte string. - DST, a byte string of at most 255 bytes. See below for information on using longer DSTs. - len_in_bytes, the length of the requested output in bytes. (64) Output: - uniform_bytes, a byte string. Steps: 1. ell = ceil(len_in_bytes / b_in_bytes) 2. ABORT if ell > 255 3. DST_prime = DST || I2OSP(len(DST), 1) 4. Z_pad = I2OSP(0, r_in_bytes) 5. l_i_b_str = I2OSP(len_in_bytes, 2) 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime 7. b_0 = H(msg_prime) 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) // In our case ell = 1, so it'll skip the loop 9. for i in (2, ..., ell): 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) 11. uniform_bytes = b_1 || ... || b_ell 12.return substr(uniform_bytes, 0, len_in_bytes) ``` --- ### `map_to_curve(u)` ``` map_to_curve_elligator2_edwards(u) Input: u, an element of F. Output: (v, w), a point on E. 1. (s, t) = map_to_curve_elligator2(u) # (s, t) is on M 2. (v, w) = rational_map(s, t) # (v, w) is on E 3. return (v, w) ``` #### `map_to_curve_elligator2(u)` ``` Constants: * J, (486662) * K, (1) * Z, (2) Sign of t: this mapping fixes the sign of t as specified in [BHKL13]. No additional adjustment is required. Exceptions: The exceptional case is Z * u^2 == -1, i.e., 1 + Z * u^2 == 0. Implementations must detect this case and set x1 = -(J / K). Note that this can only happen when q = 3 (mod 4). Operations: 1. x1 = -(J / K) * inv0(1 + Z * u^2) 2. If x1 == 0, set x1 = -(J / K) 3. gx1 = x1^3 + (J / K) * x1^2 + x1 / K^2 4. x2 = -x1 - (J / K) // gx2 might not happen? (Because gx1 is always square.) 5. gx2 = x2^3 + (J / K) * x2^2 + x2 / K^2 6. If is_square(gx1), set x = x1, y = sqrt(gx1), and sgn0(y) == 1. 7. Else set x = x2, y = sqrt(gx2), and sgn0(y) == 0. // ----------- 8. s = x * K 9. t = y * K 10. return (s, t) ``` #### `rational_map` ``` The birational maps are: (u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x) (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1)) ``` --- ### `clear_cofactor` ``` The clear_cofactor function is parameterized by a scalar h_eff. * h_eff = 8 Specifically, clear_cofactor(P) := h_eff * P where * represents scalar multiplication. When a curve does not support a fast cofactor clearing method, h_eff = h and the cofactor MUST be cleared via scalar multiplication. ``` --- ## 2. de ## Utility function ### `I2OSP(x, xLen)` ``` I2OSP converts a nonnegative integer to an octet string of a specified length. Input: x nonnegative integer to be converted xLen intended length of the resulting octet string Output: X corresponding octet string of length xLen Error: "integer too large" Steps: 1. If x >= 256^xLen, output "integer too large" and stop. 2. Write the integer x in its unique xLen-digit representation in base 256: x = x_(xLen-1) 256^(xLen-1) + x_(xLen-2) 256^(xLen-2) + ... + x_1 256 + x_0, where 0 <= x_i < 256 (note that one or more leading digits will be zero if x is less than 256^(xLen-1)). 3. Let the octet X_i have the integer value x_(xLen-i) for 1 <= i <= xLen. Output the octet string X = X_1 X_2 ... X_xLen. ``` ### `OS2IP(X)` ``` OS2IP converts an octet string to a nonnegative integer. Input: X octet string to be converted Output: x corresponding nonnegative integer Steps: 1. Let X_1 X_2 ... X_xLen be the octets of X from first to last, and let x_(xLen-i) be the integer value of the octet X_i for 1 <= i <= xLen. 2. Let x = x_(xLen-1) 256^(xLen-1) + x_(xLen-2) 256^(xLen-2) + ... + x_1 256 + x_0. 3. Output x. ``` ### `sgn0(x)` ``` * sgn0(x): This function returns either 0 or 1 indicating the "sign" of x, where sgn0(x) == 1 just when x is "negative". (In other words, this function always considers 0 to be positive.) Section 4.1 defines this function and discusses its implementation. ``` ### `is_sqaure(x)` ``` is_square(x): This function returns True whenever the value x is a square in the field F. By Euler’s criterion, this function can be calculated in constant time as is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F; { False, otherwise. ``` ## Question - `"ECVRF_" || "edwards25519_XMD:SHA-512_ELL2_NU_" || suite_string` when do we use it and is it DST?