/* * Choose a signing format for the key */ PK_Signer* choose_sig_format(const Private_Key& key, const std::string& hash_fn, AlgorithmIdentifier& sig_algo) { const std::string algo_name = key.algo_name(); std::unique_ptr<HashFunction> hash(HashFunction::create(hash_fn)); if(!hash) throw Algorithm_Not_Found(hash_fn); if(key.max_input_bits() < hash->output_length() * 8) throw Invalid_Argument("Key is too small for chosen hash function"); std::string padding; if(algo_name == "RSA") padding = "EMSA3"; else if(algo_name == "DSA") padding = "EMSA1"; else if(algo_name == "ECDSA") padding = "EMSA1_BSI"; else throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363; padding = padding + "(" + hash->name() + ")"; sig_algo.oid = OIDS::lookup(algo_name + "/" + padding); sig_algo.parameters = key.algorithm_identifier().parameters; return new PK_Signer(key, padding, format); }
u32bit to_u32bit(const std::string& str) { try { // std::stoul is not strict enough. Ensure that str is digit only [0-9]* for (const char chr : str) { if (chr < '0' || chr > '9') { auto chrAsString = std::string(1, chr); throw Invalid_Argument("String contains non-digit char: " + chrAsString); } } const auto integerValue = std::stoul(str); // integerValue might be uint64 if (integerValue > std::numeric_limits<u32bit>::max()) { throw Invalid_Argument("Integer value exceeds 32 bit range: " + std::to_string(integerValue)); } return integerValue; } catch(std::exception& e) { auto message = std::string("Could not read '" + str + "' as decimal string"); auto exceptionMessage = std::string(e.what()); if (!exceptionMessage.empty()) message += ": " + exceptionMessage; throw std::runtime_error(message); } }
/* * Create a RSA private key */ RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng, size_t bits, size_t exp) { if(bits < 1024) throw Invalid_Argument(algo_name() + ": Can't make a key that is only " + std::to_string(bits) + " bits long"); if(exp < 3 || exp % 2 == 0) throw Invalid_Argument(algo_name() + ": Invalid encryption exponent"); e = exp; do { p = random_prime(rng, (bits + 1) / 2, e); q = random_prime(rng, bits - p.bits(), e); n = p * q; } while(n.bits() != bits); d = inverse_mod(e, lcm(p - 1, q - 1)); d1 = d % (p - 1); d2 = d % (q - 1); c = inverse_mod(q, p); gen_check(rng); }
/* * Modified key schedule used for bcrypt password hashing */ void Blowfish::eks_key_schedule(const uint8_t key[], size_t length, const uint8_t salt[16], size_t workfactor) { // Truncate longer passwords to the 56 byte limit Blowfish enforces length = std::min<size_t>(length, 55); if(workfactor == 0) throw Invalid_Argument("Bcrypt work factor must be at least 1"); /* * On a 2.8 GHz Core-i7, workfactor == 18 takes about 25 seconds to * hash a password. This seems like a reasonable upper bound for the * time being. */ if(workfactor > 18) throw Invalid_Argument("Requested Bcrypt work factor " + std::to_string(workfactor) + " too large"); m_P.resize(18); copy_mem(m_P.data(), P_INIT, 18); m_S.resize(1024); copy_mem(m_S.data(), S_INIT, 1024); key_expansion(key, length, salt); const uint8_t null_salt[16] = { 0 }; const size_t rounds = static_cast<size_t>(1) << workfactor; for(size_t r = 0; r != rounds; ++r) { key_expansion(key, length, null_salt); key_expansion(salt, 16, null_salt); } }
AlgorithmIdentifier PSSR::config_for_x509(const Private_Key& key, const std::string& cert_hash_name) const { if(cert_hash_name != m_hash->name()) throw Invalid_Argument("Hash function from opts and hash_fn argument" " need to be identical"); // check that the signature algorithm and the padding scheme fit if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA4")) { throw Invalid_Argument("Encoding scheme with canonical name EMSA4" " not supported for signature algorithm " + key.algo_name()); } AlgorithmIdentifier sig_algo; // hardcoded as RSA is the only valid algorithm for EMSA4 at the moment sig_algo.oid = OIDS::lookup( "RSA/EMSA4" ); const AlgorithmIdentifier hash_id(cert_hash_name, AlgorithmIdentifier::USE_NULL_PARAM); const AlgorithmIdentifier mgf_id("MGF1", hash_id.BER_encode()); DER_Encoder(sig_algo.parameters) .start_cons(SEQUENCE) .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).encode(hash_id).end_cons() .start_cons(ASN1_Tag(1), CONTEXT_SPECIFIC).encode(mgf_id).end_cons() .start_cons(ASN1_Tag(2), CONTEXT_SPECIFIC).encode(m_SALT_SIZE).end_cons() .start_cons(ASN1_Tag(3), CONTEXT_SPECIFIC).encode(size_t(1)).end_cons() // trailer field .end_cons(); return sig_algo; }
/* * Set the time with a human readable string */ void EAC_Time::set_to(const std::string& time_str) { if(time_str == "") { year = month = day = 0; return; } std::vector<std::string> params; std::string current; for(u32bit j = 0; j != time_str.size(); ++j) { if(Charset::is_digit(time_str[j])) current += time_str[j]; else { if(current != "") params.push_back(current); current.clear(); } } if(current != "") params.push_back(current); if(params.size() != 3) throw Invalid_Argument("Invalid time specification " + time_str); year = to_u32bit(params[0]); month = to_u32bit(params[1]); day = to_u32bit(params[2]); if(!passes_sanity_check()) throw Invalid_Argument("Invalid time specification " + time_str); }
/* * PKCS#5 v2.0 PBE Constructor */ PBE_PKCS5v20::PBE_PKCS5v20(BlockCipher* cipher, HashFunction* digest) : direction(ENCRYPTION), block_cipher(cipher), hash_function(digest) { if(!known_cipher(block_cipher->name())) throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher " + cipher->name()); if(hash_function->name() != "SHA-160") throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid digest " + digest->name()); }
Montgomery_Exponentation_State::Montgomery_Exponentation_State(const BigInt& g, const BigInt& p, const Modular_Reducer& mod_p, size_t window_bits) : m_p(p), m_p_words(p.sig_words()), m_window_bits(window_bits) { if(p.is_positive() == false || p.is_even()) throw Invalid_Argument("Cannot use Montgomery reduction on even or negative integer"); if(window_bits > 12) // really even 8 is too large ... throw Invalid_Argument("Montgomery window bits too large"); m_mod_prime = monty_inverse(m_p.word_at(0)); const BigInt r = BigInt::power_of_2(m_p_words * BOTAN_MP_WORD_BITS); m_R_mod = mod_p.reduce(r); m_R2_mod = mod_p.square(m_R_mod); m_g.resize(1U << m_window_bits); BigInt z(BigInt::Positive, 2 * (m_p_words + 1)); secure_vector<word> workspace(z.size()); m_g[0] = 1; bigint_monty_mul(z, m_g[0], m_R2_mod, m_p.data(), m_p_words, m_mod_prime, workspace.data()); m_g[0] = z; m_g[1] = mod_p.reduce(g); bigint_monty_mul(z, m_g[1], m_R2_mod, m_p.data(), m_p_words, m_mod_prime, workspace.data()); m_g[1] = z; const BigInt& x = m_g[1]; for(size_t i = 2; i != m_g.size(); ++i) { const BigInt& y = m_g[i-1]; bigint_monty_mul(z, x, y, m_p.data(), m_p_words, m_mod_prime, workspace.data()); m_g[i] = z; m_g[i].shrink_to_fit(); m_g[i].grow_to(m_p_words); } }
/* * Decode a BigInt */ BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base) { BigInt r; if(base == Binary) { r.binary_decode(buf, length); } else if(base == Hexadecimal) { secure_vector<uint8_t> binary; if(length % 2) { // Handle lack of leading 0 const char buf0_with_leading_0[2] = { '0', static_cast<char>(buf[0]) }; binary = hex_decode_locked(buf0_with_leading_0, 2); binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]), length - 1, false); } else binary = hex_decode_locked(cast_uint8_ptr_to_char(buf), length, false); r.binary_decode(binary.data(), binary.size()); } else if(base == Decimal) { for(size_t i = 0; i != length; ++i) { if(Charset::is_space(buf[i])) continue; if(!Charset::is_digit(buf[i])) throw Invalid_Argument("BigInt::decode: " "Invalid character in decimal input"); const uint8_t x = Charset::char2digit(buf[i]); if(x >= 10) throw Invalid_Argument("BigInt: Invalid decimal string"); r *= 10; r += x; } } else throw Invalid_Argument("Unknown BigInt decoding method"); return r; }
Comb4P::Comb4P(HashFunction* h1, HashFunction* h2) : m_hash1(h1), m_hash2(h2) { if(m_hash1->name() == m_hash2->name()) throw Invalid_Argument("Comb4P: Must use two distinct hashes"); if(m_hash1->output_length() != m_hash2->output_length()) throw Invalid_Argument("Comb4P: Incompatible hashes " + m_hash1->name() + " and " + m_hash2->name()); clear(); }
Compression_Decompression_Filter::Compression_Decompression_Filter(Transform* transform, size_t bs) : m_buffersize(std::max<size_t>(256, bs)), m_buffer(m_buffersize) { if (!transform) { throw Invalid_Argument("Transform is null"); } m_transform.reset(dynamic_cast<Compressor_Transform*>(transform)); if(!m_transform) { throw Invalid_Argument("Transform " + transform->name() + " is not a compressor"); } }
void aont_unpackage(BlockCipher* cipher, const byte input[], size_t input_len, byte output[]) { const size_t BLOCK_SIZE = cipher->block_size(); if(!cipher->valid_keylength(BLOCK_SIZE)) throw Invalid_Argument("AONT::unpackage: Invalid cipher"); if(input_len < BLOCK_SIZE) throw Invalid_Argument("AONT::unpackage: Input too short"); // The all-zero string which is used both as the CTR IV and as K0 const std::string all_zeros(BLOCK_SIZE*2, '0'); cipher->set_key(SymmetricKey(all_zeros)); SecureVector<byte> package_key(BLOCK_SIZE); SecureVector<byte> buf(BLOCK_SIZE); // Copy the package key (masked with the block hashes) copy_mem(&package_key[0], input + (input_len - BLOCK_SIZE), BLOCK_SIZE); const size_t blocks = ((input_len - 1) / BLOCK_SIZE); // XOR the blocks into the package key bits for(size_t i = 0; i != blocks; ++i) { const size_t left = std::min<size_t>(BLOCK_SIZE, input_len - BLOCK_SIZE * (i+1)); zeroise(buf); copy_mem(&buf[0], input + (BLOCK_SIZE * i), left); for(size_t j = 0; j != sizeof(i); ++j) buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i); cipher->encrypt(buf); xor_buf(&package_key[0], buf, BLOCK_SIZE); } Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key)); pipe.process_msg(input, input_len - BLOCK_SIZE); pipe.read(output, pipe.remaining()); }
/* * Lion Constructor */ Lion::Lion(HashFunction* hash, StreamCipher* cipher, size_t bs) : m_block_size(std::max<size_t>(2*hash->output_length() + 1, bs)), m_hash(hash), m_cipher(cipher) { if(2*left_size() + 1 > m_block_size) throw Invalid_Argument(name() + ": Chosen block size is too small"); if(!m_cipher->valid_keylength(left_size())) throw Invalid_Argument(name() + ": This stream/hash combo is invalid"); m_key1.resize(left_size()); m_key2.resize(left_size()); }
/** * DataSource_Command Constructor */ DataSource_Command::DataSource_Command(const std::string& prog_and_args, const std::vector<std::string>& paths) : MAX_BLOCK_USECS(100000), KILL_WAIT(10000) { arg_list = split_on(prog_and_args, ' '); if(arg_list.size() == 0) throw Invalid_Argument("DataSource_Command: No command given"); if(arg_list.size() > 5) throw Invalid_Argument("DataSource_Command: Too many args"); pipe = 0; create_pipe(paths); }
void EC_PublicKey::set_parameter_encoding(EC_dompar_enc type) { if((type != ENC_EXPLICIT) && (type != ENC_IMPLICITCA) && (type != ENC_OID)) throw Invalid_Argument("Invalid encoding type for EC-key object specified"); affirm_init(); if((type == ENC_OID) && (mp_dom_pars->get_oid() == "")) throw Invalid_Argument("Invalid encoding type ENC_OID specified for " "EC-key object whose corresponding domain " "parameters are without oid"); m_param_enc = type; }
/* * CCM_Mode Constructor */ CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) : m_tag_size(tag_size), m_L(L), m_cipher(cipher) { if(m_cipher->block_size() != BS) throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode"); if(L < 2 || L > 8) throw Invalid_Argument("Invalid CCM L value " + std::to_string(L)); if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size)); }
void EC_PublicKey::set_parameter_encoding(EC_Group_Encoding form) { if(form != EC_DOMPAR_ENC_EXPLICIT && form != EC_DOMPAR_ENC_IMPLICITCA && form != EC_DOMPAR_ENC_OID) throw Invalid_Argument("Invalid encoding form for EC-key object specified"); if((form == EC_DOMPAR_ENC_OID) && (m_domain_params.get_oid() == "")) throw Invalid_Argument("Invalid encoding form OID specified for " "EC-key object whose corresponding domain " "parameters are without oid"); m_domain_encoding = form; }
/************************************************* * Decode a BigInt * *************************************************/ BigInt BigInt::decode(const byte buf[], u32bit length, Base base) { BigInt r; if(base == Binary) r.binary_decode(buf, length); #ifndef BOTAN_MINIMAL_BIGINT else if(base == Hexadecimal) { SecureVector<byte> hex; for(u32bit j = 0; j != length; ++j) if(Hex_Decoder::is_valid(buf[j])) hex.append(buf[j]); u32bit offset = (hex.size() % 2); SecureVector<byte> binary(hex.size() / 2 + offset); if(offset) { byte temp[2] = { '0', hex[0] }; binary[0] = Hex_Decoder::decode(temp); } for(u32bit j = offset; j != binary.size(); ++j) binary[j] = Hex_Decoder::decode(hex+2*j-offset); r.binary_decode(binary, binary.size()); } #endif else if(base == Decimal || base == Octal) { const u32bit RADIX = ((base == Decimal) ? 10 : 8); for(u32bit j = 0; j != length; ++j) { byte x = Charset::char2digit(buf[j]); if(x >= RADIX) { if(RADIX == 10) throw Invalid_Argument("BigInt: Invalid decimal string"); else throw Invalid_Argument("BigInt: Invalid octal string"); } r *= RADIX; r += x; } } else throw Invalid_Argument("Unknown BigInt decoding method"); return r; }
/* * DL_Group Initializer */ void DL_Group::initialize(const BigInt& p1, const BigInt& q1, const BigInt& g1) { if(p1 < 3) throw Invalid_Argument("DL_Group: Prime invalid"); if(g1 < 2 || g1 >= p1) throw Invalid_Argument("DL_Group: Generator invalid"); if(q1 < 0 || q1 >= p1) throw Invalid_Argument("DL_Group: Subgroup invalid"); m_p = p1; m_g = g1; m_q = q1; m_initialized = true; }
PointGFp::PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y) : m_curve(curve), m_coord_x(x), m_coord_y(y), m_coord_z(1) { if(x <= 0 || x >= curve.get_p()) throw Invalid_Argument("Invalid PointGFp affine x"); if(y <= 0 || y >= curve.get_p()) throw Invalid_Argument("Invalid PointGFp affine y"); m_curve.to_rep(m_coord_x, m_monty_ws); m_curve.to_rep(m_coord_y, m_monty_ws); m_curve.to_rep(m_coord_z, m_monty_ws); }
std::shared_ptr<const X509_Certificate> Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const { if(key_hash.size() != 20) { throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash"); } scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash)); check_notnull(key_hash_cfdata, "create key hash search object"); scoped_CFType<CFArrayRef> result(m_impl->search( { {kSecAttrPublicKeyHash, key_hash_cfdata.get()}, })); if(!result) { return nullptr; // no certificate found } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "certificate result list contains an object"); // `count` might be greater than 1, but we'll just select the first match auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0)); return readCertificate(cfCert); }
/* * Compress a message */ void CMS_Encoder::compress(const std::string& algo) { if(!CMS_Encoder::can_compress_with(algo)) throw Invalid_Argument("CMS_Encoder: Cannot compress with " + algo); Filter* compressor = 0; #if defined(BOTAN_HAS_COMPRESSOR_ZLIB) if(algo == "Zlib") compressor = new Zlib_Compression; #endif if(compressor == 0) throw Internal_Error("CMS: Couldn't get ahold of a compressor"); Pipe pipe(compressor); pipe.process_msg(data); SecureVector<byte> compressed = pipe.read_all(); DER_Encoder encoder; encoder.start_cons(SEQUENCE). encode(static_cast<size_t>(0)). encode(AlgorithmIdentifier("Compression." + algo, MemoryVector<byte>())). raw_bytes(make_econtent(compressed, type)). end_cons(); add_layer("CMS.CompressedData", encoder); }
/* * Read a PEM or BER X.509 object */ void X509_Object::init(DataSource& in, const std::string& labels) { m_PEM_labels_allowed = split_on(labels, '/'); if(m_PEM_labels_allowed.size() < 1) throw Invalid_Argument("Bad labels argument to X509_Object"); m_PEM_label_pref = m_PEM_labels_allowed[0]; std::sort(m_PEM_labels_allowed.begin(), m_PEM_labels_allowed.end()); try { if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) { BER_Decoder dec(in); decode_from(dec); } else { std::string got_label; DataSource_Memory ber(PEM_Code::decode(in, got_label)); if(!std::binary_search(m_PEM_labels_allowed.begin(), m_PEM_labels_allowed.end(), got_label)) throw Decoding_Error("Invalid PEM label: " + got_label); BER_Decoder dec(ber); decode_from(dec); } } catch(Decoding_Error& e) { throw Decoding_Error(m_PEM_label_pref + " decoding failed: " + e.what()); } }
/************************************************* * Initialize the certificate options * *************************************************/ X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts, u32bit expiration_time_in_seconds) { is_CA = false; path_limit = 0; constraints = NO_CONSTRAINTS; const u32bit now = system_time(); start = X509_Time(now); end = X509_Time(now + expiration_time_in_seconds); if(initial_opts == "") return; std::vector<std::string> parsed = split_on(initial_opts, '/'); if(parsed.size() > 4) throw Invalid_Argument("X.509 cert options: Too many names: " + initial_opts); if(parsed.size() >= 1) common_name = parsed[0]; if(parsed.size() >= 2) country = parsed[1]; if(parsed.size() >= 3) organization = parsed[2]; if(parsed.size() == 4) org_unit = parsed[3]; }
/* * Create an ASN1_EAC_String */ ASN1_EAC_String::ASN1_EAC_String(const std::string& str, ASN1_Tag t) : tag(t) { iso_8859_str = Charset::transcode(str, LOCAL_CHARSET, LATIN1_CHARSET); if(!sanity_check()) throw Invalid_Argument("ASN1_EAC_String contains illegal characters"); }
/* * Tiger Constructor */ Tiger::Tiger(size_t hash_len, size_t passes) : MDx_HashFunction(64, false, false), X(8), digest(3), hash_len(hash_len), passes(passes) { if(output_length() != 16 && output_length() != 20 && output_length() != 24) throw Invalid_Argument("Tiger: Illegal hash output size: " + to_string(output_length())); if(passes < 3) throw Invalid_Argument("Tiger: Invalid number of passes: " + to_string(passes)); clear(); }
/* * Prepend a Filter to the Pipe */ void Pipe::prepend(Filter* filter) { if(inside_msg) throw Invalid_State("Cannot prepend to a Pipe while it is processing"); if(!filter) return; if(dynamic_cast<SecureQueue*>(filter)) throw Invalid_Argument("Pipe::prepend: SecureQueue cannot be used"); if(filter->owned) throw Invalid_Argument("Filters cannot be shared among multiple Pipes"); filter->owned = true; if(pipe) filter->attach(pipe); pipe = filter; }
/************************************************* * Derive a key * *************************************************/ SecureVector<byte> DH_PrivateKey::derive_key(const BigInt& w) const { const BigInt& p = group_p(); if(w <= 1 || w >= p-1) throw Invalid_Argument(algo_name() + "::derive_key: Invalid key input"); return BigInt::encode_1363(core.agree(w), p.bytes()); }
/* * SSL3 PRF */ SecureVector<byte> SSL3_PRF::derive(size_t key_len, const byte secret[], size_t secret_len, const byte seed[], size_t seed_len) const { if(key_len > 416) throw Invalid_Argument("SSL3_PRF: Requested key length is too large"); MD5 md5; SHA_160 sha1; OctetString output; int counter = 0; while(key_len) { const size_t produce = std::min<size_t>(key_len, md5.output_length()); output = output + next_hash(counter++, produce, md5, sha1, secret, secret_len, seed, seed_len); key_len -= produce; } return output.bits_of(); }
/* * Multiply-Add Operation */ BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c) { if(c.is_negative() || c.is_zero()) throw Invalid_Argument("mul_add: Third argument must be > 0"); BigInt::Sign sign = BigInt::Positive; if(a.sign() != b.sign()) sign = BigInt::Negative; const size_t a_sw = a.sig_words(); const size_t b_sw = b.sig_words(); const size_t c_sw = c.sig_words(); BigInt r(sign, std::max(a.size() + b.size(), c_sw) + 1); secure_vector<word> workspace(r.size()); bigint_mul(r.mutable_data(), r.size(), workspace.data(), a.data(), a.size(), a_sw, b.data(), b.size(), b_sw); const size_t r_size = std::max(r.sig_words(), c_sw); bigint_add2(r.mutable_data(), r_size, c.data(), c_sw); return r; }