diff options
author | lloyd <[email protected]> | 2008-09-28 20:58:26 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2008-09-28 20:58:26 +0000 |
commit | 3948d38e2bef3f42169f96a17cc5daa6e03fb575 (patch) | |
tree | 1fe40baedc9e49a5d734b100790f79ac70839513 /src/cms/cms_dalg.cpp | |
parent | f5ebea8d593d2f6d5b536ddb978f44d80d4e873f (diff) |
Move CMS code into main src tree, though it currently doesn't compile (needs further updating)
Diffstat (limited to 'src/cms/cms_dalg.cpp')
-rw-r--r-- | src/cms/cms_dalg.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/cms/cms_dalg.cpp b/src/cms/cms_dalg.cpp new file mode 100644 index 000000000..cd38c596d --- /dev/null +++ b/src/cms/cms_dalg.cpp @@ -0,0 +1,270 @@ +/************************************************* +* CMS Decoding Operations Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/cms_dec.h> +#include <botan/x509find.h> +#include <botan/ber_dec.h> +#include <botan/oids.h> +#include <botan/lookup.h> +#include <botan/look_pk.h> +#include <botan/bigint.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); + std::auto_ptr<HashFunction> hash_fn(get_hash(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; + } + } + +} |