131 lines
3.7 KiB
Go
131 lines
3.7 KiB
Go
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"
|
|
)
|
|
|
|
var _ crypto.PublicKeySigningBytes = PublicKey{}
|
|
var _ crypto.PublicKeySigningASN1 = PublicKey{}
|
|
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)
|
|
}
|