github.com/cloudflare/circl@v1.5.0/pki/pki.go (about)

     1  package pki
     2  
     3  import (
     4  	"crypto/x509/pkix"
     5  	"encoding/asn1"
     6  	"encoding/pem"
     7  	"errors"
     8  	"strings"
     9  
    10  	"github.com/cloudflare/circl/sign"
    11  	"github.com/cloudflare/circl/sign/schemes"
    12  )
    13  
    14  var (
    15  	allSchemesByOID map[string]sign.Scheme
    16  	allSchemesByTLS map[uint]sign.Scheme
    17  )
    18  
    19  type pkixPrivKey struct {
    20  	Version    int
    21  	Algorithm  pkix.AlgorithmIdentifier
    22  	PrivateKey []byte
    23  }
    24  
    25  func init() {
    26  	allSchemesByOID = make(map[string]sign.Scheme)
    27  	allSchemesByTLS = make(map[uint]sign.Scheme)
    28  	for _, scheme := range schemes.All() {
    29  		if cert, ok := scheme.(CertificateScheme); ok {
    30  			allSchemesByOID[cert.Oid().String()] = scheme
    31  		}
    32  		if tlsScheme, ok := scheme.(TLSScheme); ok {
    33  			allSchemesByTLS[tlsScheme.TLSIdentifier()] = scheme
    34  		}
    35  	}
    36  }
    37  
    38  func SchemeByOid(oid asn1.ObjectIdentifier) sign.Scheme { return allSchemesByOID[oid.String()] }
    39  
    40  func SchemeByTLSID(id uint) sign.Scheme { return allSchemesByTLS[id] }
    41  
    42  // Additional methods when the signature scheme is supported in X509.
    43  type CertificateScheme interface {
    44  	// Return the appropriate OIDs for this instance.  It is implicitly
    45  	// assumed that the encoding is simple: e.g. uses the same OID for
    46  	// signature and public key like Ed25519.
    47  	Oid() asn1.ObjectIdentifier
    48  }
    49  
    50  // Additional methods when the signature scheme is supported in TLS.
    51  type TLSScheme interface {
    52  	TLSIdentifier() uint
    53  }
    54  
    55  func UnmarshalPEMPublicKey(data []byte) (sign.PublicKey, error) {
    56  	block, rest := pem.Decode(data)
    57  	if len(rest) != 0 {
    58  		return nil, errors.New("trailing data")
    59  	}
    60  	if !strings.HasSuffix(block.Type, "PUBLIC KEY") {
    61  		return nil, errors.New("pem block type is not public key")
    62  	}
    63  
    64  	return UnmarshalPKIXPublicKey(block.Bytes)
    65  }
    66  
    67  func UnmarshalPKIXPublicKey(data []byte) (sign.PublicKey, error) {
    68  	var pkix struct {
    69  		Raw       asn1.RawContent
    70  		Algorithm pkix.AlgorithmIdentifier
    71  		PublicKey asn1.BitString
    72  	}
    73  	if rest, err := asn1.Unmarshal(data, &pkix); err != nil {
    74  		return nil, err
    75  	} else if len(rest) != 0 {
    76  		return nil, errors.New("trailing data")
    77  	}
    78  	scheme := SchemeByOid(pkix.Algorithm.Algorithm)
    79  	if scheme == nil {
    80  		return nil, errors.New("unsupported public key algorithm")
    81  	}
    82  	return scheme.UnmarshalBinaryPublicKey(pkix.PublicKey.RightAlign())
    83  }
    84  
    85  func UnmarshalPEMPrivateKey(data []byte) (sign.PrivateKey, error) {
    86  	block, rest := pem.Decode(data)
    87  	if len(rest) != 0 {
    88  		return nil, errors.New("trailing")
    89  	}
    90  	if !strings.HasSuffix(block.Type, "PRIVATE KEY") {
    91  		return nil, errors.New("pem block type is not private key")
    92  	}
    93  
    94  	return UnmarshalPKIXPrivateKey(block.Bytes)
    95  }
    96  
    97  func UnmarshalPKIXPrivateKey(data []byte) (sign.PrivateKey, error) {
    98  	var pkix pkixPrivKey
    99  	if rest, err := asn1.Unmarshal(data, &pkix); err != nil {
   100  		return nil, err
   101  	} else if len(rest) != 0 {
   102  		return nil, errors.New("trailing data")
   103  	}
   104  	scheme := SchemeByOid(pkix.Algorithm.Algorithm)
   105  	if scheme == nil {
   106  		return nil, errors.New("unsupported public key algorithm")
   107  	}
   108  	var sk []byte
   109  	if rest, err := asn1.Unmarshal(pkix.PrivateKey, &sk); err != nil {
   110  		return nil, err
   111  	} else if len(rest) > 0 {
   112  		return nil, errors.New("trailing data")
   113  	}
   114  	return scheme.UnmarshalBinaryPrivateKey(sk)
   115  }
   116  
   117  func MarshalPEMPublicKey(pk sign.PublicKey) ([]byte, error) {
   118  	data, err := MarshalPKIXPublicKey(pk)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	str := pem.EncodeToMemory(&pem.Block{
   123  		Type:  "PUBLIC KEY",
   124  		Bytes: data,
   125  	})
   126  	return str, nil
   127  }
   128  
   129  func MarshalPKIXPublicKey(pk sign.PublicKey) ([]byte, error) {
   130  	data, err := pk.MarshalBinary()
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	scheme := pk.Scheme()
   136  	return asn1.Marshal(struct {
   137  		pkix.AlgorithmIdentifier
   138  		asn1.BitString
   139  	}{
   140  		pkix.AlgorithmIdentifier{
   141  			Algorithm: scheme.(CertificateScheme).Oid(),
   142  		},
   143  		asn1.BitString{
   144  			Bytes:     data,
   145  			BitLength: len(data) * 8,
   146  		},
   147  	})
   148  }
   149  
   150  func MarshalPEMPrivateKey(sk sign.PrivateKey) ([]byte, error) {
   151  	data, err := MarshalPKIXPrivateKey(sk)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	str := pem.EncodeToMemory(&pem.Block{
   156  		Type:  sk.Scheme().Name() + " PRIVATE KEY",
   157  		Bytes: data,
   158  	},
   159  	)
   160  	return str, nil
   161  }
   162  
   163  func MarshalPKIXPrivateKey(sk sign.PrivateKey) ([]byte, error) {
   164  	data, err := sk.MarshalBinary()
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	data, err = asn1.Marshal(data)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	scheme := sk.Scheme()
   175  	return asn1.Marshal(pkixPrivKey{
   176  		0,
   177  		pkix.AlgorithmIdentifier{
   178  			Algorithm: scheme.(CertificateScheme).Oid(),
   179  		},
   180  		data,
   181  	})
   182  }