pubkey.go raw

   1  package p256k1
   2  
   3  import (
   4  	"errors"
   5  )
   6  
   7  // PublicKey represents a secp256k1 public key
   8  type PublicKey struct {
   9  	data [64]byte // Internal representation
  10  }
  11  
  12  // Compression flags for public key serialization
  13  const (
  14  	ECCompressed   = 0x02
  15  	ECUncompressed = 0x04
  16  )
  17  
  18  // ECPubkeyParse parses a public key from bytes
  19  func ECPubkeyParse(pubkey *PublicKey, input []byte) error {
  20  	if len(input) == 0 {
  21  		return errors.New("input cannot be empty")
  22  	}
  23  	
  24  	var point GroupElementAffine
  25  	
  26  	switch len(input) {
  27  	case 33:
  28  		// Compressed format
  29  		if input[0] != 0x02 && input[0] != 0x03 {
  30  			return errors.New("invalid compressed public key prefix")
  31  		}
  32  		
  33  		// Extract X coordinate
  34  		var x FieldElement
  35  		if err := x.setB32(input[1:33]); err != nil {
  36  			return err
  37  		}
  38  		
  39  		// Determine Y coordinate from X and parity
  40  		odd := input[0] == 0x03
  41  		if !point.setXOVar(&x, odd) {
  42  			return errors.New("invalid public key")
  43  		}
  44  		
  45  	case 65:
  46  		// Uncompressed format
  47  		if input[0] != 0x04 {
  48  			return errors.New("invalid uncompressed public key prefix")
  49  		}
  50  		
  51  		// Extract X and Y coordinates
  52  		var x, y FieldElement
  53  		if err := x.setB32(input[1:33]); err != nil {
  54  			return err
  55  		}
  56  		if err := y.setB32(input[33:65]); err != nil {
  57  			return err
  58  		}
  59  		
  60  		point.setXY(&x, &y)
  61  		
  62  	default:
  63  		return errors.New("invalid public key length")
  64  	}
  65  	
  66  	// Validate the point is on the curve
  67  	if !point.isValid() {
  68  		return errors.New("public key not on curve")
  69  	}
  70  	
  71  	// Store in internal format
  72  	point.toBytes(pubkey.data[:])
  73  	
  74  	return nil
  75  }
  76  
  77  // ECPubkeySerialize serializes a public key to bytes
  78  func ECPubkeySerialize(output []byte, pubkey *PublicKey, flags uint) int {
  79  	// Load the public key
  80  	var point GroupElementAffine
  81  	point.fromBytes(pubkey.data[:])
  82  	
  83  	if point.isInfinity() {
  84  		return 0 // Invalid public key
  85  	}
  86  	
  87  	// Normalize coordinates
  88  	point.x.normalize()
  89  	point.y.normalize()
  90  	
  91  	if flags == ECCompressed {
  92  		if len(output) < 33 {
  93  			return 0 // Buffer too small
  94  		}
  95  		
  96  		// Compressed format: 0x02/0x03 + X coordinate
  97  		if point.y.isOdd() {
  98  			output[0] = 0x03
  99  		} else {
 100  			output[0] = 0x02
 101  		}
 102  		point.x.getB32(output[1:33])
 103  		return 33
 104  		
 105  	} else if flags == ECUncompressed {
 106  		if len(output) < 65 {
 107  			return 0 // Buffer too small
 108  		}
 109  		
 110  		// Uncompressed format: 0x04 + X + Y coordinates
 111  		output[0] = 0x04
 112  		point.x.getB32(output[1:33])
 113  		point.y.getB32(output[33:65])
 114  		return 65
 115  		
 116  	} else {
 117  		return 0 // Invalid flags
 118  	}
 119  }
 120  
 121  // ECPubkeyCmp compares two public keys
 122  func ECPubkeyCmp(pubkey1, pubkey2 *PublicKey) int {
 123  	// Load both public keys
 124  	var point1, point2 GroupElementAffine
 125  	point1.fromBytes(pubkey1.data[:])
 126  	point2.fromBytes(pubkey2.data[:])
 127  	
 128  	if point1.equal(&point2) {
 129  		return 0
 130  	}
 131  	
 132  	// For ordering, compare the serialized forms
 133  	var buf1, buf2 [33]byte
 134  	ECPubkeySerialize(buf1[:], pubkey1, ECCompressed)
 135  	ECPubkeySerialize(buf2[:], pubkey2, ECCompressed)
 136  	
 137  	for i := 0; i < 33; i++ {
 138  		if buf1[i] < buf2[i] {
 139  			return -1
 140  		}
 141  		if buf1[i] > buf2[i] {
 142  			return 1
 143  		}
 144  	}
 145  	
 146  	return 0
 147  }
 148  
 149  // ECPubkeyCreate creates a public key from a private key
 150  func ECPubkeyCreate(pubkey *PublicKey, seckey []byte) error {
 151  	if len(seckey) != 32 {
 152  		return errors.New("private key must be 32 bytes")
 153  	}
 154  	
 155  	// Parse the private key as a scalar
 156  	var scalar Scalar
 157  	if !scalar.setB32Seckey(seckey) {
 158  		return errors.New("invalid private key")
 159  	}
 160  	
 161  	// Compute pubkey = scalar * G
 162  	var point GroupElementJacobian
 163  	EcmultGen(&point, &scalar)
 164  	
 165  	// Convert to affine and store directly - optimize by avoiding intermediate copy
 166  	var affine GroupElementAffine
 167  	affine.setGEJ(&point)
 168  	
 169  	// Normalize in-place and write directly to pubkey.data to avoid copy allocation
 170  	affine.x.normalize()
 171  	affine.y.normalize()
 172  	affine.x.getB32(pubkey.data[:32])
 173  	affine.y.getB32(pubkey.data[32:64])
 174  	
 175  	// Clear sensitive data
 176  	scalar.clear()
 177  	point.clear()
 178  	affine.clear()
 179  	
 180  	return nil
 181  }
 182  
 183  // pubkeyLoad loads a public key from internal format (helper function)
 184  func pubkeyLoad(point *GroupElementAffine, pubkey *PublicKey) {
 185  	point.fromBytes(pubkey.data[:])
 186  }
 187  
 188  // pubkeySave saves a public key to internal format (helper function)
 189  func pubkeySave(pubkey *PublicKey, point *GroupElementAffine) {
 190  	point.toBytes(pubkey.data[:])
 191  }
 192