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 }