This commit introduces two new files: `BENCHMARK_RESULTS.md` and `benchmark_results.txt`, which document the performance metrics of various cryptographic operations, including ECDSA signing, verification, and ECDH key exchange. The results provide insights into operation times, memory allocations, and comparisons with C implementations. Additionally, new test files for ECDSA and ECDH functionalities have been added, ensuring comprehensive coverage and validation of the implemented algorithms. This enhances the overall robustness and performance understanding of the secp256k1 implementation.
677 lines
14 KiB
Go
677 lines
14 KiB
Go
package p256k1
|
|
|
|
// No imports needed for basic group operations
|
|
|
|
// GroupElementAffine represents a point on the secp256k1 curve in affine coordinates (x, y)
|
|
type GroupElementAffine struct {
|
|
x, y FieldElement
|
|
infinity bool
|
|
}
|
|
|
|
// GroupElementJacobian represents a point on the secp256k1 curve in Jacobian coordinates (x, y, z)
|
|
// where the affine coordinates are (x/z^2, y/z^3)
|
|
type GroupElementJacobian struct {
|
|
x, y, z FieldElement
|
|
infinity bool
|
|
}
|
|
|
|
// GroupElementStorage represents a point in storage format (compressed coordinates)
|
|
type GroupElementStorage struct {
|
|
x [32]byte
|
|
y [32]byte
|
|
}
|
|
|
|
// Generator point G for secp256k1 curve
|
|
var (
|
|
// Generator point in affine coordinates
|
|
// G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
|
|
// 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
|
|
GeneratorX FieldElement
|
|
GeneratorY FieldElement
|
|
Generator GroupElementAffine
|
|
)
|
|
|
|
// Initialize generator point
|
|
func init() {
|
|
// Generator X coordinate: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
|
|
gxBytes := []byte{
|
|
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07,
|
|
0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98,
|
|
}
|
|
|
|
// Generator Y coordinate: 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
|
|
gyBytes := []byte{
|
|
0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8,
|
|
0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8,
|
|
}
|
|
|
|
GeneratorX.setB32(gxBytes)
|
|
GeneratorY.setB32(gyBytes)
|
|
|
|
// Create generator point
|
|
Generator = GroupElementAffine{
|
|
x: GeneratorX,
|
|
y: GeneratorY,
|
|
infinity: false,
|
|
}
|
|
}
|
|
|
|
// NewGroupElementAffine creates a new affine group element
|
|
func NewGroupElementAffine() *GroupElementAffine {
|
|
return &GroupElementAffine{
|
|
x: FieldElementZero,
|
|
y: FieldElementZero,
|
|
infinity: true,
|
|
}
|
|
}
|
|
|
|
// NewGroupElementJacobian creates a new Jacobian group element
|
|
func NewGroupElementJacobian() *GroupElementJacobian {
|
|
return &GroupElementJacobian{
|
|
x: FieldElementZero,
|
|
y: FieldElementZero,
|
|
z: FieldElementZero,
|
|
infinity: true,
|
|
}
|
|
}
|
|
|
|
// setXY sets a group element to the point with given coordinates
|
|
func (r *GroupElementAffine) setXY(x, y *FieldElement) {
|
|
r.x = *x
|
|
r.y = *y
|
|
r.infinity = false
|
|
}
|
|
|
|
// setXOVar sets a group element to the point with given X coordinate and Y oddness
|
|
func (r *GroupElementAffine) setXOVar(x *FieldElement, odd bool) bool {
|
|
// Compute y^2 = x^3 + 7 (secp256k1 curve equation)
|
|
var x2, x3, y2 FieldElement
|
|
x2.sqr(x)
|
|
x3.mul(&x2, x)
|
|
|
|
// Add 7 (the curve parameter b)
|
|
var seven FieldElement
|
|
seven.setInt(7)
|
|
y2 = x3
|
|
y2.add(&seven)
|
|
|
|
// Try to compute square root
|
|
var y FieldElement
|
|
if !y.sqrt(&y2) {
|
|
return false // x is not on the curve
|
|
}
|
|
|
|
// Choose the correct square root based on oddness
|
|
y.normalize()
|
|
if y.isOdd() != odd {
|
|
y.negate(&y, 1)
|
|
y.normalize()
|
|
}
|
|
|
|
r.setXY(x, &y)
|
|
return true
|
|
}
|
|
|
|
// isInfinity returns true if the group element is the point at infinity
|
|
func (r *GroupElementAffine) isInfinity() bool {
|
|
return r.infinity
|
|
}
|
|
|
|
// isValid checks if the group element is valid (on the curve)
|
|
func (r *GroupElementAffine) isValid() bool {
|
|
if r.infinity {
|
|
return true
|
|
}
|
|
|
|
// Check curve equation: y^2 = x^3 + 7
|
|
var lhs, rhs, x2, x3 FieldElement
|
|
|
|
// Normalize coordinates
|
|
var xNorm, yNorm FieldElement
|
|
xNorm = r.x
|
|
yNorm = r.y
|
|
xNorm.normalize()
|
|
yNorm.normalize()
|
|
|
|
// Compute y^2
|
|
lhs.sqr(&yNorm)
|
|
|
|
// Compute x^3 + 7
|
|
x2.sqr(&xNorm)
|
|
x3.mul(&x2, &xNorm)
|
|
rhs = x3
|
|
var seven FieldElement
|
|
seven.setInt(7)
|
|
rhs.add(&seven)
|
|
|
|
// Normalize both sides
|
|
lhs.normalize()
|
|
rhs.normalize()
|
|
|
|
return lhs.equal(&rhs)
|
|
}
|
|
|
|
// negate sets r to the negation of a (mirror around X axis)
|
|
func (r *GroupElementAffine) negate(a *GroupElementAffine) {
|
|
if a.infinity {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
r.x = a.x
|
|
r.y.negate(&a.y, a.y.magnitude)
|
|
r.infinity = false
|
|
}
|
|
|
|
// setInfinity sets the group element to the point at infinity
|
|
func (r *GroupElementAffine) setInfinity() {
|
|
r.x = FieldElementZero
|
|
r.y = FieldElementZero
|
|
r.infinity = true
|
|
}
|
|
|
|
// equal returns true if two group elements are equal
|
|
func (r *GroupElementAffine) equal(a *GroupElementAffine) bool {
|
|
if r.infinity && a.infinity {
|
|
return true
|
|
}
|
|
if r.infinity || a.infinity {
|
|
return false
|
|
}
|
|
|
|
// Normalize both points
|
|
var rNorm, aNorm GroupElementAffine
|
|
rNorm = *r
|
|
aNorm = *a
|
|
rNorm.x.normalize()
|
|
rNorm.y.normalize()
|
|
aNorm.x.normalize()
|
|
aNorm.y.normalize()
|
|
|
|
return rNorm.x.equal(&aNorm.x) && rNorm.y.equal(&aNorm.y)
|
|
}
|
|
|
|
// Jacobian coordinate operations
|
|
|
|
// setInfinity sets the Jacobian group element to the point at infinity
|
|
func (r *GroupElementJacobian) setInfinity() {
|
|
r.x = FieldElementZero
|
|
r.y = FieldElementOne
|
|
r.z = FieldElementZero
|
|
r.infinity = true
|
|
}
|
|
|
|
// isInfinity returns true if the Jacobian group element is the point at infinity
|
|
func (r *GroupElementJacobian) isInfinity() bool {
|
|
return r.infinity
|
|
}
|
|
|
|
// setGE sets a Jacobian element from an affine element
|
|
func (r *GroupElementJacobian) setGE(a *GroupElementAffine) {
|
|
if a.infinity {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
r.x = a.x
|
|
r.y = a.y
|
|
r.z = FieldElementOne
|
|
r.infinity = false
|
|
}
|
|
|
|
// setGEJ sets an affine element from a Jacobian element
|
|
// This follows the C secp256k1_ge_set_gej_var implementation exactly
|
|
func (r *GroupElementAffine) setGEJ(a *GroupElementJacobian) {
|
|
if a.infinity {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
// Following C code exactly: secp256k1_ge_set_gej_var modifies the input!
|
|
// We need to make a copy to avoid modifying the original
|
|
var aCopy GroupElementJacobian
|
|
aCopy = *a
|
|
|
|
r.infinity = false
|
|
|
|
// secp256k1_fe_inv_var(&a->z, &a->z);
|
|
// Note: inv normalizes the input internally
|
|
aCopy.z.inv(&aCopy.z)
|
|
|
|
// secp256k1_fe_sqr(&z2, &a->z);
|
|
var z2 FieldElement
|
|
z2.sqr(&aCopy.z)
|
|
|
|
// secp256k1_fe_mul(&z3, &a->z, &z2);
|
|
var z3 FieldElement
|
|
z3.mul(&aCopy.z, &z2)
|
|
|
|
// secp256k1_fe_mul(&a->x, &a->x, &z2);
|
|
aCopy.x.mul(&aCopy.x, &z2)
|
|
|
|
// secp256k1_fe_mul(&a->y, &a->y, &z3);
|
|
aCopy.y.mul(&aCopy.y, &z3)
|
|
|
|
// secp256k1_fe_set_int(&a->z, 1);
|
|
aCopy.z.setInt(1)
|
|
|
|
// secp256k1_ge_set_xy(r, &a->x, &a->y);
|
|
r.x = aCopy.x
|
|
r.y = aCopy.y
|
|
}
|
|
|
|
// negate sets r to the negation of a Jacobian point
|
|
func (r *GroupElementJacobian) negate(a *GroupElementJacobian) {
|
|
if a.infinity {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
r.x = a.x
|
|
r.y.negate(&a.y, a.y.magnitude)
|
|
r.z = a.z
|
|
r.infinity = false
|
|
}
|
|
|
|
// double sets r = 2*a (point doubling in Jacobian coordinates)
|
|
// This follows the C secp256k1_gej_double implementation exactly
|
|
func (r *GroupElementJacobian) double(a *GroupElementJacobian) {
|
|
// Exact C translation - no early return for infinity
|
|
// From C code - exact translation with proper variable reuse:
|
|
// secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */
|
|
// secp256k1_fe_sqr(&s, &a->y); /* S = Y1^2 (1) */
|
|
// secp256k1_fe_sqr(&l, &a->x); /* L = X1^2 (1) */
|
|
// secp256k1_fe_mul_int(&l, 3); /* L = 3*X1^2 (3) */
|
|
// secp256k1_fe_half(&l); /* L = 3/2*X1^2 (2) */
|
|
// secp256k1_fe_negate(&t, &s, 1); /* T = -S (2) */
|
|
// secp256k1_fe_mul(&t, &t, &a->x); /* T = -X1*S (1) */
|
|
// secp256k1_fe_sqr(&r->x, &l); /* X3 = L^2 (1) */
|
|
// secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + T (2) */
|
|
// secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + 2*T (3) */
|
|
// secp256k1_fe_sqr(&s, &s); /* S' = S^2 (1) */
|
|
// secp256k1_fe_add(&t, &r->x); /* T' = X3 + T (4) */
|
|
// secp256k1_fe_mul(&r->y, &t, &l); /* Y3 = L*(X3 + T) (1) */
|
|
// secp256k1_fe_add(&r->y, &s); /* Y3 = L*(X3 + T) + S^2 (2) */
|
|
// secp256k1_fe_negate(&r->y, &r->y, 2); /* Y3 = -(L*(X3 + T) + S^2) (3) */
|
|
|
|
var l, s, t FieldElement
|
|
|
|
r.infinity = a.infinity
|
|
|
|
// Z3 = Y1*Z1 (1)
|
|
r.z.mul(&a.z, &a.y)
|
|
|
|
// S = Y1^2 (1)
|
|
s.sqr(&a.y)
|
|
|
|
// L = X1^2 (1)
|
|
l.sqr(&a.x)
|
|
|
|
// L = 3*X1^2 (3)
|
|
l.mulInt(3)
|
|
|
|
// L = 3/2*X1^2 (2)
|
|
l.half(&l)
|
|
|
|
// T = -S (2) where S = Y1^2
|
|
t.negate(&s, 1)
|
|
|
|
// T = -X1*S = -X1*Y1^2 (1)
|
|
t.mul(&t, &a.x)
|
|
|
|
// X3 = L^2 (1)
|
|
r.x.sqr(&l)
|
|
|
|
// X3 = L^2 + T (2)
|
|
r.x.add(&t)
|
|
|
|
// X3 = L^2 + 2*T (3)
|
|
r.x.add(&t)
|
|
|
|
// S = S^2 = (Y1^2)^2 = Y1^4 (1)
|
|
s.sqr(&s)
|
|
|
|
// T = X3 + T = X3 + (-X1*Y1^2) (4)
|
|
t.add(&r.x)
|
|
|
|
// Y3 = L*(X3 + T) = L*(X3 + (-X1*Y1^2)) (1)
|
|
r.y.mul(&t, &l)
|
|
|
|
// Y3 = L*(X3 + T) + S^2 = L*(X3 + (-X1*Y1^2)) + Y1^4 (2)
|
|
r.y.add(&s)
|
|
|
|
// Y3 = -(L*(X3 + T) + S^2) (3)
|
|
r.y.negate(&r.y, 2)
|
|
}
|
|
|
|
// addVar sets r = a + b (variable-time point addition in Jacobian coordinates)
|
|
// This follows the C secp256k1_gej_add_var implementation exactly
|
|
// Operations: 12 mul, 4 sqr, 11 add/negate/normalizes_to_zero
|
|
func (r *GroupElementJacobian) addVar(a, b *GroupElementJacobian) {
|
|
// Handle infinity cases
|
|
if a.infinity {
|
|
*r = *b
|
|
return
|
|
}
|
|
if b.infinity {
|
|
*r = *a
|
|
return
|
|
}
|
|
|
|
// Following C code exactly: secp256k1_gej_add_var
|
|
// z22 = b->z^2
|
|
// z12 = a->z^2
|
|
// u1 = a->x * z22
|
|
// u2 = b->x * z12
|
|
// s1 = a->y * z22 * b->z
|
|
// s2 = b->y * z12 * a->z
|
|
// h = u2 - u1
|
|
// i = s2 - s1
|
|
// If h == 0 and i == 0: double(a)
|
|
// If h == 0 and i != 0: infinity
|
|
// Otherwise: add
|
|
|
|
var z22, z12, u1, u2, s1, s2, h, i, h2, h3, t FieldElement
|
|
|
|
// z22 = b->z^2
|
|
z22.sqr(&b.z)
|
|
|
|
// z12 = a->z^2
|
|
z12.sqr(&a.z)
|
|
|
|
// u1 = a->x * z22
|
|
u1.mul(&a.x, &z22)
|
|
|
|
// u2 = b->x * z12
|
|
u2.mul(&b.x, &z12)
|
|
|
|
// s1 = a->y * z22 * b->z
|
|
s1.mul(&a.y, &z22)
|
|
s1.mul(&s1, &b.z)
|
|
|
|
// s2 = b->y * z12 * a->z
|
|
s2.mul(&b.y, &z12)
|
|
s2.mul(&s2, &a.z)
|
|
|
|
// h = u2 - u1
|
|
h.negate(&u1, 1)
|
|
h.add(&u2)
|
|
|
|
// i = s2 - s1
|
|
i.negate(&s2, 1)
|
|
i.add(&s1)
|
|
|
|
// Check if h normalizes to zero
|
|
if h.normalizesToZeroVar() {
|
|
if i.normalizesToZeroVar() {
|
|
// Points are equal - double
|
|
r.double(a)
|
|
return
|
|
} else {
|
|
// Points are negatives - result is infinity
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
}
|
|
|
|
// General addition case
|
|
r.infinity = false
|
|
|
|
// t = h * b->z
|
|
t.mul(&h, &b.z)
|
|
|
|
// r->z = a->z * t
|
|
r.z.mul(&a.z, &t)
|
|
|
|
// h2 = h^2
|
|
h2.sqr(&h)
|
|
|
|
// h2 = -h2
|
|
h2.negate(&h2, 1)
|
|
|
|
// h3 = h2 * h
|
|
h3.mul(&h2, &h)
|
|
|
|
// t = u1 * h2
|
|
t.mul(&u1, &h2)
|
|
|
|
// r->x = i^2
|
|
r.x.sqr(&i)
|
|
|
|
// r->x = i^2 + h3
|
|
r.x.add(&h3)
|
|
|
|
// r->x = i^2 + h3 + t
|
|
r.x.add(&t)
|
|
|
|
// r->x = i^2 + h3 + 2*t
|
|
r.x.add(&t)
|
|
|
|
// t = t + r->x
|
|
t.add(&r.x)
|
|
|
|
// r->y = t * i
|
|
r.y.mul(&t, &i)
|
|
|
|
// h3 = h3 * s1
|
|
h3.mul(&h3, &s1)
|
|
|
|
// r->y = t * i + h3
|
|
r.y.add(&h3)
|
|
}
|
|
|
|
// addGE sets r = a + b where a is Jacobian and b is affine
|
|
// This follows the C secp256k1_gej_add_ge_var implementation exactly
|
|
// Operations: 8 mul, 3 sqr, 11 add/negate/normalizes_to_zero
|
|
func (r *GroupElementJacobian) addGE(a *GroupElementJacobian, b *GroupElementAffine) {
|
|
if a.infinity {
|
|
r.setGE(b)
|
|
return
|
|
}
|
|
if b.infinity {
|
|
*r = *a
|
|
return
|
|
}
|
|
|
|
// Following C code exactly: secp256k1_gej_add_ge_var
|
|
// z12 = a->z^2
|
|
// u1 = a->x
|
|
// u2 = b->x * z12
|
|
// s1 = a->y
|
|
// s2 = b->y * z12 * a->z
|
|
// h = u2 - u1
|
|
// i = s2 - s1
|
|
// If h == 0 and i == 0: double(a)
|
|
// If h == 0 and i != 0: infinity
|
|
// Otherwise: add
|
|
|
|
var z12, u1, u2, s1, s2, h, i, h2, h3, t FieldElement
|
|
|
|
// z12 = a->z^2
|
|
z12.sqr(&a.z)
|
|
|
|
// u1 = a->x
|
|
u1 = a.x
|
|
|
|
// u2 = b->x * z12
|
|
u2.mul(&b.x, &z12)
|
|
|
|
// s1 = a->y
|
|
s1 = a.y
|
|
|
|
// s2 = b->y * z12 * a->z
|
|
s2.mul(&b.y, &z12)
|
|
s2.mul(&s2, &a.z)
|
|
|
|
// h = u2 - u1
|
|
h.negate(&u1, a.x.magnitude)
|
|
h.add(&u2)
|
|
|
|
// i = s2 - s1
|
|
i.negate(&s2, 1)
|
|
i.add(&s1)
|
|
|
|
// Check if h normalizes to zero
|
|
if h.normalizesToZeroVar() {
|
|
if i.normalizesToZeroVar() {
|
|
// Points are equal - double
|
|
r.double(a)
|
|
return
|
|
} else {
|
|
// Points are negatives - result is infinity
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
}
|
|
|
|
// General addition case
|
|
r.infinity = false
|
|
|
|
// r->z = a->z * h
|
|
r.z.mul(&a.z, &h)
|
|
|
|
// h2 = h^2
|
|
h2.sqr(&h)
|
|
|
|
// h2 = -h2
|
|
h2.negate(&h2, 1)
|
|
|
|
// h3 = h2 * h
|
|
h3.mul(&h2, &h)
|
|
|
|
// t = u1 * h2
|
|
t.mul(&u1, &h2)
|
|
|
|
// r->x = i^2
|
|
r.x.sqr(&i)
|
|
|
|
// r->x = i^2 + h3
|
|
r.x.add(&h3)
|
|
|
|
// r->x = i^2 + h3 + t
|
|
r.x.add(&t)
|
|
|
|
// r->x = i^2 + h3 + 2*t
|
|
r.x.add(&t)
|
|
|
|
// t = t + r->x
|
|
t.add(&r.x)
|
|
|
|
// r->y = t * i
|
|
r.y.mul(&t, &i)
|
|
|
|
// h3 = h3 * s1
|
|
h3.mul(&h3, &s1)
|
|
|
|
// r->y = t * i + h3
|
|
r.y.add(&h3)
|
|
}
|
|
|
|
// clear clears a group element to prevent leaking sensitive information
|
|
func (r *GroupElementAffine) clear() {
|
|
r.x.clear()
|
|
r.y.clear()
|
|
r.infinity = true
|
|
}
|
|
|
|
// clear clears a Jacobian group element
|
|
func (r *GroupElementJacobian) clear() {
|
|
r.x.clear()
|
|
r.y.clear()
|
|
r.z.clear()
|
|
r.infinity = true
|
|
}
|
|
|
|
// toStorage converts a group element to storage format
|
|
func (r *GroupElementAffine) toStorage(s *GroupElementStorage) {
|
|
if r.infinity {
|
|
// Store infinity as all zeros
|
|
for i := range s.x {
|
|
s.x[i] = 0
|
|
s.y[i] = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
// Normalize and convert to bytes
|
|
var normalized GroupElementAffine
|
|
normalized = *r
|
|
normalized.x.normalize()
|
|
normalized.y.normalize()
|
|
|
|
normalized.x.getB32(s.x[:])
|
|
normalized.y.getB32(s.y[:])
|
|
}
|
|
|
|
// fromStorage converts from storage format to group element
|
|
func (r *GroupElementAffine) fromStorage(s *GroupElementStorage) {
|
|
// Check if it's the infinity point (all zeros)
|
|
var allZero bool = true
|
|
for i := range s.x {
|
|
if s.x[i] != 0 || s.y[i] != 0 {
|
|
allZero = false
|
|
break
|
|
}
|
|
}
|
|
|
|
if allZero {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
// Convert from bytes
|
|
r.x.setB32(s.x[:])
|
|
r.y.setB32(s.y[:])
|
|
r.infinity = false
|
|
}
|
|
|
|
// toBytes converts a group element to byte representation
|
|
func (r *GroupElementAffine) toBytes(buf []byte) {
|
|
if len(buf) < 64 {
|
|
panic("buffer too small for group element")
|
|
}
|
|
|
|
if r.infinity {
|
|
// Represent infinity as all zeros
|
|
for i := range buf[:64] {
|
|
buf[i] = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
// Normalize and convert
|
|
var normalized GroupElementAffine
|
|
normalized = *r
|
|
normalized.x.normalize()
|
|
normalized.y.normalize()
|
|
|
|
normalized.x.getB32(buf[:32])
|
|
normalized.y.getB32(buf[32:64])
|
|
}
|
|
|
|
// fromBytes converts from byte representation to group element
|
|
func (r *GroupElementAffine) fromBytes(buf []byte) {
|
|
if len(buf) < 64 {
|
|
panic("buffer too small for group element")
|
|
}
|
|
|
|
// Check if it's all zeros (infinity)
|
|
var allZero bool = true
|
|
for i := 0; i < 64; i++ {
|
|
if buf[i] != 0 {
|
|
allZero = false
|
|
break
|
|
}
|
|
}
|
|
|
|
if allZero {
|
|
r.setInfinity()
|
|
return
|
|
}
|
|
|
|
// Convert from bytes
|
|
r.x.setB32(buf[:32])
|
|
r.y.setB32(buf[32:64])
|
|
r.infinity = false
|
|
}
|