diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index 6b2d3b595..8a8202ac4 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -109,6 +109,10 @@ fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Eleme fe_carry(out1, fe_relax_cast(out1)) } +fe_carry_abs :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) { + fe_cond_negate(out1, arg1, fe_is_negative(arg1)) +} + fe_carry_sqrt_ratio_m1 :: proc "contextless" ( out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, // u @@ -168,8 +172,7 @@ fe_carry_sqrt_ratio_m1 :: proc "contextless" ( fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i) // Pick the non-negative square root. - fe_carry_opp(r_prime, r) - fe_cond_select(out1, r, r_prime, fe_is_negative(r)) + fe_carry_abs(out1, r) fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3}) mem.zero_explicit(&b, size_of(b)) @@ -254,3 +257,11 @@ fe_cond_select :: #force_no_inline proc "contextless" ( out1[3] = x4 out1[4] = x5 } + +fe_cond_negate :: proc "contextless" (out1, arg1: ^Tight_Field_Element, ctrl: int) { + tmp1: Tight_Field_Element = --- + fe_carry_opp(&tmp1, arg1) + fe_cond_select(out1, arg1, &tmp1, ctrl) + + fe_clear(&tmp1) +} diff --git a/core/crypto/ristretto255/ristretto255.odin b/core/crypto/ristretto255/ristretto255.odin new file mode 100644 index 000000000..d1f2b6ee5 --- /dev/null +++ b/core/crypto/ristretto255/ristretto255.odin @@ -0,0 +1,510 @@ +/* +package ristretto255 implement the ristretto255 prime-order group. + +See: +- https://www.rfc-editor.org/rfc/rfc9496 +*/ +package ristretto255 + +import grp "core:crypto/_edwards25519" +import field "core:crypto/_fiat/field_curve25519" +import "core:mem" + +// ELEMENT_SIZE is the size of a byte-encoded ristretto255 group element. +ELEMENT_SIZE :: 32 +// WIDE_ELEMENT_SIZE is the side of a wide byte-encoded ristretto255 +// group element. +WIDE_ELEMENT_SIZE :: 64 + +@(private) +FE_NEG_ONE := field.Tight_Field_Element { + 2251799813685228, + 2251799813685247, + 2251799813685247, + 2251799813685247, + 2251799813685247, +} +@(private) +FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element { + 278908739862762, + 821645201101625, + 8113234426968, + 1777959178193151, + 2118520810568447, +} +@(private) +FE_ONE_MINUS_D_SQ := field.Tight_Field_Element { + 1136626929484150, + 1998550399581263, + 496427632559748, + 118527312129759, + 45110755273534, +} +@(private) +FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element { + 1507062230895904, + 1572317787530805, + 683053064812840, + 317374165784489, + 1572899562415810, +} +@(private) +FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element { + 2241493124984347, + 425987919032274, + 2207028919301688, + 1220490630685848, + 974799131293748, +} +@(private) +GE_IDENTITY := Group_Element{grp.GE_IDENTITY, true} + +// Group_Element is a ristretto255 group element. The zero-initialized +// value is invalid. +Group_Element :: struct { + // WARNING: While the internal representation is an Edwards25519 + // group element, this is not guaranteed to always be the case, + // and your code *WILL* break if you mess with `_p`. + _p: grp.Group_Element, + _is_initialized: bool, +} + +// ge_clear clears ge to the uninitialized state. +ge_clear :: proc "contextless" (ge: ^Group_Element) { + mem.zero_explicit(ge, size_of(Group_Element)) +} + +// ge_set sets `ge = a`. +ge_set :: proc(ge, a: ^Group_Element) { + _ge_assert_initialized([]^Group_Element{a}) + + grp.ge_set(&ge._p, &a._p) + ge._is_initialized = true +} + +// ge_identity sets ge to the identity (neutral) element. +ge_identity :: proc "contextless" (ge: ^Group_Element) { + grp.ge_identity(&ge._p) + ge._is_initialized = true +} + +// ge_generator sets ge to the group generator. +ge_generator :: proc "contextless" (ge: ^Group_Element) { + grp.ge_generator(&ge._p) + ge._is_initialized = true +} + +// ge_set_bytes sets ge to the result of decoding b as a ristretto255 +// group element, and returns true on success. +@(require_results) +ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool { + // 1. Interpret the string as an unsigned integer s in little-endian + // representation. If the length of the string is not 32 bytes or + // if the resulting value is >= p, decoding fails. + // + // 2. If IS_NEGATIVE(s) returns TRUE, decoding fails. + + if len(b) != ELEMENT_SIZE { + return false + } + if b[31] & 128 != 0 || b[0] & 1 != 0 { + // Fail early if b is clearly > p, or negative. + return false + } + + b_ := transmute(^[32]byte)(raw_data(b)) + + s: field.Tight_Field_Element = --- + defer field.fe_clear(&s) + + field.fe_from_bytes(&s, b_) + if field.fe_equal_bytes(&s, b_) != 1 { + // Reject non-canonical encodings of s. + return false + } + + // 3. Process s as follows: + v, u1, u2: field.Loose_Field_Element = ---, ---, --- + tmp, u2_sqr: field.Tight_Field_Element = ---, --- + + // ss = s^2 + // u1 = 1 - ss + // u2 = 1 + ss + // u2_sqr = u2^2 + field.fe_carry_square(&tmp, field.fe_relax_cast(&s)) + field.fe_sub(&u1, &field.FE_ONE, &tmp) + field.fe_add(&u2, &field.FE_ONE, &tmp) + field.fe_carry_square(&u2_sqr, &u2) + + // v = -(D * u1^2) - u2_sqr + field.fe_carry_square(&tmp, &u1) + field.fe_carry_mul(&tmp, field.fe_relax_cast(&grp.FE_D), field.fe_relax_cast(&tmp)) + field.fe_carry_add(&tmp, &tmp, &u2_sqr) + field.fe_opp(&v, &tmp) + + // (was_square, invsqrt) = SQRT_RATIO_M1(1, v * u2_sqr) + field.fe_carry_mul(&tmp, &v, field.fe_relax_cast(&u2_sqr)) + was_square := field.fe_carry_sqrt_ratio_m1( + &tmp, + field.fe_relax_cast(&field.FE_ONE), + field.fe_relax_cast(&tmp), + ) + + // den_x = invsqrt * u2 + // den_y = invsqrt * den_x * v + x, y, t: field.Tight_Field_Element = ---, ---, --- + field.fe_carry_mul(&x, field.fe_relax_cast(&tmp), &u2) + field.fe_carry_mul(&y, field.fe_relax_cast(&tmp), field.fe_relax_cast(&x)) + field.fe_carry_mul(&y, field.fe_relax_cast(&y), &v) + + // x = CT_ABS(2 * s * den_x) + field.fe_carry_mul(&x, field.fe_relax_cast(&s), field.fe_relax_cast(&x)) + field.fe_carry_add(&x, &x, &x) + field.fe_carry_abs(&x, &x) + + // y = u1 * den_y + field.fe_carry_mul(&y, &u1, field.fe_relax_cast(&y)) + + // t = x * y + field.fe_carry_mul(&t, field.fe_relax_cast(&x), field.fe_relax_cast(&y)) + + field.fe_clear_vec([]^field.Loose_Field_Element{&v, &u1, &u2}) + field.fe_clear_vec([]^field.Tight_Field_Element{&tmp, &u2_sqr}) + defer field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &t}) + + // 4. If was_square is FALSE, IS_NEGATIVE(t) returns TRUE, or y = 0, + // decoding fails. Otherwise, return the group element represented + // by the internal representation (x, y, 1, t) as the result of + // decoding. + + switch { + case was_square == 0: + // Not sure why the RFC doesn't have this just fail early. + return false + case field.fe_is_negative(&t) != 0: + return false + case field.fe_equal(&y, &field.FE_ZERO) != 0: + return false + } + + field.fe_set(&ge._p.x, &x) + field.fe_set(&ge._p.y, &y) + field.fe_one(&ge._p.z) + field.fe_set(&ge._p.t, &t) + ge._is_initialized = true + + return true +} + +// ge_set_wide_bytes sets ge to the result of deriving a ristretto255 +// group element, from a wide (512-bit) byte string. +ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) { + if len(b) != WIDE_ELEMENT_SIZE { + panic("crypto/ristretto255: invalid wide input size") + } + + // The element derivation function on an input string b proceeds as + // follows: + // + // 1. Compute P1 as MAP(b[0:32]). + // 2. Compute P2 as MAP(b[32:64]). + // 3. Return P1 + P2. + + p1, p2: Group_Element = ---, --- + ge_map(&p1, b[0:32]) + ge_map(&p2, b[32:64]) + + ge_add(ge, &p1, &p2) + + ge_clear(&p1) + ge_clear(&p2) +} + +// ge_bytes sets dst to the canonical encoding of ge. +ge_bytes :: proc(ge: ^Group_Element, dst: []byte) { + _ge_assert_initialized([]^Group_Element{ge}) + if len(dst) != ELEMENT_SIZE { + panic("crypto/ristretto255: invalid destination size") + } + + x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t + + // 1. Process the internal representation into a field element s as + // follows: + + // u1 = (z0 + y0) * (z0 - y0) + // u2 = x0 * y0 + u1, u2: field.Tight_Field_Element = ---, --- + tmp1, tmp2: field.Loose_Field_Element = ---, --- + field.fe_add(&tmp1, z0, y0) + field.fe_sub(&tmp2, z0, y0) + field.fe_carry_mul(&u1, &tmp1, &tmp2) + field.fe_carry_mul(&u2, field.fe_relax_cast(x0), field.fe_relax_cast(y0)) + + // Ignore was_square since this is always square. + // (_, invsqrt) = SQRT_RATIO_M1(1, u1 * u2^2) + tmp: field.Tight_Field_Element = --- + field.fe_carry_square(&tmp, field.fe_relax_cast(&u2)) + field.fe_carry_mul(&tmp, field.fe_relax_cast(&u1), field.fe_relax_cast(&tmp)) + _ = field.fe_carry_sqrt_ratio_m1( + &tmp, + field.fe_relax_cast(&field.FE_ONE), + field.fe_relax_cast(&tmp), + ) + + // den1 = invsqrt * u1 + // den2 = invsqrt * u2 + // z_inv = den1 * den2 * t0 + den1, den2 := &u1, &u2 + z_inv: field.Tight_Field_Element = --- + field.fe_carry_mul(den1, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u1)) + field.fe_carry_mul(den2, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u2)) + field.fe_carry_mul(&z_inv, field.fe_relax_cast(den1), field.fe_relax_cast(den2)) + field.fe_carry_mul(&z_inv, field.fe_relax_cast(&z_inv), field.fe_relax_cast(t0)) + + // rotate = IS_NEGATIVE(t0 * z_inv) + // Note: Reordered from the RFC because invsqrt is no longer needed. + field.fe_carry_mul(&tmp, field.fe_relax_cast(t0), field.fe_relax_cast(&z_inv)) + rotate := field.fe_is_negative(&tmp) + + // ix0 = x0 * SQRT_M1 + // iy0 = y0 * SQRT_M1 + // enchanted_denominator = den1 * INVSQRT_A_MINUS_D + ix0, iy0: field.Tight_Field_Element = ---, --- + field.fe_carry_mul(&ix0, field.fe_relax_cast(x0), field.fe_relax_cast(&field.FE_SQRT_M1)) + field.fe_carry_mul(&iy0, field.fe_relax_cast(y0), field.fe_relax_cast(&field.FE_SQRT_M1)) + field.fe_carry_mul(&tmp, field.fe_relax_cast(den1), field.fe_relax_cast(&FE_INVSQRT_A_MINUS_D)) + + // Conditionally rotate x and y. + // x = CT_SELECT(iy0 IF rotate ELSE x0) + // y = CT_SELECT(ix0 IF rotate ELSE y0) + // z = z0 + // den_inv = CT_SELECT(enchanted_denominator IF rotate ELSE den2) + x, y: field.Tight_Field_Element = ---, --- + field.fe_cond_select(&x, x0, &iy0, rotate) + field.fe_cond_select(&y, y0, &ix0, rotate) + field.fe_cond_select(&tmp, den2, &tmp, rotate) + + // y = CT_SELECT(-y IF IS_NEGATIVE(x * z_inv) ELSE y) + field.fe_carry_mul(&x, field.fe_relax_cast(&x), field.fe_relax_cast(&z_inv)) + field.fe_cond_negate(&y, &y, field.fe_is_negative(&x)) + + // s = CT_ABS(den_inv * (z - y)) + field.fe_sub(&tmp1, z0, &y) + field.fe_carry_mul(&tmp, field.fe_relax_cast(&tmp), &tmp1) + field.fe_carry_abs(&tmp, &tmp) + + // 2. Return the 32-byte little-endian encoding of s. More + // specifically, this is the encoding of the canonical + // representation of s as an integer between 0 and p-1, inclusive. + dst_ := transmute(^[32]byte)(raw_data(dst)) + field.fe_to_bytes(dst_, &tmp) + + field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y}) + field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &tmp2}) +} + +// ge_add sets `ge = a + b`. +ge_add :: proc(ge, a, b: ^Group_Element) { + _ge_assert_initialized([]^Group_Element{a, b}) + + grp.ge_add(&ge._p, &a._p, &b._p) + ge._is_initialized = true +} + +// ge_double sets `ge = a + a`. +ge_double :: proc(ge, a: ^Group_Element) { + _ge_assert_initialized([]^Group_Element{a}) + + grp.ge_double(&ge._p, &a._p) + ge._is_initialized = true +} + +// ge_negate sets `ge = -a`. +ge_negate :: proc(ge, a: ^Group_Element) { + _ge_assert_initialized([]^Group_Element{a}) + + grp.ge_negate(&ge._p, &a._p) + ge._is_initialized = true +} + +// ge_scalarmult sets `ge = A * sc`. +ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) { + _ge_assert_initialized([]^Group_Element{A}) + + grp.ge_scalarmult(&ge._p, &A._p, sc) + ge._is_initialized = true +} + +// ge_scalarmult_generator sets `ge = G * sc` +ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) { + grp.ge_scalarmult_basepoint(&ge._p, sc) + ge._is_initialized = true +} + +// ge_scalarmult_vartime sets `ge = A * sc` in variable time. +ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) { + _ge_assert_initialized([]^Group_Element{A}) + + grp.ge_scalarmult_vartime(&ge._p, &A._p, sc) + ge._is_initialized = true +} + +// ge_double_scalarmult_generator_vartime sets `ge = A * a + G * b` in variable +// time. +ge_double_scalarmult_generator_vartime :: proc( + ge: ^Group_Element, + a: ^Scalar, + A: ^Group_Element, + b: ^Scalar, +) { + _ge_assert_initialized([]^Group_Element{A}) + + grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b) + ge._is_initialized = true +} + +// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`. +// Behavior for all other values of ctrl are undefined, +ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) { + _ge_assert_initialized([]^Group_Element{a}) + + grp.ge_cond_negate(&ge._p, &a._p, ctrl) + ge._is_initialized = true +} + +// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`. +// Behavior for all other values of ctrl are undefined, +ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) { + _ge_assert_initialized([]^Group_Element{ge, a}) + + grp.ge_cond_assign(&ge._p, &a._p, ctrl) +} + +// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`. +// Behavior for all other values of ctrl are undefined, +ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) { + _ge_assert_initialized([]^Group_Element{a, b}) + + grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl) + ge._is_initialized = true +} + +// ge_equal returns 1 iff `a == b`, and 0 otherwise. +@(require_results) +ge_equal :: proc(a, b: ^Group_Element) -> int { + _ge_assert_initialized([]^Group_Element{a, b}) + + // CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2) + ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, --- + field.fe_carry_mul(&ax_by, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.y)) + field.fe_carry_mul(&ay_bx, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.x)) + field.fe_carry_mul(&ay_by, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.y)) + field.fe_carry_mul(&ax_bx, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.x)) + + ret := field.fe_equal(&ax_by, &ay_bx) | field.fe_equal(&ay_by, &ax_bx) + + field.fe_clear_vec([]^field.Tight_Field_Element{&ax_by, &ay_bx, &ay_by, &ax_bx}) + + return ret +} + +// ge_is_identity returns 1 iff `ge` is the identity element, and 0 otherwise. +@(require_results) +ge_is_identity :: proc(ge: ^Group_Element) -> int { + return ge_equal(ge, &GE_IDENTITY) +} + +@(private) +ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) { + b_ := transmute(^[32]byte)(raw_data(b)) + + // The MAP function is defined on 32-byte strings as: + // + // 1. Mask the most significant bit in the final byte of the string, + // and interpret the string as an unsigned integer r in little- + // endian representation. Reduce r modulo p to obtain a field + // element t. + // * Masking the most significant bit is equivalent to interpreting + // the whole string as an unsigned integer in little-endian + // representation and then reducing it modulo 2^255. + t: field.Tight_Field_Element = --- + field.fe_from_bytes(&t, b_) + + // 2. Process t as follows: + // + // r = SQRT_M1 * t^2 + // u = (r + 1) * ONE_MINUS_D_SQ + // v = (-1 - r*D) * (r + D) + tmp1: field.Loose_Field_Element = --- + r, u, v: field.Tight_Field_Element = ---, ---, --- + + field.fe_carry_square(&r, field.fe_relax_cast(&t)) + field.fe_carry_mul(&r, field.fe_relax_cast(&field.FE_SQRT_M1), field.fe_relax_cast(&r)) + + field.fe_add(&tmp1, &field.FE_ONE, &r) + field.fe_carry_mul(&u, &tmp1, field.fe_relax_cast(&FE_ONE_MINUS_D_SQ)) + + field.fe_carry_mul(&v, field.fe_relax_cast(&r), field.fe_relax_cast(&grp.FE_D)) + field.fe_carry_add(&v, &field.FE_ONE, &v) + field.fe_carry_opp(&v, &v) + field.fe_add(&tmp1, &r, &grp.FE_D) + field.fe_carry_mul(&v, field.fe_relax_cast(&v), &tmp1) + + // (was_square, s) = SQRT_RATIO_M1(u, v) + // s_prime = -CT_ABS(s*t) + // s = CT_SELECT(s IF was_square ELSE s_prime) + // c = CT_SELECT(-1 IF was_square ELSE r) + s, s_prime, c: field.Tight_Field_Element = ---, ---, --- + was_square := field.fe_carry_sqrt_ratio_m1( + &s, + field.fe_relax_cast(&u), + field.fe_relax_cast(&v), + ) + field.fe_carry_mul(&s_prime, field.fe_relax_cast(&s), field.fe_relax_cast(&t)) + field.fe_carry_abs(&s_prime, &s_prime) + field.fe_carry_opp(&s_prime, &s_prime) + field.fe_cond_select(&s, &s_prime, &s, was_square) + field.fe_cond_select(&c, &r, &FE_NEG_ONE, was_square) + + // N = c * (r - 1) * D_MINUS_ONE_SQ - v + N: field.Tight_Field_Element = --- + field.fe_sub(&tmp1, &r, &field.FE_ONE) + field.fe_carry_mul(&N, field.fe_relax_cast(&c), &tmp1) + field.fe_carry_mul(&N, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_D_MINUS_ONE_SQUARED)) + field.fe_carry_sub(&N, &N, &v) + + // w0 = 2 * s * v + // w1 = N * SQRT_AD_MINUS_ONE + // w2 = 1 - s^2 + // w3 = 1 + s^2 + w0, w1: field.Tight_Field_Element = ---, --- + w2, w3: field.Loose_Field_Element = ---, --- + field.fe_carry_mul(&w0, field.fe_relax_cast(&s), field.fe_relax_cast(&v)) + field.fe_carry_add(&w0, &w0, &w0) + field.fe_carry_mul(&w1, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_SQRT_AD_MINUS_ONE)) + field.fe_carry_square(&s, field.fe_relax_cast(&s)) + field.fe_sub(&w2, &field.FE_ONE, &s) + field.fe_add(&w3, &field.FE_ONE, &s) + + // 3. Return the group element represented by the internal + // representation (w0*w3, w2*w1, w1*w3, w0*w2). + + field.fe_carry_mul(&ge._p.x, field.fe_relax_cast(&w0), &w3) + field.fe_carry_mul(&ge._p.y, &w2, field.fe_relax_cast(&w1)) + field.fe_carry_mul(&ge._p.z, field.fe_relax_cast(&w1), &w3) + field.fe_carry_mul(&ge._p.t, field.fe_relax_cast(&w0), &w2) + ge._is_initialized = true + + field.fe_clear_vec([]^field.Tight_Field_Element{&r, &u, &v, &s, &s_prime, &c, &N, &w0, &w1}) + field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &w2, &w3}) +} + +@(private) +_ge_assert_initialized :: proc(ges: []^Group_Element) { + for ge in ges { + if !ge._is_initialized { + panic("crypto/ristretto255: uninitialized group element") + } + } +} diff --git a/core/crypto/ristretto255/ristretto255_scalar.odin b/core/crypto/ristretto255/ristretto255_scalar.odin new file mode 100644 index 000000000..f581e5963 --- /dev/null +++ b/core/crypto/ristretto255/ristretto255_scalar.odin @@ -0,0 +1,97 @@ +package ristretto255 + +import grp "core:crypto/_edwards25519" + +// SCALAR_SIZE is the size of a byte-encoded ristretto255 scalar. +SCALAR_SIZE :: 32 +// WIDE_SCALAR_SIZE is the size of a wide byte-encoded ristretto255 +// scalar. +WIDE_SCALAR_SIZE :: 64 + +// Scalar is a ristretto255 scalar. The zero-initialized value is valid, +// and represents `0`. +Scalar :: grp.Scalar + +// sc_clear clears sc to the uninitialized state. +sc_clear :: proc "contextless" (sc: ^Scalar) { + grp.sc_clear(sc) +} + +// sc_set sets `sc = a`. +sc_set :: proc "contextless" (sc, a: ^Scalar) { + grp.sc_set(sc, a) +} + +// sc_set_u64 sets `sc = i`. +sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) { + grp.sc_set_u64(sc, i) +} + +// sc_set_bytes sets sc to the result of decoding b as a ristretto255 +// scalar, and returns true on success. +@(require_results) +sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool { + if len(b) != SCALAR_SIZE { + return false + } + + return grp.sc_set_bytes(sc, b) +} + +// sc_set_wide_bytes sets sc to the result of deriving a ristretto255 +// scalar, from a wide (512-bit) byte string by interpreting b as a +// little-endian value, and reducing it mod the group order. +sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) { + if len(b) != WIDE_SCALAR_SIZE { + panic("crypto/ristretto255: invalid wide input size") + } + + b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b)) + grp.sc_set_bytes_wide(sc, b_) +} + +// sc_bytes sets dst to the canonical encoding of sc. +sc_bytes :: proc(sc: ^Scalar, dst: []byte) { + if len(dst) != SCALAR_SIZE { + panic("crypto/ristretto255: invalid destination size") + } + + grp.sc_bytes(dst, sc) +} + +// sc_add sets `sc = a + b`. +sc_add :: proc "contextless" (sc, a, b: ^Scalar) { + grp.sc_add(sc, a, b) +} + +// sc_sub sets `sc = a - b`. +sc_sub :: proc "contextless" (sc, a, b: ^Scalar) { + grp.sc_sub(sc, a, b) +} + +// sc_negate sets `sc = -a`. +sc_negate :: proc "contextless" (sc, a: ^Scalar) { + grp.sc_negate(sc, a) +} + +// sc_mul sets `sc = a * b`. +sc_mul :: proc "contextless" (sc, a, b: ^Scalar) { + grp.sc_mul(sc, a, b) +} + +// sc_square sets `sc = a^2`. +sc_square :: proc "contextless" (sc, a: ^Scalar) { + grp.sc_square(sc, a) +} + +// sc_cond_assign sets `sc = sc` iff `ctrl == 0` and `sc = a` iff `ctrl == 1`. +// Behavior for all other values of ctrl are undefined, +sc_cond_assign :: proc(sc, a: ^Scalar, ctrl: int) { + grp.sc_cond_assign(sc, a, ctrl) +} + +// sc_equal returns 1 iff `a == b`, and 0 otherwise. +@(require_results) +sc_equal :: proc(a, b: ^Scalar) -> int { + return grp.sc_equal(a, b) +} diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index bc1aff607..cc0005840 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -37,6 +37,7 @@ import md5 "core:crypto/legacy/md5" import sha1 "core:crypto/legacy/sha1" import pbkdf2 "core:crypto/pbkdf2" import poly1305 "core:crypto/poly1305" +import ristretto255 "core:crypto/ristretto255" import sha2 "core:crypto/sha2" import sha3 "core:crypto/sha3" import shake "core:crypto/shake" @@ -158,6 +159,7 @@ _ :: keccak _ :: md5 _ :: pbkdf2 _ :: poly1305 +_ :: ristretto255 _ :: sha1 _ :: sha2 _ :: sha3