diff options
Diffstat (limited to 'botan/src/cms')
-rw-r--r-- | botan/src/cms/cms_algo.cpp | 164 | ||||
-rw-r--r-- | botan/src/cms/cms_comp.cpp | 107 | ||||
-rw-r--r-- | botan/src/cms/cms_dalg.cpp | 276 | ||||
-rw-r--r-- | botan/src/cms/cms_dec.cpp | 127 | ||||
-rw-r--r-- | botan/src/cms/cms_dec.h | 65 | ||||
-rw-r--r-- | botan/src/cms/cms_ealg.cpp | 401 | ||||
-rw-r--r-- | botan/src/cms/cms_enc.cpp | 86 | ||||
-rw-r--r-- | botan/src/cms/cms_enc.h | 92 | ||||
-rw-r--r-- | botan/src/cms/info.txt | 31 |
9 files changed, 1349 insertions, 0 deletions
diff --git a/botan/src/cms/cms_algo.cpp b/botan/src/cms/cms_algo.cpp new file mode 100644 index 0000000..748aa73 --- /dev/null +++ b/botan/src/cms/cms_algo.cpp @@ -0,0 +1,164 @@ +/* +* CMS Algorithm Specific Code +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_enc.h> +#include <botan/der_enc.h> +#include <botan/sha160.h> +#include <botan/cbc.h> +#include <botan/filters.h> +#include <botan/libstate.h> + +#if defined(BOTAN_HAS_RC2) + #include <botan/rc2.h> +#endif + +namespace Botan { + +namespace { + +/* +* Wrap a key as specified in RFC 3217 +*/ +SecureVector<byte> do_rfc3217_wrap(RandomNumberGenerator& rng, + const std::string& cipher_name, + const SymmetricKey& kek, + const SecureVector<byte>& input) + { + class Flip_Bytes : public Filter + { + public: + void write(const byte data[], u32bit length) + { + buf.append(data, length); + } + void end_msg() + { + for(u32bit j = 0; j != buf.size(); j++) + send(buf[buf.size()-j-1]); + buf.destroy(); + } + Flip_Bytes(const SecureVector<byte>& prefix) { buf.append(prefix); } + private: + SecureVector<byte> buf; + }; + + Algorithm_Factory& af = global_state().algorithm_factory(); + + const BlockCipher* cipher = af.prototype_block_cipher(cipher_name); + + if(!cipher || cipher->BLOCK_SIZE != 8) + throw Encoding_Error("do_rfc3217_wrap: Bad cipher: " + cipher_name); + + Pipe icv(new Hash_Filter(new SHA_160, 8)); + icv.process_msg(input); + + InitializationVector iv(rng, 8); + InitializationVector fixed("4ADDA22C79E82105"); + + Pipe pipe(new CBC_Encryption(cipher->clone(), new Null_Padding, kek, iv), + new Flip_Bytes(iv.bits_of()), + new CBC_Encryption(cipher->clone(), new Null_Padding, kek, iv)); + + pipe.start_msg(); + pipe.write(input); + pipe.write(icv.read_all()); + pipe.end_msg(); + return pipe.read_all(); + } + +} + +/* +* Wrap a CEK with a KEK +*/ +SecureVector<byte> CMS_Encoder::wrap_key(RandomNumberGenerator& rng, + const std::string& cipher, + const SymmetricKey& cek, + const SymmetricKey& kek) + { +#if defined(BOTAN_HAS_DES) + if(cipher == "TripleDES") + { + SymmetricKey cek_parity = cek; + cek_parity.set_odd_parity(); + return do_rfc3217_wrap(rng, cipher, kek, cek_parity.bits_of()); + } +#endif + +#if defined(BOTAN_HAS_RC2) || defined(BOTAN_HAS_CAST) + if(cipher == "RC2" || cipher == "CAST-128") + { + if(kek.length() != 16) + throw Encoding_Error("CMS: 128-bit KEKs must be used with " + cipher); + + SecureVector<byte> lcekpad; + lcekpad.append((byte)cek.length()); + lcekpad.append(cek.bits_of()); + while(lcekpad.size() % 8) + lcekpad.append(rng.next_byte()); + return do_rfc3217_wrap(rng, cipher, kek, lcekpad); + } +#endif + + throw Invalid_Argument("CMS_Encoder::wrap: Unknown cipher " + cipher); + } + +/* +* Encode the parameters for an encryption algo +*/ +SecureVector<byte> CMS_Encoder::encode_params(const std::string& cipher, + const SymmetricKey& key, + const InitializationVector& iv) + { + DER_Encoder encoder; + +#if defined(BOTAN_HAS_RC2) + if(cipher == "RC2") + { + encoder.start_cons(SEQUENCE). + encode((u32bit)RC2::EKB_code(8*key.length())). + encode(iv.bits_of(), OCTET_STRING). + end_cons(); + return encoder.get_contents(); + } +#endif + + if(cipher == "CAST-128") + { + encoder.start_cons(SEQUENCE). + encode(iv.bits_of(), OCTET_STRING). + encode(8*key.length()). + end_cons(); + } + else + encoder.encode(iv.bits_of(), OCTET_STRING); + + return encoder.get_contents(); + } + +/* +* Generate a CEK or KEK for the cipher +*/ +SymmetricKey CMS_Encoder::setup_key(RandomNumberGenerator& rng, + const std::string& cipher) + { + u32bit keysize = 0; + + if(cipher == "TripleDES") keysize = 24; + if(cipher == "RC2") keysize = 16; + if(cipher == "CAST-128") keysize = 16; + + if(keysize == 0) + throw Invalid_Argument("CMS: Cannot encrypt with cipher " + cipher); + + SymmetricKey key(rng, keysize); + if(cipher == "DES" || cipher == "TripleDES") + key.set_odd_parity(); + return key; + } + +} diff --git a/botan/src/cms/cms_comp.cpp b/botan/src/cms/cms_comp.cpp new file mode 100644 index 0000000..b11cf90 --- /dev/null +++ b/botan/src/cms/cms_comp.cpp @@ -0,0 +1,107 @@ +/* +* CMS Compression +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_enc.h> +#include <botan/cms_dec.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/oids.h> +#include <botan/pipe.h> + +#if defined(BOTAN_HAS_COMPRESSOR_ZLIB) + #include <botan/zlib.h> +#endif + +namespace Botan { + +/* +* 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((u32bit)0). + encode(AlgorithmIdentifier("Compression." + algo, + MemoryVector<byte>())). + raw_bytes(make_econtent(compressed, type)). + end_cons(); + + add_layer("CMS.CompressedData", encoder); + } + +/* +* See if the named compression algo is available +*/ +bool CMS_Encoder::can_compress_with(const std::string& algo) + { + if(algo == "") + throw Invalid_Algorithm_Name("Empty string to can_compress_with"); + +#if defined(BOTAN_HAS_COMPRESSOR_ZLIB) + if(algo == "Zlib") + return true; +#endif + + return false; + } + +/* +* Decompress a message +*/ +void CMS_Decoder::decompress(BER_Decoder& decoder) + { + u32bit version; + AlgorithmIdentifier comp_algo; + + BER_Decoder comp_info = decoder.start_cons(SEQUENCE); + + comp_info.decode(version); + if(version != 0) + throw Decoding_Error("CMS: Unknown version for CompressedData"); + + comp_info.decode(comp_algo); + read_econtent(comp_info); + comp_info.end_cons(); + + Filter* decompressor = 0; + + info = comp_algo.oid.as_string(); + +#if defined(BOTAN_HAS_COMPRESSOR_ZLIB) + if(comp_algo.oid == OIDS::lookup("Compression.Zlib")) + { + decompressor = new Zlib_Decompression; + info = "Zlib"; + } +#endif + + if(!decompressor) + status = FAILURE; + + Pipe pipe(decompressor); + pipe.process_msg(data); + data = pipe.read_all(); + } + +} diff --git a/botan/src/cms/cms_dalg.cpp b/botan/src/cms/cms_dalg.cpp new file mode 100644 index 0000000..7ed793f --- /dev/null +++ b/botan/src/cms/cms_dalg.cpp @@ -0,0 +1,276 @@ +/* +* CMS Decoding Operations +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_dec.h> +#include <botan/x509find.h> +#include <botan/ber_dec.h> +#include <botan/oids.h> +#include <botan/hash.h> +#include <botan/look_pk.h> +#include <botan/bigint.h> +#include <botan/libstate.h> +#include <memory> + +namespace Botan { + +namespace { + +/* +* Compute the hash of some content +*/ +SecureVector<byte> hash_of(const SecureVector<byte>& content, + const AlgorithmIdentifier& hash_algo, + std::string& hash_name) + { + hash_name = OIDS::lookup(hash_algo.oid); + + Algorithm_Factory& af = global_state().algorithm_factory(); + + std::auto_ptr<HashFunction> hash_fn(af.make_hash_function(hash_name)); + return hash_fn->process(content); + } + +/* +* Find a cert based on SignerIdentifier +*/ +std::vector<X509_Certificate> get_cert(BER_Decoder& signer_info, + X509_Store& store) + { + BER_Object id = signer_info.get_next_object(); + + std::vector<X509_Certificate> found; + + if(id.type_tag == SEQUENCE && id.class_tag == CONSTRUCTED) + { + X509_DN issuer; + BigInt serial; + BER_Decoder iands(id.value); + iands.decode(issuer); + iands.decode(serial); + + found = store.get_certs(IandS_Match(issuer, BigInt::encode(serial))); + } + else if(id.type_tag == 0 && id.class_tag == CONSTRUCTED) + found = store.get_certs(SKID_Match(id.value)); + else + throw Decoding_Error("CMS: Unknown tag for cert identifier"); + + // verify cert if found + + if(found.size() > 1) + throw Internal_Error("CMS: Found more than one match in get_cert"); + return found; + } + +/* +* Read OriginatorInfo +*/ +void read_orig_info(BER_Decoder& info, X509_Store& store) + { + BER_Object next = info.get_next_object(); + + if(next.type_tag == 0 && + next.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) + { + DataSource_Memory certs(next.value); + while(!certs.end_of_data()) + { + // FIXME: can be attribute certs too + // FIXME: DoS? + X509_Certificate cert(certs); + store.add_cert(cert); + } + next = info.get_next_object(); + } + if(next.type_tag == 1 && + next.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) + { + DataSource_Memory crls(next.value); + while(!crls.end_of_data()) + { + // FIXME: DoS? + X509_CRL crl(crls); + store.add_crl(crl); + } + next = info.get_next_object(); + } + info.push_back(next); + } + +/* +* Decode any Attributes, and check type +*/ +SecureVector<byte> decode_attributes(BER_Decoder& ber, const OID& type, + bool& bad_attributes) + { + BER_Object obj = ber.get_next_object(); + SecureVector<byte> digest; + + bool got_digest = false; + bool got_content_type = false; + + if(obj.type_tag == 0 && + obj.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) + ber.push_back(obj); + else + { + BER_Decoder attributes(obj.value); + while(attributes.more_items()) + { + Attribute attr; + attributes.decode(attr); + BER_Decoder attr_value(attr.parameters); + + if(attr.oid == OIDS::lookup("PKCS9.MessageDigest")) + { + got_digest = true; + attr_value.decode(digest, OCTET_STRING); + } + else if(attr.oid == OIDS::lookup("PKCS9.ContentType")) + { + got_content_type = true; + OID inner_type; + attr_value.decode(inner_type); + if(inner_type != type) + bad_attributes = true; + } + else + throw Decoding_Error("Unknown/unhandled CMS attribute found: " + + OIDS::lookup(attr.oid)); + } + + if(!got_digest || !got_content_type) + bad_attributes = true; + } + + return digest; + } + +} + +/* +* Decode this layer of CMS encoding +*/ +void CMS_Decoder::decode_layer() + { + try { + if(status == FAILURE) + throw Invalid_State("CMS: Decoder is in FAILURE state"); + + status = GOOD; + info = ""; + + type = next_type; + + if(type == OIDS::lookup("CMS.DataContent")) + return; + + BER_Decoder decoder(data); + if(type == OIDS::lookup("CMS.CompressedData")) + decompress(decoder); + else if(type == OIDS::lookup("CMS.DigestedData")) + { + u32bit version; + AlgorithmIdentifier hash_algo; + SecureVector<byte> digest; + + BER_Decoder hash_info = decoder.start_cons(SEQUENCE); + + hash_info.decode(version); + if(version != 0 && version != 2) + throw Decoding_Error("CMS: Unknown version for DigestedData"); + + hash_info.decode(hash_algo); + read_econtent(hash_info); + hash_info.decode(digest, OCTET_STRING); + hash_info.end_cons(); + + if(digest != hash_of(data, hash_algo, info)) + status = BAD; + } + else if(type == OIDS::lookup("CMS.SignedData")) + { +#if 1 + throw Exception("FIXME: not implemented"); +#else + u32bit version; + + BER_Decoder sig_info = BER::get_subsequence(decoder); + BER::decode(sig_info, version); + if(version != 1 && version != 3) + throw Decoding_Error("CMS: Unknown version for SignedData"); + BER::get_subset(sig_info); // hash algos (do something with these?) + read_econtent(sig_info); + read_orig_info(sig_info, store); + + BER_Decoder signer_infos = BER::get_subset(sig_info); + while(signer_infos.more_items()) + { + AlgorithmIdentifier sig_algo, hash_algo; + SecureVector<byte> signature, digest; + u32bit version; + + BER_Decoder signer_info = BER::get_subsequence(signer_infos); + BER::decode(signer_info, version); + if(version != 1 && version != 3) + throw Decoding_Error("CMS: Unknown version for SignerInfo"); + + std::vector<X509_Certificate> certs = get_cert(signer_info, store); + if(certs.size() == 0) { status = NO_KEY; continue; } + + BER::decode(signer_info, hash_algo); + bool bad_attr = false; + digest = decode_attributes(signer_info, next_type, bad_attr); + if(bad_attr) { status = BAD; continue; } + BER::decode(signer_info, sig_algo); + BER::decode(signer_info, signature, OCTET_STRING); + // unsigned attributes + signer_info.verify_end(); + + if(digest.has_items()) + { + std::string hash; + if(digest != hash_of(data, hash_algo, hash)) + { + status = BAD; + continue; + } + status = check_sig(signed_attr, sig_algo, signature, certs[0]); + } + else + status = check_sig(data, sig_algo, signature, certs[0]); + + if(status == BAD) + continue; + + // fix this (gets only last signer, for one thing) + // maybe some way for the user to get all certs that signed the + // message? that would be useful + info = "CN=" + cert.subject_info("CommonName") + + ",O=" + cert.subject_info("Organization") + + ",OU=" + cert.subject_info("Organizational Unit"); + } +#endif + } + else if(type == OIDS::lookup("CMS.EnvelopedData")) + { + throw Exception("FIXME: not implemented"); + } + else if(type == OIDS::lookup("CMS.AuthenticatedData")) + { + throw Exception("FIXME: not implemented"); + } + else + throw Decoding_Error("CMS: Unknown content ID " + type.as_string()); + } + catch(std::exception) + { + status = FAILURE; + } + } + +} diff --git a/botan/src/cms/cms_dec.cpp b/botan/src/cms/cms_dec.cpp new file mode 100644 index 0000000..222399f --- /dev/null +++ b/botan/src/cms/cms_dec.cpp @@ -0,0 +1,127 @@ +/* +* CMS Decoding +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_dec.h> +#include <botan/ber_dec.h> +#include <botan/asn1_int.h> +#include <botan/oids.h> +#include <botan/pem.h> + +namespace Botan { + +/* +* CMS_Decoder Constructor +*/ +CMS_Decoder::CMS_Decoder(DataSource& in, const X509_Store& x509store, + User_Interface& ui_ref, PKCS8_PrivateKey* key) : + ui(ui_ref), store(x509store) + { + status = GOOD; + + add_key(key); + + if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) + initial_read(in); + else + { + DataSource_Memory ber(PEM_Code::decode_check_label(in, "PKCS7")); + initial_read(ber); + } + } + +/* +* Read the outermost ContentInfo +*/ +void CMS_Decoder::initial_read(DataSource&) + { + // FIXME... + + /* + BER_Decoder decoder(in); + BER_Decoder content_info = decoder.start_cons(SEQUENCE); + + content_info.decode(next_type); + + + BER_Decoder content_type = BER::get_subsequence(content_info, ASN1_Tag(0)); + data = content_type.get_remaining(); + */ + + decode_layer(); + } + +/* +* Add another private key to use +*/ +void CMS_Decoder::add_key(PKCS8_PrivateKey* key) + { + if(!key) + return; + +#if 0 + for(u32bit j = 0; j != keys.size(); j++) + if(keys[j]->key_id() == key->key_id()) + return; +#endif + + keys.push_back(key); + } + +/* +* Return the status information +*/ +CMS_Decoder::Status CMS_Decoder::layer_status() const + { + return status; + } + +/* +* Return the final data content +*/ +std::string CMS_Decoder::get_data() const + { + if(layer_type() != DATA) + throw Invalid_State("CMS: Cannot retrieve data from non-DATA layer"); + return std::string((const char*)data.begin(), data.size()); + } + +/* +* Return the content type of this layer +*/ +CMS_Decoder::Content_Type CMS_Decoder::layer_type() const + { + if(type == OIDS::lookup("CMS.DataContent")) return DATA; + if(type == OIDS::lookup("CMS.EnvelopedData")) return ENVELOPED; + if(type == OIDS::lookup("CMS.CompressedData")) return COMPRESSED; + if(type == OIDS::lookup("CMS.SignedData")) return SIGNED; + if(type == OIDS::lookup("CMS.AuthenticatedData")) return AUTHENTICATED; + if(type == OIDS::lookup("CMS.DigestedData")) return DIGESTED; + return UNKNOWN; + } + +/* +* Return some information about this layer +*/ +std::string CMS_Decoder::layer_info() const + { + return info; + } + +/* +* Return some information about this layer +*/ +void CMS_Decoder::read_econtent(BER_Decoder& decoder) + { + BER_Decoder econtent_info = decoder.start_cons(SEQUENCE); + econtent_info.decode(next_type); + + // FIXME + //BER_Decoder econtent = BER::get_subsequence(econtent_info, ASN1_Tag(0)); + //econtent.decode(data, OCTET_STRING); + } + +} diff --git a/botan/src/cms/cms_dec.h b/botan/src/cms/cms_dec.h new file mode 100644 index 0000000..75b61c9 --- /dev/null +++ b/botan/src/cms/cms_dec.h @@ -0,0 +1,65 @@ +/* +* CMS Decoding +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CMS_DECODER_H__ +#define BOTAN_CMS_DECODER_H__ + +#include <botan/x509cert.h> +#include <botan/x509stor.h> +#include <botan/pkcs8.h> +#include <botan/ber_dec.h> +#include <botan/ui.h> + +namespace Botan { + +/* +* CMS Decoding Operation +*/ +class BOTAN_DLL CMS_Decoder + { + public: + enum Status { GOOD, BAD, NO_KEY, FAILURE }; + + enum Content_Type { DATA, UNKNOWN, COMPRESSED, ENVELOPED, SIGNED, + AUTHENTICATED, DIGESTED }; + + Status layer_status() const; + Content_Type layer_type() const; + std::string layer_info() const; + std::string layer_algo() const; + std::string get_data() const; + std::vector<X509_Certificate> get_certs() const; + std::vector<X509_CRL> get_crls() const; + + void next_layer() { decode_layer(); } + + void add_key(PKCS8_PrivateKey*); + + CMS_Decoder(DataSource&, const X509_Store&, User_Interface&, + PKCS8_PrivateKey* = 0); + private: + std::string get_passphrase(const std::string&); + void read_econtent(BER_Decoder&); + void initial_read(DataSource&); + void decode_layer(); + void decompress(BER_Decoder&); + + User_Interface& ui; + + X509_Store store; + std::vector<std::string> passphrases; + std::vector<PKCS8_PrivateKey*> keys; + + OID type, next_type; + SecureVector<byte> data; + Status status; + std::string info; + }; + +} + +#endif diff --git a/botan/src/cms/cms_ealg.cpp b/botan/src/cms/cms_ealg.cpp new file mode 100644 index 0000000..2970e8e --- /dev/null +++ b/botan/src/cms/cms_ealg.cpp @@ -0,0 +1,401 @@ +/* +* CMS Encoding Operations +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_enc.h> +#include <botan/der_enc.h> +#include <botan/x509find.h> +#include <botan/bigint.h> +#include <botan/oids.h> +#include <botan/cbc.h> +#include <botan/hash.h> +#include <botan/look_pk.h> +#include <botan/libstate.h> +#include <botan/pipe.h> +#include <memory> + +namespace Botan { + +namespace { + +/* +* Choose an algorithm +*/ +std::string choose_algo(const std::string& user_algo, + const std::string& default_algo) + { + if(user_algo == "") + return global_state().deref_alias(default_algo); + return global_state().deref_alias(user_algo); + } + +/* +* Encode a SignerIdentifier/RecipientIdentifier +*/ +DER_Encoder& encode_si(DER_Encoder& der, const X509_Certificate& cert, + bool use_skid_encoding = false) + { + if(cert.subject_key_id().size() && use_skid_encoding) + der.encode(cert.subject_key_id(), OCTET_STRING, ASN1_Tag(0)); + else + { + der.start_cons(SEQUENCE). + encode(cert.issuer_dn()). + encode(BigInt::decode(cert.serial_number())). + end_cons(); + } + + return der; + } + +/* +* Compute the hash of some content +*/ +SecureVector<byte> hash_of(const SecureVector<byte>& content, + const std::string& hash_name) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + std::auto_ptr<HashFunction> hash_fn(af.make_hash_function(hash_name)); + return hash_fn->process(content); + } + +/* +* Encode Attributes containing info on content +*/ +SecureVector<byte> encode_attr(const SecureVector<byte>& data, + const std::string& type, + const std::string& hash) + { + SecureVector<byte> digest = hash_of(data, hash); + + DER_Encoder encoder; + encoder.encode(OIDS::lookup(type)); + Attribute content_type("PKCS9.ContentType", encoder.get_contents()); + + encoder.encode(digest, OCTET_STRING); + Attribute message_digest("PKCS9.MessageDigest", encoder.get_contents()); + + encoder.start_cons(SET) + .encode(content_type) + .encode(message_digest) + .end_cons(); + + return encoder.get_contents(); + } + +} + +/* +* Encrypt a message +*/ +void CMS_Encoder::encrypt(RandomNumberGenerator& rng, + const X509_Certificate& to, + const std::string user_cipher) + { + const std::string cipher = choose_algo(user_cipher, "TripleDES"); + + std::auto_ptr<X509_PublicKey> key(to.subject_public_key()); + const std::string algo = key->algo_name(); + + Key_Constraints constraints = to.constraints(); + + if(algo == "RSA") + { + if(constraints != NO_CONSTRAINTS && !(constraints & KEY_ENCIPHERMENT)) + throw Invalid_Argument("CMS: Constraints not set for encryption"); + + PK_Encrypting_Key* enc_key = dynamic_cast<PK_Encrypting_Key*>(key.get()); + if(enc_key == 0) + throw Internal_Error("CMS_Encoder::encrypt: " + algo + + " can't encrypt"); + + encrypt_ktri(rng, to, enc_key, cipher); + } + else if(algo == "DH") + { + if(constraints != NO_CONSTRAINTS && !(constraints & KEY_AGREEMENT)) + throw Invalid_Argument("CMS: Constraints not set for key agreement"); + + encrypt_kari(rng, to, key.get(), cipher); + } + else + throw Invalid_Argument("Unknown CMS PK encryption algorithm " + algo); + } + +/* +* Encrypt a message with a key transport algo +*/ +void CMS_Encoder::encrypt_ktri(RandomNumberGenerator& rng, + const X509_Certificate& to, + PK_Encrypting_Key* pub_key, + const std::string& cipher) + { + const std::string padding = "EME-PKCS1-v1_5"; + const std::string pk_algo = pub_key->algo_name(); + std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(*pub_key, padding)); + + SymmetricKey cek = setup_key(rng, cipher); + + AlgorithmIdentifier alg_id(OIDS::lookup(pk_algo + '/' + padding), + AlgorithmIdentifier::USE_NULL_PARAM); + + DER_Encoder encoder; + + encoder.start_cons(SEQUENCE) + .encode((u32bit)0) + .start_cons(SET) + .start_cons(SEQUENCE) + .encode((u32bit)0); + encode_si(encoder, to) + .encode(alg_id) + .encode(enc->encrypt(cek.bits_of(), rng), OCTET_STRING) + .end_cons() + .end_cons() + .raw_bytes(do_encrypt(rng, cek, cipher)) + .end_cons(); + + add_layer("CMS.EnvelopedData", encoder); + } + +/* +* Encrypt a message with a key agreement algo +*/ +void CMS_Encoder::encrypt_kari(RandomNumberGenerator&, + const X509_Certificate&, + X509_PublicKey*, + const std::string&) + { + throw Exception("FIXME: unimplemented"); + +#if 0 + SymmetricKey cek = setup_key(rng, cipher); + + DER_Encoder encoder; + encoder.start_cons(SEQUENCE); + encoder.encode(2); + encoder.start_cons(SET); + encoder.start_sequence(ASN1_Tag(1)); + encoder.encode(3); + encode_si(encoder, to); + encoder.encode(AlgorithmIdentifier(pk_algo + "/" + padding)); + encoder.encode(encrypted_cek, OCTET_STRING); + encoder.end_cons(); + encoder.end_cons(); + encoder.raw_bytes(do_encrypt(rng, cek, cipher)); + encoder.end_cons(); + + add_layer("CMS.EnvelopedData", encoder); +#endif + } + +/* +* Encrypt a message with a shared key +*/ +void CMS_Encoder::encrypt(RandomNumberGenerator& rng, + const SymmetricKey& kek, + const std::string& user_cipher) + { + throw Exception("FIXME: untested"); + + const std::string cipher = choose_algo(user_cipher, "TripleDES"); + SymmetricKey cek = setup_key(rng, cipher); + + SecureVector<byte> kek_id; // FIXME: ? + + DER_Encoder encoder; + + encoder.start_cons(SEQUENCE) + .encode((u32bit)2) + .start_explicit(ASN1_Tag(2)) + .encode((u32bit)4) + .start_cons(SEQUENCE) + .encode(kek_id, OCTET_STRING) + .end_cons() + .encode(AlgorithmIdentifier(OIDS::lookup("KeyWrap." + cipher), + AlgorithmIdentifier::USE_NULL_PARAM)) + .encode(wrap_key(rng, cipher, cek, kek), OCTET_STRING) + .end_cons() + .raw_bytes(do_encrypt(rng, cek, cipher)) + .end_cons(); + + add_layer("CMS.EnvelopedData", encoder); + } + +/* +* Encrypt a message with a passphrase +*/ +void CMS_Encoder::encrypt(RandomNumberGenerator&, + const std::string&, + const std::string& user_cipher) + { + const std::string cipher = choose_algo(user_cipher, "TripleDES"); + throw Exception("FIXME: unimplemented"); + /* + SymmetricKey cek = setup_key(key); + + DER_Encoder encoder; + encoder.start_cons(SEQUENCE); + encoder.encode(0); + encoder.raw_bytes(do_encrypt(rng, cek, cipher)); + encoder.end_cons(); + + add_layer("CMS.EnvelopedData", encoder); + */ + } + +/* +* Encrypt the content with the chosen key/cipher +*/ +SecureVector<byte> CMS_Encoder::do_encrypt(RandomNumberGenerator& rng, + const SymmetricKey& key, + const std::string& cipher_name) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + const BlockCipher* cipher = af.prototype_block_cipher(cipher_name); + + if(!cipher) + throw Invalid_Argument("CMS: Can't encrypt with non-existent cipher " + cipher_name); + + if(!OIDS::have_oid(cipher->name() + "/CBC")) + throw Encoding_Error("CMS: No OID assigned for " + cipher_name + "/CBC"); + + InitializationVector iv(rng, cipher->BLOCK_SIZE); + + AlgorithmIdentifier content_cipher; + content_cipher.oid = OIDS::lookup(cipher->name() + "/CBC"); + content_cipher.parameters = encode_params(cipher->name(), key, iv); + + Pipe pipe(new CBC_Encryption(cipher->clone(), new PKCS7_Padding, key, iv)); + + pipe.process_msg(data); + + DER_Encoder encoder; + encoder.start_cons(SEQUENCE); + encoder.encode(OIDS::lookup(type)); + encoder.encode(content_cipher); + encoder.encode(pipe.read_all(), OCTET_STRING, ASN1_Tag(0)); + encoder.end_cons(); + + return encoder.get_contents(); + } + +/* +* Sign a message +*/ +void CMS_Encoder::sign(const X509_Certificate& cert, + const PKCS8_PrivateKey& key, + RandomNumberGenerator& rng, + const std::vector<X509_Certificate>& chain, + const std::string& hash, + const std::string& pad_algo) + { + std::string padding = pad_algo + "(" + hash + ")"; + + // FIXME: Add new get_format() func to PK_Signing_Key, PK_Verifying_*_Key + Signature_Format format = IEEE_1363; + + const PK_Signing_Key& sig_key = dynamic_cast<const PK_Signing_Key&>(key); + std::auto_ptr<PK_Signer> signer(get_pk_signer(sig_key, padding, format)); + + AlgorithmIdentifier sig_algo(OIDS::lookup(key.algo_name() + "/" + padding), + AlgorithmIdentifier::USE_NULL_PARAM); + + SecureVector<byte> signed_attr = encode_attr(data, type, hash); + signer->update(signed_attr); + SecureVector<byte> signature = signer->signature(rng); + signed_attr[0] = 0xA0; + + const u32bit SI_VERSION = cert.subject_key_id().size() ? 3 : 1; + const u32bit CMS_VERSION = (type != "CMS.DataContent") ? 3 : SI_VERSION; + + DER_Encoder encoder; + + encoder.start_cons(SEQUENCE) + .encode(CMS_VERSION) + .start_cons(SET) + .encode(AlgorithmIdentifier(OIDS::lookup(hash), + AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons() + .raw_bytes(make_econtent(data, type)); + + encoder.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC); + for(u32bit j = 0; j != chain.size(); j++) + encoder.raw_bytes(chain[j].BER_encode()); + encoder.raw_bytes(cert.BER_encode()).end_cons(); + + encoder.start_cons(SET) + .start_cons(SEQUENCE) + .encode(SI_VERSION); + encode_si(encoder, cert, ((SI_VERSION == 3) ? true : false)) + .encode( + AlgorithmIdentifier(OIDS::lookup(hash), + AlgorithmIdentifier::USE_NULL_PARAM) + ) + .raw_bytes(signed_attr) + .encode(sig_algo) + .encode(signature, OCTET_STRING) + .end_cons() + .end_cons() + .end_cons(); + + add_layer("CMS.SignedData", encoder); + } + +/* +* Digest a message +*/ +void CMS_Encoder::digest(const std::string& user_hash) + { + const std::string hash = choose_algo(user_hash, "SHA-1"); + if(!OIDS::have_oid(hash)) + throw Encoding_Error("CMS: No OID assigned for " + hash); + + const u32bit VERSION = (type != "CMS.DataContent") ? 2 : 0; + + DER_Encoder encoder; + encoder.start_cons(SEQUENCE) + .encode(VERSION) + .encode(AlgorithmIdentifier(OIDS::lookup(hash), + AlgorithmIdentifier::USE_NULL_PARAM)) + .raw_bytes(make_econtent(data, type)) + .encode(hash_of(data, hash), OCTET_STRING) + .end_cons(); + + add_layer("CMS.DigestedData", encoder); + } + +/* +* MAC a message with an encrypted key +*/ +void CMS_Encoder::authenticate(const X509_Certificate&, + const std::string& mac_algo) + { + const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)"); + throw Exception("FIXME: unimplemented"); + } + +/* +* MAC a message with a shared key +*/ +void CMS_Encoder::authenticate(const SymmetricKey&, + const std::string& mac_algo) + { + const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)"); + throw Exception("FIXME: unimplemented"); + } + +/* +* MAC a message with a passphrase +*/ +void CMS_Encoder::authenticate(const std::string&, + const std::string& mac_algo) + { + const std::string mac = choose_algo(mac_algo, "HMAC(SHA-1)"); + throw Exception("FIXME: unimplemented"); + } + +} diff --git a/botan/src/cms/cms_enc.cpp b/botan/src/cms/cms_enc.cpp new file mode 100644 index 0000000..2413676 --- /dev/null +++ b/botan/src/cms/cms_enc.cpp @@ -0,0 +1,86 @@ +/* +* CMS Encoding Base +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cms_enc.h> +#include <botan/der_enc.h> +#include <botan/oids.h> +#include <botan/pem.h> + +namespace Botan { + +/* +* Setup the intitial layer of CMS data +*/ +void CMS_Encoder::set_data(const byte buf[], u32bit length) + { + if(data.has_items()) + throw Invalid_State("Cannot call CMS_Encoder::set_data here"); + + data.set(buf, length); + type = "CMS.DataContent"; + } + +/* +* Setup the intitial layer of CMS data +*/ +void CMS_Encoder::set_data(const std::string& str) + { + set_data((const byte*)str.c_str(), str.length()); + } + +/* +* Finalize and return the CMS encoded data +*/ +SecureVector<byte> CMS_Encoder::get_contents() + { + DER_Encoder encoder; + + encoder.start_cons(SEQUENCE). + encode(OIDS::lookup(type)). + start_explicit(0). + raw_bytes(data). + end_explicit(). + end_cons(); + + data.clear(); + + return encoder.get_contents(); + } + +/* +* Add a new layer of encapsulation +*/ +void CMS_Encoder::add_layer(const std::string& oid, DER_Encoder& new_layer) + { + data = new_layer.get_contents(); + type = oid; + } + +/* +* Return the PEM-encoded data +*/ +std::string CMS_Encoder::PEM_contents() + { + return PEM_Code::encode(get_contents(), "PKCS7"); + } + +/* +* Make an EncapsulatedContentInfo +*/ +SecureVector<byte> CMS_Encoder::make_econtent(const SecureVector<byte>& data, + const std::string& type) + { + return DER_Encoder().start_cons(SEQUENCE). + encode(OIDS::lookup(type)). + start_explicit(0). + encode(data, OCTET_STRING). + end_explicit(). + end_cons(). + get_contents(); + } + +} diff --git a/botan/src/cms/cms_enc.h b/botan/src/cms/cms_enc.h new file mode 100644 index 0000000..6fdd2b7 --- /dev/null +++ b/botan/src/cms/cms_enc.h @@ -0,0 +1,92 @@ +/* +* CMS Encoding +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CMS_ENCODER_H__ +#define BOTAN_CMS_ENCODER_H__ + +#include <botan/x509cert.h> +#include <botan/x509stor.h> +#include <botan/pkcs8.h> +#include <botan/symkey.h> + +namespace Botan { + +/* +* CMS Encoding Operation +*/ +class BOTAN_DLL CMS_Encoder + { + public: + + void encrypt(RandomNumberGenerator&, + const X509_Certificate&, const std::string = ""); + + void encrypt(RandomNumberGenerator& rng, + const std::string&, const std::string& = ""); + + void encrypt(RandomNumberGenerator& rng, + const SymmetricKey&, const std::string& = ""); + + void authenticate(const X509_Certificate&, const std::string& = ""); + void authenticate(const std::string&, const std::string& = ""); + void authenticate(const SymmetricKey&, const std::string& = ""); + + void sign(const X509_Certificate& cert, + const PKCS8_PrivateKey& key, + RandomNumberGenerator& rng, + const std::vector<X509_Certificate>& cert_chain, + const std::string& hash, + const std::string& padding); + + void digest(const std::string& = ""); + + void compress(const std::string&); + static bool can_compress_with(const std::string&); + + SecureVector<byte> get_contents(); + std::string PEM_contents(); + + void set_data(const std::string&); + void set_data(const byte[], u32bit); + + CMS_Encoder(const std::string& str) { set_data(str); } + CMS_Encoder(const byte buf[], u32bit length) { set_data(buf, length); } + private: + void add_layer(const std::string&, DER_Encoder&); + + void encrypt_ktri(RandomNumberGenerator&, + const X509_Certificate&, PK_Encrypting_Key*, + const std::string&); + void encrypt_kari(RandomNumberGenerator&, + const X509_Certificate&, X509_PublicKey*, + const std::string&); + + SecureVector<byte> do_encrypt(RandomNumberGenerator& rng, + const SymmetricKey&, const std::string&); + + static SecureVector<byte> make_econtent(const SecureVector<byte>&, + const std::string&); + + static SymmetricKey setup_key(RandomNumberGenerator& rng, + const std::string&); + + static SecureVector<byte> wrap_key(RandomNumberGenerator& rng, + const std::string&, + const SymmetricKey&, + const SymmetricKey&); + + static SecureVector<byte> encode_params(const std::string&, + const SymmetricKey&, + const InitializationVector&); + + SecureVector<byte> data; + std::string type; + }; + +} + +#endif diff --git a/botan/src/cms/info.txt b/botan/src/cms/info.txt new file mode 100644 index 0000000..82c31b5 --- /dev/null +++ b/botan/src/cms/info.txt @@ -0,0 +1,31 @@ +realname "CMS" + +define CMS + +load_on auto + +<add> +cms_algo.cpp +cms_comp.cpp +cms_dalg.cpp +cms_dec.cpp +cms_dec.h +cms_ealg.cpp +cms_enc.cpp +cms_enc.h +</add> + +<requires> +asn1 +bigint +cbc +filters +hash +libstate +oid_lookup +pem +pk_codecs +sha1 +sym_algo +x509 +</requires> |