116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package x25519
|
|
|
|
import (
|
|
"crypto/ecdh"
|
|
"crypto/sha512"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
|
|
"code.sonr.org/go/did-it/crypto"
|
|
"code.sonr.org/go/did-it/crypto/ed25519"
|
|
)
|
|
|
|
var _ crypto.PrivateKeyKeyExchange = (*PrivateKey)(nil)
|
|
|
|
type PrivateKey struct {
|
|
k *ecdh.PrivateKey
|
|
}
|
|
|
|
// PrivateKeyFromBytes converts a serialized private key to a PrivateKey.
|
|
// This compact serialization format is the raw key material, without metadata or structure.
|
|
// It errors if len(privateKey) is not [PrivateKeyBytesSize].
|
|
func PrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
|
// this already check the size of b
|
|
priv, err := ecdh.X25519().NewPrivateKey(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PrivateKey{k: priv}, nil
|
|
}
|
|
|
|
// PrivateKeyFromEd25519 converts an ed25519 private key to a x25519 private key.
|
|
// It errors if the slice is not the right size.
|
|
//
|
|
// This function is based on the algorithm described in https://datatracker.ietf.org/doc/html/draft-ietf-core-oscore-groupcomm#name-curve25519
|
|
func PrivateKeyFromEd25519(priv ed25519.PrivateKey) (*PrivateKey, error) {
|
|
// get the 32-byte seed (first half of the private key)
|
|
seed := priv.Seed()
|
|
|
|
h := sha512.Sum512(seed)
|
|
|
|
// clamp as per the X25519 spec
|
|
h[0] &= 248
|
|
h[31] &= 127
|
|
h[31] |= 64
|
|
|
|
return PrivateKeyFromBytes(h[:32])
|
|
}
|
|
|
|
// 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
|
|
}
|
|
ecdhPriv, ok := priv.(*ecdh.PrivateKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid private key type")
|
|
}
|
|
return &PrivateKey{k: ecdhPriv}, 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 {
|
|
return p.k.Equal(other.k)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *PrivateKey) Public() crypto.PublicKey {
|
|
return &PublicKey{k: p.k.Public().(*ecdh.PublicKey)}
|
|
}
|
|
|
|
func (p *PrivateKey) ToBytes() []byte {
|
|
return p.k.Bytes()
|
|
}
|
|
|
|
func (p *PrivateKey) ToPKCS8DER() []byte {
|
|
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,
|
|
}))
|
|
}
|
|
|
|
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 local, ok := remote.(*PublicKey); ok {
|
|
return p.k.ECDH(local.k)
|
|
}
|
|
return nil, fmt.Errorf("incompatible public key")
|
|
}
|