Files
p256k1/util.go

163 lines
4.1 KiB
Go

// Package p256k1 provides a pure Go implementation of the secp256k1 elliptic curve
// cryptographic primitives, ported from the libsecp256k1 C library.
package p256k1
import (
"crypto/subtle"
"encoding/binary"
"fmt"
"os"
"unsafe"
)
// Constants from the C implementation
const (
// Field prime: 2^256 - 2^32 - 977
FieldPrime = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"
// Group order (number of points on the curve)
GroupOrder = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
)
// Utility functions ported from util.h
// memclear clears memory to prevent leaking sensitive information
func memclear(ptr unsafe.Pointer, n uintptr) {
// Zero the memory
slice := (*[1 << 30]byte)(ptr)[:n:n]
for i := range slice {
slice[i] = 0
}
}
// memczero conditionally zeros memory if flag == 1. Flag must be 0 or 1. Constant time.
func memczero(s []byte, flag int) {
mask := byte(-flag)
for i := range s {
s[i] &= ^mask
}
}
// isZeroArray returns 1 if all elements of array s are 0, otherwise 0. Constant-time.
func isZeroArray(s []byte) (ret int) {
var acc byte
for i := range s {
acc |= s[i]
}
ret = subtle.ConstantTimeByteEq(acc, 0)
return
}
// intCmov conditionally moves an integer. If flag is true, set *r equal to *a; otherwise leave it.
// Constant-time. Both *r and *a must be initialized and non-negative.
func intCmov(r *int, a *int, flag int) {
*r = subtle.ConstantTimeSelect(flag, *a, *r)
}
// readBE32 reads a uint32 in big endian
func readBE32(p []byte) uint32 {
return binary.BigEndian.Uint32(p)
}
// writeBE32 writes a uint32 in big endian
func writeBE32(p []byte, x uint32) {
binary.BigEndian.PutUint32(p, x)
}
// readBE64 reads a uint64 in big endian
func readBE64(p []byte) uint64 {
return binary.BigEndian.Uint64(p)
}
// writeBE64 writes a uint64 in big endian
func writeBE64(p []byte, x uint64) {
binary.BigEndian.PutUint64(p, x)
}
// rotr32 rotates a uint32 to the right
func rotr32(x uint32, by uint) uint32 {
by &= 31 // Reduce rotation amount to avoid issues
return (x >> by) | (x << (32 - by))
}
// ctz32Var determines the number of trailing zero bits in a (non-zero) 32-bit x
func ctz32Var(x uint32) int {
if x == 0 {
panic("ctz32Var called with zero")
}
// Use De Bruijn sequence for bit scanning
debruijn := [32]uint8{
0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B,
0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B,
}
return int(debruijn[(x&-x)*0x04D7651F>>27])
}
// ctz64Var determines the number of trailing zero bits in a (non-zero) 64-bit x
func ctz64Var(x uint64) int {
if x == 0 {
panic("ctz64Var called with zero")
}
// Use De Bruijn sequence for bit scanning
debruijn := [64]uint8{
0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12,
}
return int(debruijn[(x&-x)*0x022FDD63CC95386D>>58])
}
// Callback represents an error callback function
type Callback struct {
Fn func(string, interface{})
Data interface{}
}
// call invokes the callback function
func (cb *Callback) call(text string) {
if cb.Fn != nil {
cb.Fn(text, cb.Data)
}
}
// Default callbacks
var (
defaultIllegalCallback = Callback{
Fn: func(str string, data interface{}) {
fmt.Fprintf(os.Stderr, "[libsecp256k1] illegal argument: %s\n", str)
os.Exit(1)
},
}
defaultErrorCallback = Callback{
Fn: func(str string, data interface{}) {
fmt.Fprintf(os.Stderr, "[libsecp256k1] internal consistency check failed: %s\n", str)
os.Exit(1)
},
}
)
// argCheck checks a condition and calls the illegal callback if it fails
func argCheck(cond bool, ctx *Context, msg string) (ok bool) {
if !cond {
if ctx != nil {
ctx.illegalCallback.call(msg)
} else {
defaultIllegalCallback.call(msg)
}
return false
}
return true
}
// verifyCheck checks a condition in verify mode (debug builds)
func verifyCheck(cond bool, msg string) {
if !cond {
defaultErrorCallback.call(msg)
}
}