github.com/emmansun/gmsm@v0.29.1/pkcs/pkcs5_pbes2.go (about)

     1  package pkcs
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"crypto/sha256"
     6  	"crypto/sha512"
     7  	"crypto/x509/pkix"
     8  	"encoding/asn1"
     9  	"errors"
    10  	"fmt"
    11  	"hash"
    12  	"io"
    13  	"strconv"
    14  
    15  	"github.com/emmansun/gmsm/sm3"
    16  )
    17  
    18  var (
    19  	oidPBES2  = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
    20  	oidSMPBES = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 4, 1, 5, 2}
    21  )
    22  
    23  // Hash identifies a cryptographic hash function that is implemented in another
    24  // package.
    25  type Hash uint
    26  
    27  const (
    28  	SHA1 Hash = 1 + iota
    29  	SHA224
    30  	SHA256
    31  	SHA384
    32  	SHA512
    33  	SHA512_224
    34  	SHA512_256
    35  	SM3
    36  )
    37  
    38  // New returns a new hash.Hash calculating the given hash function. New panics
    39  // if the hash function is not linked into the binary.
    40  func (h Hash) New() hash.Hash {
    41  	switch h {
    42  	case SM3:
    43  		return sm3.New()
    44  	case SHA1:
    45  		return sha1.New()
    46  	case SHA224:
    47  		return sha256.New224()
    48  	case SHA256:
    49  		return sha256.New()
    50  	case SHA384:
    51  		return sha512.New384()
    52  	case SHA512:
    53  		return sha512.New()
    54  	case SHA512_224:
    55  		return sha512.New512_224()
    56  	case SHA512_256:
    57  		return sha512.New512_256()
    58  
    59  	}
    60  	panic("pbes: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
    61  }
    62  
    63  var (
    64  	ErrPBEDecryption = errors.New("pbes: decryption error, please verify the password and try again")
    65  )
    66  
    67  // PBKDF2Opts contains algorithm identifiers and related parameters for PBKDF2 key derivation function.
    68  //
    69  //	PBES2-params ::= SEQUENCE {
    70  //		keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
    71  //		encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
    72  //	}
    73  type PBES2Params struct {
    74  	KeyDerivationFunc pkix.AlgorithmIdentifier
    75  	EncryptionScheme  pkix.AlgorithmIdentifier
    76  }
    77  
    78  // PBES2Opts contains options for encrypting a key using PBES2.
    79  type PBES2Opts struct {
    80  	Cipher
    81  	KDFOpts
    82  	pbesOID asn1.ObjectIdentifier
    83  }
    84  
    85  // DefaultOpts are the default options for encrypting a key if none are given.
    86  // The defaults can be changed by the library user.
    87  var DefaultOpts = &PBES2Opts{
    88  	Cipher: AES256CBC,
    89  	KDFOpts: PBKDF2Opts{
    90  		SaltSize:       16,
    91  		IterationCount: 2048,
    92  		HMACHash:       SHA256,
    93  		pbkdfOID:       oidPKCS5PBKDF2,
    94  	},
    95  	pbesOID: oidPBES2,
    96  }
    97  
    98  // NewPBES2Encrypter returns a new PBES2Encrypter with the given cipher and KDF options.
    99  func NewPBESEncrypter(cipher Cipher, kdfOpts KDFOpts) PBESEncrypter {
   100  	return &PBES2Opts{
   101  		Cipher:  cipher,
   102  		KDFOpts: kdfOpts,
   103  		pbesOID: oidPBES2,
   104  	}
   105  }
   106  
   107  // NewSMPBESEncrypterWithKDF returns a new SMPBESEncrypter (ShangMi PBES Encrypter) with the given KDF options.
   108  func NewSMPBESEncrypterWithKDF(kdfOpts KDFOpts) PBESEncrypter {
   109  	return &PBES2Opts{
   110  		Cipher:  SM4CBC,
   111  		KDFOpts: kdfOpts,
   112  		pbesOID: oidSMPBES,
   113  	}
   114  }
   115  
   116  // NewSMPBESEncrypter returns a new SMPBESEncrypter (ShangMi PBES Encrypter) with the given salt size and iteration count.
   117  func NewSMPBESEncrypter(saltSize, iterationCount int) PBESEncrypter {
   118  	return NewSMPBESEncrypterWithKDF(NewSMPBKDF2Opts(saltSize, iterationCount))
   119  }
   120  
   121  // KDFOpts contains options for a key derivation function.
   122  // An implementation of this interface must be specified when encrypting a PKCS#8 key.
   123  type KDFOpts interface {
   124  	// DeriveKey derives a key of size bytes from the given password and salt.
   125  	// It returns the key and the ASN.1-encodable parameters used.
   126  	DeriveKey(password, salt []byte, size int) (key []byte, params KDFParameters, err error)
   127  	// GetSaltSize returns the salt size specified.
   128  	GetSaltSize() int
   129  	// OID returns the OID of the KDF specified.
   130  	OID() asn1.ObjectIdentifier
   131  }
   132  
   133  type PBESEncrypter interface {
   134  	Encrypt(rand io.Reader, password, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error)
   135  }
   136  
   137  // KDFParameters contains parameters (salt, etc.) for a key deriviation function.
   138  // It must be a ASN.1-decodable structure.
   139  // An implementation of this interface is created when decoding an encrypted PKCS#8 key.
   140  type KDFParameters interface {
   141  	// DeriveKey derives a key of size bytes from the given password.
   142  	// It uses the salt from the decoded parameters.
   143  	DeriveKey(oidKDF asn1.ObjectIdentifier, password []byte, size int) (key []byte, err error)
   144  	// KeyLength returns the length of the derived key from the params.
   145  	KeyLength() int
   146  }
   147  
   148  var kdfs = make(map[string]func() KDFParameters)
   149  
   150  // RegisterKDF registers a function that returns a new instance of the given KDF
   151  // parameters. This allows the library to support client-provided KDFs.
   152  func RegisterKDF(oid asn1.ObjectIdentifier, params func() KDFParameters) {
   153  	kdfs[oid.String()] = params
   154  }
   155  
   156  func (pbes2Params *PBES2Params) parseKeyDerivationFunc() (KDFParameters, error) {
   157  	oid := pbes2Params.KeyDerivationFunc.Algorithm.String()
   158  	newParams, ok := kdfs[oid]
   159  	if !ok {
   160  		return nil, fmt.Errorf("pbes: unsupported KDF (OID: %s)", oid)
   161  	}
   162  	params := newParams()
   163  	_, err := asn1.Unmarshal(pbes2Params.KeyDerivationFunc.Parameters.FullBytes, params)
   164  	if err != nil {
   165  		return nil, errors.New("pbes: invalid KDF parameters")
   166  	}
   167  	return params, nil
   168  }
   169  
   170  // Decrypt decrypts the given ciphertext using the given password and the options specified.
   171  func (pbes2Params *PBES2Params) Decrypt(password, ciphertext []byte) ([]byte, KDFParameters, error) {
   172  	cipher, err := GetCipher(pbes2Params.EncryptionScheme)
   173  	if err != nil {
   174  		return nil, nil, err
   175  	}
   176  
   177  	kdfParams, err := pbes2Params.parseKeyDerivationFunc()
   178  	if err != nil {
   179  		return nil, nil, err
   180  	}
   181  
   182  	keySize := cipher.KeySize()
   183  	symkey, err := kdfParams.DeriveKey(pbes2Params.KeyDerivationFunc.Algorithm, password, keySize)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  
   188  	plaintext, err := cipher.Decrypt(symkey, &pbes2Params.EncryptionScheme.Parameters, ciphertext)
   189  	if err != nil {
   190  		return nil, nil, ErrPBEDecryption
   191  	}
   192  	return plaintext, kdfParams, nil
   193  }
   194  
   195  func deriveKey(kdfOpts KDFOpts, rand io.Reader, password []byte, size int) ([]byte, *pkix.AlgorithmIdentifier, error) {
   196  	// Generate a random salt
   197  	salt := make([]byte, kdfOpts.GetSaltSize())
   198  	if _, err := rand.Read(salt); err != nil {
   199  		return nil, nil, err
   200  	}
   201  	key, kdfParams, err := kdfOpts.DeriveKey(password, salt, size)
   202  	if err != nil {
   203  		return nil, nil, err
   204  	}
   205  	marshalledParams, err := asn1.Marshal(kdfParams)
   206  	if err != nil {
   207  		return nil, nil, err
   208  	}
   209  	keyDerivationFunc := pkix.AlgorithmIdentifier{
   210  		Algorithm:  kdfOpts.OID(),
   211  		Parameters: asn1.RawValue{FullBytes: marshalledParams},
   212  	}
   213  	return key, &keyDerivationFunc, nil
   214  }
   215  
   216  // Encrypt encrypts the given plaintext using the given password and the options specified.
   217  func (opts *PBES2Opts) Encrypt(rand io.Reader, password, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
   218  	encAlg := opts.Cipher
   219  	key, keyDerivationFunc, err := deriveKey(opts.KDFOpts, rand, password, encAlg.KeySize())
   220  	if err != nil {
   221  		return nil, nil, err
   222  	}
   223  	// Encrypt the plaintext
   224  	encryptionScheme, ciphertext, err := encAlg.Encrypt(rand, key, plaintext)
   225  	if err != nil {
   226  		return nil, nil, err
   227  	}
   228  
   229  	encryptionAlgorithmParams := PBES2Params{
   230  		EncryptionScheme:  *encryptionScheme,
   231  		KeyDerivationFunc: *keyDerivationFunc,
   232  	}
   233  	marshalledEncryptionAlgorithmParams, err := asn1.Marshal(encryptionAlgorithmParams)
   234  	if err != nil {
   235  		return nil, nil, err
   236  	}
   237  	encryptionAlgorithm := pkix.AlgorithmIdentifier{
   238  		Algorithm:  opts.pbesOID,
   239  		Parameters: asn1.RawValue{FullBytes: marshalledEncryptionAlgorithmParams},
   240  	}
   241  
   242  	// fallback to default
   243  	if len(encryptionAlgorithm.Algorithm) == 0 {
   244  		encryptionAlgorithm.Algorithm = oidPBES2
   245  	}
   246  
   247  	return &encryptionAlgorithm, ciphertext, nil
   248  }
   249  
   250  func IsPBES2(algorithm pkix.AlgorithmIdentifier) bool {
   251  	return oidPBES2.Equal(algorithm.Algorithm)
   252  }
   253  
   254  func IsSMPBES(algorithm pkix.AlgorithmIdentifier) bool {
   255  	return oidSMPBES.Equal(algorithm.Algorithm)
   256  }