Files
did-it/crypto/p256/private.go

156 lines
3.9 KiB
Go
Raw Normal View History

package p256
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/ucan-wg/go-did-it/crypto"
)
2025-07-09 17:58:09 +02:00
var _ crypto.PrivateKeySigningBytes = &PrivateKey{}
var _ crypto.PrivateKeySigningASN1 = &PrivateKey{}
2025-07-03 15:55:58 +02:00
var _ crypto.PrivateKeyToBytes = &PrivateKey{}
var _ crypto.PrivateKeyKeyExchange = &PrivateKey{}
2025-06-25 16:46:43 +02:00
type PrivateKey struct {
k *ecdsa.PrivateKey
}
// PrivateKeyFromBytes converts a serialized public key to a PrivateKey.
// This compact serialization format is the raw key material, without metadata or structure.
// It errors if the slice is not the right size.
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
if len(b) != PrivateKeyBytesSize {
return nil, fmt.Errorf("invalid P-256 private key size")
}
2025-06-25 16:46:43 +02:00
res := &PrivateKey{
k: &ecdsa.PrivateKey{
D: new(big.Int).SetBytes(b),
PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
},
}
// recompute the public key
2025-06-25 16:46:43 +02:00
res.k.PublicKey.X, res.k.PublicKey.Y = res.k.PublicKey.Curve.ScalarBaseMult(b)
2025-06-25 16:46:43 +02:00
return res, nil
}
// PrivateKeyFromPKCS8DER decodes a PKCS#8 DER (binary) encoded private key.
func PrivateKeyFromPKCS8DER(bytes []byte) (*PrivateKey, error) {
priv, err := x509.ParsePKCS8PrivateKey(bytes)
if err != nil {
return nil, err
}
ecdsaPriv, ok := priv.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("invalid private key type")
}
2025-06-25 16:46:43 +02:00
return &PrivateKey{k: ecdsaPriv}, nil
}
// PrivateKeyFromPKCS8PEM decodes an PKCS#8 PEM (string) encoded private key.
func PrivateKeyFromPKCS8PEM(str string) (*PrivateKey, error) {
block, _ := pem.Decode([]byte(str))
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
if block.Type != pemPrivBlockType {
return nil, fmt.Errorf("incorrect PEM block type")
}
return PrivateKeyFromPKCS8DER(block.Bytes)
}
func (p *PrivateKey) Equal(other crypto.PrivateKey) bool {
if other, ok := other.(*PrivateKey); ok {
2025-06-25 16:46:43 +02:00
return p.k.Equal(other.k)
}
return false
}
func (p *PrivateKey) Public() crypto.PublicKey {
2025-06-25 16:46:43 +02:00
ecdhPub := p.k.Public().(*ecdsa.PublicKey)
return &PublicKey{k: ecdhPub}
}
func (p *PrivateKey) ToBytes() []byte {
// fixed size buffer that can get allocated on the caller's stack after inlining.
var buf [PrivateKeyBytesSize]byte
2025-06-25 16:46:43 +02:00
(p.k).D.FillBytes(buf[:])
return buf[:]
}
func (p *PrivateKey) ToPKCS8DER() []byte {
2025-06-25 16:46:43 +02:00
res, _ := x509.MarshalPKCS8PrivateKey(p.k)
return res
}
func (p *PrivateKey) ToPKCS8PEM() string {
der := p.ToPKCS8DER()
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPrivBlockType,
Bytes: der,
}))
}
// The default signing hash is SHA-256.
func (p *PrivateKey) SignToBytes(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
params := crypto.CollectSigningOptions(opts)
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
2025-06-25 16:46:43 +02:00
r, s, err := ecdsa.Sign(rand.Reader, p.k, hash[:])
if err != nil {
return nil, err
}
2025-06-25 15:53:29 +02:00
sig := make([]byte, SignatureBytesSize)
r.FillBytes(sig[:SignatureBytesSize/2])
s.FillBytes(sig[SignatureBytesSize/2:])
return sig, nil
}
// The default signing hash is SHA-256.
func (p *PrivateKey) SignToASN1(message []byte, opts ...crypto.SigningOption) ([]byte, error) {
params := crypto.CollectSigningOptions(opts)
hasher := params.HashOrDefault(crypto.SHA256).New()
hasher.Write(message)
hash := hasher.Sum(nil)
2025-06-25 16:46:43 +02:00
return ecdsa.SignASN1(rand.Reader, p.k, hash[:])
}
func (p *PrivateKey) PublicKeyIsCompatible(remote crypto.PublicKey) bool {
if _, ok := remote.(*PublicKey); ok {
return true
}
return false
}
func (p *PrivateKey) KeyExchange(remote crypto.PublicKey) ([]byte, error) {
if remote, ok := remote.(*PublicKey); ok {
// First, we need to convert the ECDSA (signing only) to the equivalent ECDH keys
2025-06-25 16:46:43 +02:00
ecdhPriv, err := p.k.ECDH()
if err != nil {
return nil, err
}
2025-06-25 16:46:43 +02:00
ecdhPub, err := remote.k.ECDH()
if err != nil {
return nil, err
}
return ecdhPriv.ECDH(ecdhPub)
}
return nil, fmt.Errorf("incompatible public key")
}