Files
did-it/crypto/ed25519/public.go

131 lines
3.7 KiB
Go
Raw Normal View History

package ed25519
import (
"crypto/ed25519"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"golang.org/x/crypto/cryptobyte"
"github.com/ucan-wg/go-did-it/crypto"
"github.com/ucan-wg/go-did-it/crypto/internal"
)
2025-07-09 17:58:09 +02:00
var _ crypto.PublicKeySigningBytes = PublicKey{}
var _ crypto.PublicKeySigningASN1 = PublicKey{}
2025-07-03 15:55:58 +02:00
var _ crypto.PublicKeyToBytes = PublicKey{}
type PublicKey struct {
k ed25519.PublicKey
}
// PublicKeyFromBytes converts a serialized public key to a PublicKey.
// This compact serialization format is the raw key material, without metadata or structure.
// It errors if the slice is not the right size.
func PublicKeyFromBytes(b []byte) (PublicKey, error) {
if len(b) != PublicKeyBytesSize {
return PublicKey{}, fmt.Errorf("invalid ed25519 public key size")
}
// make a copy
return PublicKey{k: append([]byte{}, b...)}, nil
}
// PublicKeyFromPublicKeyMultibase decodes the public key from its PublicKeyMultibase form
func PublicKeyFromPublicKeyMultibase(multibase string) (PublicKey, error) {
code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase)
if err != nil {
return PublicKey{}, err
}
if code != MultibaseCode {
return PublicKey{}, fmt.Errorf("invalid code")
}
if len(bytes) != PublicKeyBytesSize {
return PublicKey{}, fmt.Errorf("invalid ed25519 public key size")
}
return PublicKeyFromBytes(bytes)
}
// PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key.
func PublicKeyFromX509DER(bytes []byte) (PublicKey, error) {
pub, err := x509.ParsePKIXPublicKey(bytes)
if err != nil {
return PublicKey{}, err
}
return PublicKey{k: pub.(ed25519.PublicKey)}, nil
}
// PublicKeyFromX509PEM decodes an X.509 PEM (string) encoded public key.
func PublicKeyFromX509PEM(str string) (PublicKey, error) {
block, _ := pem.Decode([]byte(str))
if block == nil {
return PublicKey{}, fmt.Errorf("failed to decode PEM block")
}
if block.Type != pemPubBlockType {
return PublicKey{}, fmt.Errorf("incorrect PEM block type")
}
return PublicKeyFromX509DER(block.Bytes)
}
func (p PublicKey) ToBytes() []byte {
// Copy the private key to a fixed size buffer that can get allocated on the
// caller's stack after inlining.
var buf [PublicKeyBytesSize]byte
return append(buf[:0], p.k...)
}
func (p PublicKey) ToPublicKeyMultibase() string {
return helpers.PublicKeyMultibaseEncode(MultibaseCode, p.k)
}
func (p PublicKey) ToX509DER() []byte {
res, _ := x509.MarshalPKIXPublicKey(p.k)
return res
}
func (p PublicKey) ToX509PEM() string {
der := p.ToX509DER()
return string(pem.EncodeToMemory(&pem.Block{
Type: pemPubBlockType,
Bytes: der,
}))
}
func (p PublicKey) Equal(other crypto.PublicKey) bool {
if other, ok := other.(PublicKey); ok {
return p.k.Equal(other.k)
}
return false
}
func (p PublicKey) VerifyBytes(message, signature []byte, opts ...crypto.SigningOption) bool {
params := crypto.CollectSigningOptions(opts)
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
// ed25519 does not support custom hash functions
return false
}
return ed25519.Verify(p.k, message, signature)
}
// VerifyASN1 verifies a signature with ASN.1 encoding.
// This ASN.1 encoding uses a BIT STRING, which would be correct for an X.509 certificate.
func (p PublicKey) VerifyASN1(message, signature []byte, opts ...crypto.SigningOption) bool {
params := crypto.CollectSigningOptions(opts)
if params.Hash != crypto.Hash(0) && params.Hash != crypto.SHA512 {
// ed25519 does not support custom hash functions
return false
}
var s cryptobyte.String = signature
var bitString asn1.BitString
if !s.ReadASN1BitString(&bitString) {
return false
}
if bitString.BitLength != SignatureBytesSize*8 {
return false
}
return ed25519.Verify(p.k, message, bitString.Bytes)
}