1 /** 2 * PKCS #5 v2.0 PBE 3 * 4 * Copyright: 5 * (C) 1999-2007,2014 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.constructs.pbes2; 12 13 import botan.constants; 14 static if (BOTAN_HAS_PBE_PKCS_V20): 15 16 import botan.utils.types; 17 import botan.algo_base.transform; 18 import std.datetime; 19 import botan.pbkdf.pbkdf; 20 import botan.asn1.ber_dec; 21 import botan.asn1.der_enc; 22 import botan.asn1.alg_id; 23 import botan.asn1.oids; 24 import botan.rng.rng; 25 import botan.utils.parsing; 26 import botan.stream.stream_cipher; 27 import botan.modes.cipher_mode; 28 import botan.modes.cbc; 29 import botan.modes.aead.gcm; 30 import botan.utils.mem_ops; 31 import botan.libstate.libstate; 32 33 34 /** 35 * Encrypt with PBES2 from PKCS #5 v2.0 36 * key_bits = the passphrase to use for encryption 37 * msec = how many milliseconds to run PBKDF2 38 * cipher = specifies the block cipher to use to encrypt 39 * digest = specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") 40 * rng = a random number generator 41 */ 42 Pair!(AlgorithmIdentifier, Array!ubyte) 43 pbes2Encrypt()(auto const ref SecureVector!ubyte key_bits, 44 const string passphrase, 45 Duration msec, 46 const string cipher, 47 const string digest, 48 RandomNumberGenerator rng, 49 AlgorithmFactory af = null) 50 { 51 if (!af) af = globalState().algorithmFactory(); 52 const string prf = "HMAC(" ~ digest ~ ")"; 53 54 const Vector!string cipher_spec = splitter(cipher, '/'); 55 if(cipher_spec.length != 2) 56 throw new DecodingError("PBE-PKCS5 v2.0: Invalid cipher spec " ~ cipher); 57 58 const SecureVector!ubyte salt = rng.randomVec(12); 59 60 if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") 61 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 62 63 Unique!KeyedTransform enc; 64 static if (BOTAN_HAS_AEAD_GCM) { 65 if(cipher_spec[1] == "GCM") 66 enc = new GCMEncryption(af.makeBlockCipher(cipher_spec[0])); 67 else if(cipher_spec[1] == "CBC") 68 enc = new CBCEncryption(af.makeBlockCipher(cipher_spec[0]), new PKCS7Padding); 69 else 70 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 71 } else { 72 if(cipher_spec[1] == "CBC") 73 enc = new CBCEncryption(af.makeBlockCipher(cipher_spec[0]), new PKCS7Padding); 74 else 75 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 76 } 77 if (enc.isEmpty()) 78 throw new DecodingError("PBE-PKCS5: Cannot decrypt, no cipher " ~ cipher); 79 Unique!PBKDF pbkdf = getPbkdf("PBKDF2(" ~ prf ~ ")"); 80 81 const size_t key_length = enc.keySpec().maximumKeylength(); 82 size_t iterations = 0; 83 84 SecureVector!ubyte iv = rng.randomVec(enc.defaultNonceLength()); 85 86 auto key = pbkdf.deriveKey(key_length, passphrase, salt.ptr, salt.length, 87 msec, iterations).bitsOf(); 88 enc.setKey(key.ptr, key.length); 89 90 enc.start(iv); 91 SecureVector!ubyte buf = key_bits.ptr[0 .. key_bits.length]; 92 enc.finish(buf); 93 94 AlgorithmIdentifier id = AlgorithmIdentifier( 95 OIDS.lookup("PBE-PKCS5v20"), 96 encodePbes2Params(cipher, prf, salt, iv, iterations, key_length)); 97 98 return makePair(id, unlock(buf).dupr); 99 100 } 101 /* 102 * Encode PKCS#5 PBES2 parameters 103 */ 104 Vector!ubyte encodePbes2Params(const string cipher, 105 const string prf, 106 const ref SecureVector!ubyte salt, 107 const ref SecureVector!ubyte iv, 108 size_t iterations, 109 size_t key_length) 110 { 111 return DEREncoder() 112 .startCons(ASN1Tag.SEQUENCE) 113 .encode(AlgorithmIdentifier("PKCS5.PBKDF2", 114 DEREncoder() 115 .startCons(ASN1Tag.SEQUENCE) 116 .encode(salt, ASN1Tag.OCTET_STRING) 117 .encode(iterations) 118 .encode(key_length) 119 .encodeIf(prf != "HMAC(SHA-160)", 120 AlgorithmIdentifier(prf, AlgorithmIdentifierImpl.USE_NULL_PARAM)) 121 .endCons() 122 .getContentsUnlocked() 123 ) 124 ) 125 .encode( 126 AlgorithmIdentifier(cipher, 127 DEREncoder().encode(iv, ASN1Tag.OCTET_STRING).getContentsUnlocked() 128 ) 129 ) 130 .endCons() 131 .getContentsUnlocked(); 132 } 133 134 135 /** 136 * Decrypt a PKCS #5 v2.0 encrypted stream 137 * key_bits = the input 138 * passphrase = the passphrase to use for decryption 139 * params = the PBES2 parameters 140 */ 141 SecureVector!ubyte 142 pbes2Decrypt()(const ref SecureVector!ubyte key_bits, 143 const string passphrase, 144 auto const ref Vector!ubyte params, 145 AlgorithmFactory af = null) 146 { 147 if (!af) af = globalState().algorithmFactory(); 148 AlgorithmIdentifier kdf_algo, enc_algo; 149 150 BERDecoder(params) 151 .startCons(ASN1Tag.SEQUENCE) 152 .decode(kdf_algo) 153 .decode(enc_algo) 154 .verifyEnd() 155 .endCons(); 156 157 AlgorithmIdentifier prf_algo; 158 159 if(kdf_algo.oid != OIDS.lookup("PKCS5.PBKDF2")) 160 throw new DecodingError("PBE-PKCS5 v2.0: Unknown KDF algorithm " ~ kdf_algo.oid.toString()); 161 162 SecureVector!ubyte salt; 163 size_t iterations, key_length; 164 165 BERDecoder(kdf_algo.parameters) 166 .startCons(ASN1Tag.SEQUENCE) 167 .decode(salt, ASN1Tag.OCTET_STRING) 168 .decode(iterations) 169 .decodeOptional(key_length, ASN1Tag.INTEGER, ASN1Tag.UNIVERSAL) 170 .decodeOptional(prf_algo, ASN1Tag.SEQUENCE, ASN1Tag.CONSTRUCTED, 171 AlgorithmIdentifier("HMAC(SHA-160)", 172 AlgorithmIdentifierImpl.USE_NULL_PARAM)) 173 .verifyEnd() 174 .endCons(); 175 176 const string cipher = OIDS.lookup(enc_algo.oid); 177 const Vector!string cipher_spec = splitter(cipher, '/'); 178 if(cipher_spec.length != 2) 179 throw new DecodingError("PBE-PKCS5 v2.0: Invalid cipher spec " ~ cipher); 180 if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") 181 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 182 183 if(salt.length < 8) 184 throw new DecodingError("PBE-PKCS5 v2.0: Encoded salt is too small"); 185 186 SecureVector!ubyte iv; 187 BERDecoder(enc_algo.parameters).decode(iv, ASN1Tag.OCTET_STRING).verifyEnd(); 188 189 const string prf = OIDS.lookup(prf_algo.oid); 190 191 Unique!PBKDF pbkdf = getPbkdf("PBKDF2(" ~ prf ~ ")"); 192 193 Unique!KeyedTransform dec; 194 static if (BOTAN_HAS_AEAD_GCM) { 195 if (cipher_spec[1] == "GCM") 196 dec = new GCMDecryption(af.makeBlockCipher(cipher_spec[0])); 197 else if (cipher_spec[1] == "CBC") 198 dec = new CBCDecryption(af.makeBlockCipher(cipher_spec[0]), new PKCS7Padding); 199 else 200 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 201 } else { 202 if (cipher_spec[1] == "CBC") 203 dec = new CBCDecryption(af.makeBlockCipher(cipher_spec[0]), new PKCS7Padding); 204 else 205 throw new DecodingError("PBE-PKCS5 v2.0: Don't know param format for " ~ cipher); 206 } 207 208 if(key_length == 0) 209 key_length = dec.keySpec().maximumKeylength(); 210 211 dec.setKey(pbkdf.deriveKey(key_length, passphrase, salt.ptr, salt.length, iterations)); 212 213 dec.start(iv); 214 215 SecureVector!ubyte buf = key_bits.ptr[0 .. key_bits.length]; 216 dec.finish(buf); 217 218 return buf.move(); 219 }