...

Source file src/github.com/sassoftware/relic/lib/x509tools/pkix.go

Documentation: github.com/sassoftware/relic/lib/x509tools

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package x509tools
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"crypto/x509/pkix"
    25  	"encoding/asn1"
    26  	"errors"
    27  	"fmt"
    28  )
    29  
    30  var (
    31  	// RFC 3279
    32  	OidPublicKeyRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
    33  	OidPublicKeyDSA   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
    34  	OidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
    35  
    36  	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
    37  	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
    38  	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
    39  	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
    40  	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
    41  	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
    42  	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
    43  	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
    44  	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
    45  	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
    46  	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
    47  	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
    48  	oidISOSignatureSHA1WithRSA  = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
    49  
    50  	// RFC 4055
    51  	OidMGF1            = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
    52  	OidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
    53  
    54  	Asn1TagBMPString = 30
    55  )
    56  
    57  type sigAlgInfo struct {
    58  	oid        asn1.ObjectIdentifier
    59  	pubKeyAlgo x509.PublicKeyAlgorithm
    60  	hash       crypto.Hash
    61  }
    62  
    63  var sigAlgInfos = []sigAlgInfo{
    64  	// without a digest
    65  	{OidPublicKeyRSA, x509.RSA, 0},
    66  	{OidSignatureRSAPSS, x509.RSA, 0},
    67  	{OidPublicKeyDSA, x509.DSA, 0},
    68  	{OidPublicKeyECDSA, x509.ECDSA, 0},
    69  	// with a digest
    70  	{oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
    71  	{oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
    72  	{oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
    73  	{oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
    74  	{oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
    75  	{oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
    76  	{oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
    77  	{oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
    78  	{oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
    79  	{oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
    80  	{oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
    81  	{oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
    82  }
    83  
    84  // Given a public key and signer options, return the appropriate X.509 digest and signature algorithms
    85  func PkixAlgorithms(pub crypto.PublicKey, opts crypto.SignerOpts) (digestAlg, sigAlg pkix.AlgorithmIdentifier, err error) {
    86  	digestAlg, ok := PkixDigestAlgorithm(opts.HashFunc())
    87  	if !ok {
    88  		err = errors.New("unsupported digest algorithm")
    89  		return
    90  	}
    91  	if pss, ok := opts.(*rsa.PSSOptions); ok {
    92  		rsapub, ok := pub.(*rsa.PublicKey)
    93  		if !ok {
    94  			err = errors.New("RSA-PSS is only valid for RSA keys")
    95  			return
    96  		}
    97  		var params asn1.RawValue
    98  		params, err = MarshalRSAPSSParameters(rsapub, pss)
    99  		if err != nil {
   100  			return
   101  		}
   102  		sigAlg = pkix.AlgorithmIdentifier{
   103  			Algorithm:  OidSignatureRSAPSS,
   104  			Parameters: params,
   105  		}
   106  		return
   107  	}
   108  	switch pub.(type) {
   109  	case *rsa.PublicKey:
   110  		sigAlg.Algorithm = OidPublicKeyRSA
   111  	case *ecdsa.PublicKey:
   112  		sigAlg.Algorithm = OidPublicKeyECDSA
   113  	default:
   114  		err = errors.New("unsupported public key algorithm")
   115  		return
   116  	}
   117  	sigAlg.Parameters = asn1.NullRawValue
   118  	return
   119  }
   120  
   121  // Convert a crypto.Hash to a X.509 AlgorithmIdentifier
   122  func PkixDigestAlgorithm(hash crypto.Hash) (alg pkix.AlgorithmIdentifier, ok bool) {
   123  	if oid, ok2 := HashOids[hash]; ok2 {
   124  		alg.Algorithm = oid
   125  		alg.Parameters = asn1.NullRawValue
   126  		ok = true
   127  	}
   128  	return
   129  }
   130  
   131  // Convert a X.509 AlgorithmIdentifier to a crypto.Hash
   132  func PkixDigestToHash(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, ok bool) {
   133  	for hash, oid := range HashOids {
   134  		if alg.Algorithm.Equal(oid) {
   135  			return hash, true
   136  		}
   137  	}
   138  	return 0, false
   139  }
   140  
   141  // Convert a X.509 AlgorithmIdentifier to a crypto.Hash
   142  func PkixDigestToHashE(alg pkix.AlgorithmIdentifier) (hash crypto.Hash, err error) {
   143  	hash, ok := PkixDigestToHash(alg)
   144  	if ok && hash.Available() {
   145  		return hash, nil
   146  	}
   147  	return 0, UnknownDigestError{Algorithm: alg.Algorithm}
   148  }
   149  
   150  // Convert a crypto.PublicKey to a X.509 AlgorithmIdentifier
   151  func PkixPublicKeyAlgorithm(pub crypto.PublicKey) (alg pkix.AlgorithmIdentifier, ok bool) {
   152  	_, alg, err := PkixAlgorithms(pub, nil)
   153  	return alg, err == nil
   154  }
   155  
   156  // Verify a signature using the algorithm specified by the given X.509 AlgorithmIdentifier
   157  func PkixVerify(pub crypto.PublicKey, digestAlg, sigAlg pkix.AlgorithmIdentifier, digest, sig []byte) error {
   158  	hash, err := PkixDigestToHashE(digestAlg)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	var info sigAlgInfo
   163  	for _, a := range sigAlgInfos {
   164  		if sigAlg.Algorithm.Equal(a.oid) {
   165  			info = a
   166  		}
   167  	}
   168  	if info.hash != 0 && info.hash != hash {
   169  		return errors.New("signature type does not match digest type")
   170  	}
   171  	switch info.pubKeyAlgo {
   172  	case x509.RSA:
   173  		key, ok := pub.(*rsa.PublicKey)
   174  		if !ok {
   175  			return errors.New("incorrect key type for signature")
   176  		}
   177  		if sigAlg.Algorithm.Equal(OidSignatureRSAPSS) {
   178  			opts, err := UnmarshalRSAPSSParameters(hash, sigAlg.Parameters)
   179  			if err != nil {
   180  				return err
   181  			}
   182  			return rsa.VerifyPSS(key, hash, digest, sig, opts)
   183  		}
   184  		return rsa.VerifyPKCS1v15(key, hash, digest, sig)
   185  	case x509.ECDSA:
   186  		key, ok := pub.(*ecdsa.PublicKey)
   187  		if !ok {
   188  			return errors.New("incorrect key type for signature")
   189  		}
   190  		esig, err := UnmarshalEcdsaSignature(sig)
   191  		if err != nil {
   192  			return err
   193  		}
   194  		if !ecdsa.Verify(key, digest, esig.R, esig.S) {
   195  			return errors.New("ECDSA verification failed")
   196  		}
   197  		return nil
   198  	default:
   199  		return errors.New("unsupported public key algorithm")
   200  	}
   201  }
   202  
   203  type UnknownDigestError struct {
   204  	Algorithm asn1.ObjectIdentifier
   205  }
   206  
   207  func (e UnknownDigestError) Error() string {
   208  	return fmt.Sprintf("unsupported hash algorithm %s", e.Algorithm)
   209  }
   210  

View as plain text