--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/security/Ciphersuite_PI2.cc Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,1148 @@
+/*
+ * Copyright 2006 SPARTA Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <dtn-config.h>
+#endif
+
+#ifdef BSP_ENABLED
+
+#define OPENSSL_FIPS 1 /* required for sha256 */
+
+#include "Ciphersuite_PI2.h"
+#include "Ciphersuite_PC3.h"
+#include "bundling/Bundle.h"
+#include "bundling/BundleDaemon.h"
+#include "bundling/BundleProtocol.h"
+#include "bundling/SDNV.h"
+#include "contacts/Link.h"
+#include "security/KeySteward.h"
+#include "openssl/evp.h"
+
+namespace dtn {
+
+static const char * log = "/dtn/bundle/ciphersuite";
+
+/**
+ * Local definition borrowed from PrimaryBlockProcessor.h
+ * and with frag_offest and orig_length added
+ */
+struct PrimaryBlock_ex {
+ u_int8_t version;
+ u_int64_t processing_flags;
+ u_int64_t block_length;
+ u_int64_t dest_scheme_offset;
+ u_int64_t dest_ssp_offset;
+ u_int64_t source_scheme_offset;
+ u_int64_t source_ssp_offset;
+ u_int64_t replyto_scheme_offset;
+ u_int64_t replyto_ssp_offset;
+ u_int64_t custodian_scheme_offset;
+ u_int64_t custodian_ssp_offset;
+ u_int64_t creation_time;
+ u_int64_t creation_sequence;
+ u_int64_t lifetime;
+ u_int64_t dictionary_length;
+ u_int64_t fragment_offset;
+ u_int64_t original_length;
+};
+
+// Need quad versions of hton for manipulating full-length (unpacked) SDNV values
+
+#if defined(WORDS_BIGENDIAN) && (WORDS_BIGENDIAN == 1)
+#define htonq( x ) (x)
+#define ntohq( x ) (x)
+#else
+
+inline u_int64_t htonq( u_int64_t x )
+{
+ u_int64_t res;
+ u_int32_t hi = x >> 32;
+ u_int32_t lo = x & 0xffffffff;
+ hi = htonl( hi );
+ res = htonl( lo );
+ res = res << 32 | hi;
+
+ return res;
+}
+
+inline u_int64_t ntohq( u_int64_t x )
+{
+ u_int64_t res;
+ u_int32_t hi = x >> 32;
+ u_int32_t lo = x & 0xffffffff;
+ hi = ntohl( hi );
+ res = ntohl( lo );
+ res = res << 32 | hi;
+
+ return res;
+}
+#endif
+
+
+//----------------------------------------------------------------------
+Ciphersuite_PI2::Ciphersuite_PI2()
+{
+}
+
+//----------------------------------------------------------------------
+u_int16_t
+Ciphersuite_PI2::cs_num(void)
+{
+ return CSNUM_PI2;
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PI2::consume(Bundle* bundle,
+ BlockInfo* block,
+ u_char* buf,
+ size_t len)
+{
+ int cc = block->owner()->consume(bundle, block, buf, len);
+
+ if (cc == -1) {
+ return -1; // protocol error
+ }
+
+
+ // in on-the-fly scenario, process this data for those interested
+
+ if (! block->complete()) {
+ ASSERT(cc == (int)len);
+ return cc;
+ }
+
+ if ( block->locals() == NULL ) { // then we need to parse it
+ parse(block);
+ }
+
+ return cc;
+}
+
+//----------------------------------------------------------------------
+bool
+Ciphersuite_PI2::validate(const Bundle* bundle,
+ BlockInfoVec* block_list,
+ BlockInfo* block,
+ status_report_reason_t* reception_reason,
+ status_report_reason_t* deletion_reason)
+{
+ (void)reception_reason;
+
+ size_t sdnv_len;
+ u_char* buf;
+ size_t len;
+ size_t digest_len;
+ u_char ps_digest[EVP_MAX_MD_SIZE];
+ BP_Local_CS* locals = NULL;
+ u_int64_t field_length;
+ std::vector<u_int64_t> correlator_list;
+ std::vector<u_int64_t>::iterator cl_iter;
+ EndpointID local_eid = BundleDaemon::instance()->local_eid();
+ BlockInfoVec::iterator iter;
+ u_int16_t cs_flags;
+ int err = 0;
+ DataBuffer db;
+
+ log_debug_p(log, "Ciphersuite_PI2::validate()");
+ locals = dynamic_cast<BP_Local_CS*>(block->locals());
+ CS_FAIL_IF_NULL(locals);
+ cs_flags = locals->cs_flags();
+
+ if ( destination_is_local_node(bundle, block) )
+ { //yes - this is ours so go to work
+
+ if ( !(cs_flags & CS_BLOCK_HAS_RESULT) ) {
+ log_err_p(log, "Ciphersuite_PI2::validate: block has no security_result");
+ goto fail;
+ }
+
+ create_digest(bundle, block_list, block, db);
+ digest_len = db.len();
+ memcpy(ps_digest, db.buf(), digest_len);
+
+ log_debug_p(log, "Ciphersuite_PI2::validate() digest 0x%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx",
+ ps_digest[0], ps_digest[1], ps_digest[2], ps_digest[3], ps_digest[4], ps_digest[5], ps_digest[6], ps_digest[7], ps_digest[8], ps_digest[9], ps_digest[10],
+ ps_digest[11], ps_digest[12], ps_digest[13], ps_digest[14], ps_digest[15], ps_digest[16], ps_digest[17], ps_digest[18], ps_digest[19]);
+
+ // get pieces from results -- should be just the signature
+ buf = locals->security_result().buf();
+ len = locals->security_result().len();
+
+ log_debug_p(log, "Ciphersuite_PI2::validate() security result, len = %zu", len);
+ while ( len > 0 ) {
+ u_char item_type = *buf++;
+ --len;
+ sdnv_len = SDNV::decode(buf, len, &field_length);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ switch ( item_type ) {
+ case CS_signature_field:
+ {
+ log_debug_p(log, "Ciphersuite_PI2::validate() CS_signature_field item, len %llu", U64FMT(field_length));
+
+ err = KeySteward::verify(bundle, buf, field_length, ps_digest, digest_len);
+ if ( err == 0 ) {
+ log_debug_p(log, "Ciphersuite_PI2::validate() -- KeySteward::verify() returned %d", err);
+ locals->set_proc_flag(CS_BLOCK_PASSED_VALIDATION | CS_BLOCK_COMPLETED_DO_NOT_FORWARD);
+ } else {
+ log_err_p(log, "Ciphersuite_PI2::validate: CS_signature_field validation failed");
+ goto fail;
+ }
+
+ }
+ break;
+
+ default: // deal with improper items
+ log_err_p(log, "Ciphersuite_PI2::validate: unexpected item type %d in security_result", item_type);
+ goto fail;
+ }
+ buf += field_length;
+ len -= field_length;
+ }
+ } else
+ locals->set_proc_flag(CS_BLOCK_DID_NOT_FAIL); // not for here so we didn't check this block
+
+ log_debug_p(log, "Ciphersuite_PI2::validate() done");
+
+ return true;
+
+
+
+ fail:
+ locals->set_proc_flag(CS_BLOCK_FAILED_VALIDATION | CS_BLOCK_COMPLETED_DO_NOT_FORWARD);
+ *deletion_reason = BundleProtocol::REASON_SECURITY_FAILED;
+ return false;
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PI2::prepare(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ const BlockInfo* source,
+ const LinkRef& link,
+ list_owner_t list)
+{
+ (void)bundle;
+ (void)link;
+
+ int result = BP_FAIL;
+ u_int16_t cs_flags = 0;
+ BP_Local_CS* locals = NULL;
+ BP_Local_CS* source_locals = NULL;
+ EndpointID local_eid = BundleDaemon::instance()->local_eid();
+ BundleDaemon* bd = BundleDaemon::instance();
+
+//XXXpl - fix this test
+ if ( (source != NULL) &&
+ (dynamic_cast<BP_Local_CS*>(source->locals())->security_dest() == bd->local_eid().data()) ) {
+ log_debug_p(log, "Ciphersuite_PI2::prepare() - not being forwarded");
+ return BP_SUCCESS; //it was for us so don't forward
+ }
+
+ BlockInfo bi = BlockInfo(BundleProtocol::find_processor(BundleProtocol::PAYLOAD_SECURITY_BLOCK), source); // NULL source is OK here
+
+ // If this is a received block then there's not a lot to do yet.
+ // We copy some parameters - the main work is done in generate().
+ // Insertion is at the end of the list, which means that
+ // it will be in the same position as received
+ if ( list == BlockInfo::LIST_RECEIVED ) {
+
+ if ( Ciphersuite::destination_is_local_node(bundle, source) )
+ return BP_SUCCESS; //don't forward if it's for here
+
+ CS_FAIL_IF_NULL(source);
+ xmit_blocks->push_back(bi);
+ BlockInfo* bp = &(xmit_blocks->back());
+ CS_FAIL_IF_NULL(bp);
+ bp->set_eid_list(source->eid_list());
+ log_debug_p(log, "Ciphersuite_PI2::prepare() - forward received block len %u eid_list_count %zu new count %zu",
+ source->full_length(), source->eid_list().size(), bp->eid_list().size());
+
+ CS_FAIL_IF_NULL( source->locals() ); // broken
+
+ source_locals = dynamic_cast<BP_Local_CS*>(source->locals());
+ CS_FAIL_IF_NULL(source_locals); // also broken
+ bp->set_locals(new BP_Local_CS);
+ locals = dynamic_cast<BP_Local_CS*>(bp->locals());
+ CS_FAIL_IF_NULL(locals);
+ locals->set_owner_cs_num(CSNUM_PI2);
+ cs_flags = source_locals->cs_flags();
+ locals->set_list_owner(BlockInfo::LIST_RECEIVED);
+ locals->set_correlator(source_locals->correlator());
+ bp->writable_contents()->reserve(source->full_length());
+ bp->writable_contents()->set_len(0);
+
+ // copy security-src and -dest if they exist
+ if ( source_locals->cs_flags() & CS_BLOCK_HAS_SOURCE ) {
+ CS_FAIL_IF(source_locals->security_src().length() == 0 );
+ log_debug_p(log, "Ciphersuite_PI2::prepare() add security_src EID");
+ cs_flags |= CS_BLOCK_HAS_SOURCE;
+ locals->set_security_src(source_locals->security_src());
+ }
+
+ if ( source_locals->cs_flags() & CS_BLOCK_HAS_DEST ) {
+ CS_FAIL_IF(source_locals->security_dest().length() == 0 );
+ log_debug_p(log, "Ciphersuite_PI2::prepare() add security_dest EID");
+ cs_flags |= CS_BLOCK_HAS_DEST;
+ locals->set_security_dest(source_locals->security_dest());
+ }
+ locals->set_cs_flags(cs_flags);
+ log_debug_p(log, "Ciphersuite_PI2::prepare() - inserted block eid_list_count %zu",
+ bp->eid_list().size());
+ result = BP_SUCCESS;
+ return result;
+ } else {
+
+ // initialize the block
+ log_debug_p(log, "Ciphersuite_PI2::prepare() - add new block (or API block etc)");
+ bi.set_locals(new BP_Local_CS);
+ CS_FAIL_IF_NULL(bi.locals());
+ locals = dynamic_cast<BP_Local_CS*>(bi.locals());
+ CS_FAIL_IF_NULL(locals);
+ locals->set_owner_cs_num(CSNUM_PI2);
+ locals->set_list_owner(list);
+
+ // if there is a security-src and/or -dest, use it -- might be specified by API
+ if ( source != NULL && source->locals() != NULL) {
+ locals->set_security_src(dynamic_cast<BP_Local_CS*>(source->locals())->security_src());
+ locals->set_security_dest(dynamic_cast<BP_Local_CS*>(source->locals())->security_dest());
+ }
+
+ log_debug_p(log, "Ciphersuite_PI2::prepare() local_eid %s bundle->source_ %s", local_eid.c_str(), bundle->source().c_str());
+ // if not, and we didn't create the bundle, specify ourselves as sec-src
+ if ( (locals->security_src().length() == 0) && (local_eid != bundle->source()))
+ locals->set_security_src(local_eid.str());
+
+ // if we now have one, add it to list, etc
+ if ( locals->security_src().length() > 0 ) {
+ log_debug_p(log, "Ciphersuite_PI2::prepare() add security_src EID %s", locals->security_src().c_str());
+ cs_flags |= CS_BLOCK_HAS_SOURCE;
+ bi.add_eid(locals->security_src());
+ }
+
+ if ( locals->security_dest().length() > 0 ) {
+ log_debug_p(log, "Ciphersuite_PI2::prepare() add security_dest EID %s", locals->security_dest().c_str());
+ cs_flags |= CS_BLOCK_HAS_DEST;
+ bi.add_eid(locals->security_dest());
+ }
+
+ locals->set_cs_flags(cs_flags);
+
+ // We should already have the primary block in the list.
+ // We'll insert this after the primary and any BA blocks
+ // and before everything else
+ if ( xmit_blocks->size() > 0 ) {
+ BlockInfoVec::iterator iter = xmit_blocks->begin();
+
+ while ( iter != xmit_blocks->end()) {
+ switch (iter->type()) {
+ case BundleProtocol::PRIMARY_BLOCK:
+ case BundleProtocol::BUNDLE_AUTHENTICATION_BLOCK:
+ ++iter;
+ continue;
+
+ default:
+ break;
+ }
+ xmit_blocks->insert(iter, bi);
+ break;
+ }
+ } else {
+ // it's weird if there are no other blocks but, oh well ...
+ xmit_blocks->push_back(bi);
+ }
+ }
+
+ result = BP_SUCCESS;
+ return result;
+
+ fail:
+ if ( locals != NULL )
+ locals->set_proc_flag(CS_BLOCK_PROCESSING_FAILED_DO_NOT_SEND);
+ return BP_FAIL;
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PI2::generate(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ BlockInfo* block,
+ const LinkRef& link,
+ bool last)
+{
+ (void)bundle;
+ (void)link;
+ (void)xmit_blocks;
+
+ int result = BP_FAIL;
+ size_t sig_len = 0;
+ size_t res_len = 0;
+ size_t length = 0;
+ size_t param_len = 0;
+ u_char fragment_item[24]; // 24 is enough for 2 max-sized SDNVs and type and length
+ u_int16_t cs_flags = 0;
+ BP_Local_CS* locals = dynamic_cast<BP_Local_CS*>(block->locals());
+ u_char* ptr;
+ size_t temp;
+ size_t rem;
+ DataBuffer encrypted_key;
+ EVP_MD_CTX ctx;
+ size_t digest_len;
+ u_char* buf = NULL;
+
+ int sdnv_len = 0; // use an int to handle -1 return values
+ int err = 0;
+ int len = 0;
+ BlockInfo::DataBuffer* contents = NULL;
+ LocalBuffer* params = NULL;
+
+ log_debug_p(log, "Ciphersuite_PI2::generate() %p", block);
+ CS_FAIL_IF_NULL(locals);
+ cs_flags = locals->cs_flags(); // get flags from prepare()
+ // if this is a received block then it's easy
+ if ( locals->list_owner() == BlockInfo::LIST_RECEIVED )
+ {
+ // generate the preamble and copy the data.
+ size_t length = block->source()->data_length();
+
+ generate_preamble(xmit_blocks,
+ block,
+ BundleProtocol::PAYLOAD_SECURITY_BLOCK,
+ BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR |
+ (last ? BundleProtocol::BLOCK_FLAG_LAST_BLOCK : 0),
+ length);
+
+ BlockInfo::DataBuffer* contents = block->writable_contents();
+ contents->reserve(block->data_offset() + length);
+ contents->set_len(block->data_offset() + length);
+ memcpy(contents->buf() + block->data_offset(),
+ block->source()->data(), length);
+ log_debug_p(log, "Ciphersuite_PI2::generate() %p done", block);
+ return BP_SUCCESS;
+ } /************** forwarding done **************/
+
+
+ /* params field will contain
+ - fragment offset and length, if a fragment-bundle, plus type and length
+ */
+
+ params = locals->writable_security_params();
+
+ param_len = 0;
+
+ if ( bundle->is_fragment() ) {
+ log_debug_p(log, "Ciphersuite_PI2::generate() bundle is fragment");
+ ptr = &fragment_item[2];
+ rem = sizeof(fragment_item) - 2;
+ temp = SDNV::encode(bundle->frag_offset(), ptr, rem);
+ ptr += temp;
+ rem -= temp;
+ temp += SDNV::encode(bundle->payload().length(), ptr, rem);
+ fragment_item[0] = CS_fragment_offset_and_length_field;
+ fragment_item[1] = temp; //guaranteed to fit as a "one-byte SDNV"
+ param_len += 2 + temp;
+
+ }
+
+ if ( param_len > 0 ) {
+ cs_flags |= CS_BLOCK_HAS_PARAMS;
+ params->reserve(param_len);
+ params->set_len(param_len);
+ log_debug_p(log, "Ciphersuite_PI2::generate() security params, len = %zu", param_len);
+
+ ptr = params->buf();
+
+ if ( bundle->is_fragment() )
+ memcpy(ptr, fragment_item, 2 + temp);
+ }
+
+ // need to calculate the size of the security-result items,
+ // and the total length of the combined field
+
+ /* result field will contain
+ - signed hash, plus type and length
+ */
+ EVP_MD_CTX_init(&ctx);
+ err = EVP_DigestInit_ex(&ctx, EVP_sha1(), NULL);
+ CS_FAIL_IF(err == 0);
+ digest_len = EVP_MD_CTX_size(&ctx);
+ EVP_MD_CTX_cleanup(&ctx);
+
+ KeySteward::signature_length(bundle, NULL, link, digest_len, sig_len);
+
+ res_len = 1 + SDNV::encoding_len(sig_len) + sig_len;
+
+ // First we need to work out the lengths and create the preamble
+ cs_flags |= CS_BLOCK_HAS_RESULT;
+ locals->set_cs_flags(cs_flags);
+ length = 0;
+ length += SDNV::encoding_len(CSNUM_PI2);
+ length += SDNV::encoding_len(locals->cs_flags());
+
+ param_len = locals->security_params().len();
+ length += SDNV::encoding_len(param_len) + param_len;
+ locals->set_security_result_offset(length); //remember this for finalize()
+ length += SDNV::encoding_len(res_len) + res_len;
+
+ contents = block->writable_contents();
+
+ generate_preamble(xmit_blocks,
+ block,
+ BundleProtocol::PAYLOAD_SECURITY_BLOCK,
+ BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR |
+ (last ? BundleProtocol::BLOCK_FLAG_LAST_BLOCK : 0),
+ length);
+
+
+ log_debug_p(log, "Ciphersuite_PI2::generate() preamble len %u block len %zu", block->data_offset(), length);
+ contents->reserve(block->data_offset() + length);
+ contents->set_len(block->data_offset() + length);
+ buf = block->writable_contents()->buf() + block->data_offset();
+ len = length;
+
+ // Assemble data into block contents.
+
+ // ciphersuite number and flags
+ sdnv_len = SDNV::encode(locals->owner_cs_num(), buf, len);
+ CS_FAIL_IF(sdnv_len <= 0);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ sdnv_len = SDNV::encode(locals->cs_flags(), buf, len);
+ CS_FAIL_IF(sdnv_len <= 0);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ if ( param_len > 0 ) {
+ // length of params
+ sdnv_len = SDNV::encode(param_len, buf, len);
+ CS_FAIL_IF(sdnv_len <= 0);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ // params data
+ memcpy(buf, locals->security_params().buf(), param_len );
+ buf += param_len;
+ len -= param_len;
+ }
+
+ // length of result -- we have to put this in now
+ sdnv_len = SDNV::encode(res_len, buf, len);
+
+
+ // no, no ! Not yet !!
+ // ASSERT( len == 0 );
+ log_debug_p(log, "Ciphersuite_PI2::generate() done");
+
+
+ result = BP_SUCCESS;
+ return result;
+
+ fail:
+ if ( locals != NULL )
+ locals->set_proc_flag(CS_BLOCK_PROCESSING_FAILED_DO_NOT_SEND);
+ return BP_FAIL;
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PI2::finalize(const Bundle* bundle,
+ BlockInfoVec* xmit_blocks,
+ BlockInfo* block,
+ const LinkRef& link)
+{
+ (void)link;
+ int result = BP_FAIL;
+ size_t len;
+ size_t sdnv_len;
+ size_t res_len;
+ u_char* buf;
+ BP_Local_CS* locals = NULL;
+ std::vector<u_int64_t> correlator_list;
+ std::vector<u_int64_t>::iterator cl_iter;
+ EndpointID local_eid = BundleDaemon::instance()->local_eid();
+ BlockInfoVec::iterator iter;
+ DataBuffer db_digest;
+ DataBuffer db_signed;
+ int err = 0;
+ BlockInfo::DataBuffer* contents = NULL;
+ LocalBuffer* digest_result = NULL;
+ size_t sig_len = 0;
+
+ log_debug_p(log, "Ciphersuite_PI2::finalize()");
+ locals = dynamic_cast<BP_Local_CS*>(block->locals());
+ CS_FAIL_IF_NULL(locals);
+
+ // if this is a received block then we're done
+ if ( locals->list_owner() == BlockInfo::LIST_RECEIVED )
+ return BP_SUCCESS;
+
+ create_digest(bundle, xmit_blocks, block, db_digest);
+
+ err = KeySteward::sign(bundle, NULL, link, db_digest, db_signed);
+ CS_FAIL_IF(err != 0);
+ sig_len = db_signed.len();
+ res_len = 1 + SDNV::encoding_len(sig_len) + sig_len;
+
+ // build the result item
+ digest_result = locals->writable_security_result();
+ digest_result->reserve(res_len);
+ digest_result->set_len(res_len);
+
+ buf = digest_result->buf();
+ len = digest_result->len();
+
+ *buf++ = Ciphersuite::CS_signature_field; // item type
+ len--;
+
+ sdnv_len = SDNV::encode(sig_len, buf, len);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ memcpy(buf, db_signed.buf(), sig_len);
+
+
+ // now put the result item into the block contents
+ contents = block->writable_contents();
+ buf = contents->buf();
+ len = contents->len();
+ buf += block->data_offset(); // we need to add data_offset as well,
+ len -= block->data_offset(); // since we're pointing at the whole buffer
+
+ buf += locals->security_result_offset(); //and this offset is just within
+ len -= locals->security_result_offset(); //the data portion of the buffer
+ sdnv_len = SDNV::len(buf); // size of result-length field
+ buf += sdnv_len; // step over that length field
+ len -= sdnv_len;
+ memcpy(buf, digest_result->buf(), digest_result->len());
+ log_debug_p(log, "Ciphersuite_PI2::finalize() done");
+
+ result = BP_SUCCESS;
+ return result;
+
+ fail:
+ if ( locals != NULL )
+ locals->set_proc_flag(CS_BLOCK_PROCESSING_FAILED_DO_NOT_SEND);
+ return BP_FAIL;
+}
+
+//----------------------------------------------------------------------
+void
+Ciphersuite_PI2::digest(const Bundle* bundle,
+ const BlockInfo* caller_block,
+ const BlockInfo* target_block,
+ const void* buf,
+ size_t len,
+ OpaqueContext* r)
+{
+ (void)bundle;
+ (void)caller_block;
+ (void)target_block;
+ log_debug_p(log, "Ciphersuite_PI2::digest() %zu bytes", len);
+
+ EVP_MD_CTX* pctx = reinterpret_cast<EVP_MD_CTX*>(r);
+
+ EVP_DigestUpdate( pctx, buf, len );
+}
+
+//----------------------------------------------------------------------
+void
+Ciphersuite_PI2::create_digest(const Bundle* bundle,
+ BlockInfoVec* block_list,
+ BlockInfo* block,
+ DataBuffer& db)
+{
+ size_t len;
+ size_t sdnv_len;
+ EVP_MD_CTX ctx;
+ OpaqueContext* r = reinterpret_cast<OpaqueContext*>(&ctx);
+ char* dict;
+ u_int32_t offset;
+ u_char* buf;
+ const char* ptr;
+ size_t plen;
+ size_t digest_len;
+ u_char ps_digest[EVP_MAX_MD_SIZE];
+ u_int32_t rlen = 0;
+ u_int32_t header_len;
+ u_char c;
+ u_int64_t eid_ref_count = 0LLU;
+ BP_Local_CS* locals = NULL;
+ BP_Local_CS* target_locals = NULL;
+ u_int64_t target_flags;
+ u_int64_t flags_save;
+ u_int64_t mask = 0LLU; /// specify mask for flags
+ u_int64_t mask_primary = 0LLU; /// mask for primary-block flags
+ u_int64_t target_content_length;
+ u_int64_t correlator;
+ u_int64_t cs_flags;
+ u_int64_t suite_num;
+ std::vector<u_int64_t> correlator_list;
+ std::vector<u_int64_t>::iterator cl_iter;
+ EndpointID local_eid = BundleDaemon::instance()->local_eid();
+ BlockInfoVec::iterator iter;
+ int err = 0;
+ PrimaryBlock_ex primary;
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest()");
+ locals = dynamic_cast<BP_Local_CS*>(block->locals());
+
+ // prepare context
+ EVP_MD_CTX_init(&ctx);
+ err = EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL);
+ digest_len = EVP_MD_CTX_size(&ctx);
+ // XXX-pl check error -- zero is failure
+
+ // Walk the list and process each of the blocks.
+ // We only digest PS, C3 and the payload data,
+ // all others are ignored
+
+ // Note that we can only process PSBs and C3s that follow this block
+ // as doing otherwise would mean that there would be a
+ // correlator block preceding its parent
+
+ // There can also be tunnelling issues, depending upon the
+ // exact sequencing of blocks. It seems best to add C blocks
+ // as early as possible in order to mitigate this problem.
+ // That has its own drawbacks unfortunately
+
+ header_len = 1 //version
+ + 8 //flags SDNV
+ + 4 //header length itself
+ + 4 //destination eid length
+ + 4 //source eid length
+ + 4 //report-to eid length
+ + 8 //creation SDNV #1
+ + 8 //creation SDNV #2
+ + 8; //lifetime SDNV
+
+ if ( bundle->is_fragment() )
+ header_len += 8 //fragment offset SDNV
+ + 8; //total-length SDNV
+
+ // do stuff for primary, and ignore it during the walk
+
+ iter = block_list->begin(); //primary
+
+ err = read_primary(bundle, &*iter, primary, &dict);
+
+ header_len += strlen(dict + primary.dest_scheme_offset);
+ header_len += strlen(dict + primary.dest_ssp_offset);
+ header_len += strlen(dict + primary.source_scheme_offset);
+ header_len += strlen(dict + primary.source_ssp_offset);
+ header_len += strlen(dict + primary.replyto_scheme_offset);
+ header_len += strlen(dict + primary.replyto_ssp_offset);
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() header_len %u", header_len);
+
+
+ // Now start the actual digest process
+ digest( bundle, block, &*iter, &primary.version, 1, r); //version
+
+ primary.processing_flags &= mask_primary;
+ target_flags = htonq(primary.processing_flags);
+ digest( bundle, block, &*iter, &primary.processing_flags, sizeof(primary.processing_flags), r);
+
+ header_len = htonl(header_len);
+ digest( bundle, block, &*iter, &header_len, sizeof(header_len), r);
+
+
+ offset = strlen(dict + primary.dest_scheme_offset) + strlen(dict + primary.dest_ssp_offset); // Note:- "offset" is 4 bytes, not 8
+ offset = htonl(offset);
+ digest( bundle, block, &*iter, &offset, sizeof(offset), r);
+ digest( bundle, block, &*iter, dict + primary.dest_scheme_offset, strlen(dict + primary.dest_scheme_offset), r);
+ digest( bundle, block, &*iter, dict + primary.dest_ssp_offset, strlen(dict + primary.dest_ssp_offset), r);
+
+ offset = strlen(dict + primary.source_scheme_offset) + strlen(dict + primary.source_ssp_offset);
+ offset = htonl(offset);
+ digest( bundle, block, &*iter, &offset, sizeof(offset), r);
+ digest( bundle, block, &*iter, dict + primary.source_scheme_offset, strlen(dict + primary.source_scheme_offset), r);
+ digest( bundle, block, &*iter, dict + primary.source_ssp_offset, strlen(dict + primary.source_ssp_offset), r);
+
+ offset = strlen(dict + primary.replyto_scheme_offset) + strlen(dict + primary.replyto_ssp_offset);
+ offset = htonl(offset);
+ digest( bundle, block, &*iter, &offset, sizeof(offset), r);
+ digest( bundle, block, &*iter, dict + primary.replyto_scheme_offset, strlen(dict + primary.replyto_scheme_offset), r);
+ digest( bundle, block, &*iter, dict + primary.replyto_ssp_offset, strlen(dict + primary.replyto_ssp_offset), r);
+
+ // two SDNVs for creation timestamp, one for lifetime
+ primary.creation_time = htonq(primary.creation_time);
+ digest( bundle, block, &*iter, &primary.creation_time, sizeof(primary.creation_time), r);
+ primary.creation_sequence = htonq(primary.creation_sequence);
+ digest( bundle, block, &*iter, &primary.creation_sequence, sizeof(primary.creation_sequence), r);
+ primary.lifetime = htonq(primary.lifetime);
+ digest( bundle, block, &*iter, &primary.lifetime, sizeof(primary.lifetime), r);
+
+ if ( bundle->is_fragment() ) {
+ primary.fragment_offset = htonq(primary.fragment_offset);
+ digest( bundle, block, &*iter, &primary.fragment_offset, sizeof(primary.fragment_offset), r);
+ primary.original_length = htonq(primary.original_length);
+ digest( bundle, block, &*iter, &primary.original_length, sizeof(primary.original_length), r);
+ }
+
+ ++iter; //primary is done now
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() walk block list");
+ for ( ;
+ iter != block_list->end();
+ ++iter)
+ {
+ // Advance the iterator to our current position.
+ // While we do it, we also remember the correlator values
+ // of any PSBs or C3 blocks we encounter.
+ // We do this to avoid processing any related correlated blocks
+ // Note that we include the current block in the test below
+ // in order to prevent encapsulating it !!
+ target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+ if ( (&*iter) <= block ) {
+ if ( iter->type() == BundleProtocol::PAYLOAD_SECURITY_BLOCK ||
+ (iter->type() == BundleProtocol::CONFIDENTIALITY_BLOCK &&
+ target_locals->owner_cs_num() == Ciphersuite_PC3::CSNUM_PC3 ) ) {
+ if ( target_locals->cs_flags() & CS_BLOCK_HAS_CORRELATOR) {
+ //add correlator to exclude-list
+ correlator_list.push_back(target_locals->correlator());
+ }
+ }
+ continue;
+ }
+
+
+ switch ( iter->type() ) {
+ case BundleProtocol::PAYLOAD_SECURITY_BLOCK:
+ case BundleProtocol::CONFIDENTIALITY_BLOCK:
+ {
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() PS or C block type %d cs_num %d",
+ iter->type(), target_locals->owner_cs_num());
+ if ( iter->type() == BundleProtocol::PAYLOAD_SECURITY_BLOCK &&
+ target_locals->owner_cs_num() != Ciphersuite_PC3::CSNUM_PC3 )
+ continue; // only digest C3
+
+
+ // see if there's a correlator and, if there is,
+ // if this is a secondary block. Only process a secondary
+ // if we also did its primary
+ bool skip_target = false;
+ target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() target_locals->cs_flags 0x%hx", target_locals->cs_flags());
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() target_locals->correlator() 0x%llx", U64FMT(target_locals->correlator()));
+ if ( target_locals->cs_flags() & CS_BLOCK_HAS_CORRELATOR) {
+ correlator = target_locals->correlator();
+ for ( cl_iter = correlator_list.begin();
+ cl_iter < correlator_list.end();
+ ++cl_iter) {
+ if ( correlator == *cl_iter) {
+ skip_target = true;
+ break; //break from for-loop
+ }
+ }
+ if ( skip_target )
+ break; //break from switch, continue for "for" loop
+
+ }
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() digest this block, len %u eid_list().size() %zu",
+ iter->full_length(), iter->eid_list().size());
+ // Either it has no correlator, or it wasn't in the list.
+ // So we will process it in the digest
+
+ /********** start preamble processing **********/
+ buf = iter->contents().buf();
+ len = iter->full_length();
+
+
+ // Process block type
+ c = *buf++;
+ len--;
+ digest( bundle, block, &*iter, &c, 1, r);
+
+ // Process flags
+ sdnv_len = SDNV::decode( buf, len, &target_flags);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ flags_save = target_flags;
+ target_flags &= mask;
+ target_flags = htonq(target_flags);
+ digest( bundle, block, &*iter, &target_flags, sizeof(target_flags), r);
+
+ // EID list is next, starting with the count although we don't digest it
+ if ( flags_save & BundleProtocol::BLOCK_FLAG_EID_REFS ) {
+ sdnv_len = SDNV::decode(buf, len, &eid_ref_count);
+ buf += sdnv_len;
+ len -= sdnv_len;
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() eid_ref_count %llu", U64FMT(eid_ref_count));
+
+ // each ref is a pair of SDNVs, so process 2 * eid_ref_count text pieces
+ if ( eid_ref_count > 0 ) {
+ for ( u_int32_t i = 0; i < (2 * eid_ref_count); i++ ) {
+ sdnv_len = SDNV::decode(buf, len, &offset);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ ptr = dict + offset; //point at item in dictionary
+ plen = strlen(ptr); // length *without* NULL-terminator
+ digest( bundle, block, &*iter, ptr, plen, r);
+ }
+ }
+ }
+
+ // Process data length
+ sdnv_len = SDNV::decode(buf, len, &target_content_length);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ target_content_length = htonq(target_content_length);
+ digest( bundle, block, &*iter, &target_content_length, sizeof(target_content_length), r);
+
+ // start of data is where to start main digest
+ offset = buf - iter->contents().buf();
+ ASSERT(offset == iter->data_offset());
+ /********** end of preamble processing **********/
+
+
+ /********** start content processing **********/
+
+ // if it's the current block, we have to exclude security-result data.
+ // Note that security-result-length *is* included
+ if ( (&*iter) == block ) {
+
+ // ciphersuite number and flags
+ sdnv_len = SDNV::decode(buf,
+ len,
+ &suite_num);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ sdnv_len = SDNV::decode(buf,
+ len,
+ &cs_flags);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ if ( cs_flags & CS_BLOCK_HAS_RESULT ) {
+ // if there's a security-result we have to ease up to it
+ if ( cs_flags & CS_BLOCK_HAS_CORRELATOR )
+ buf += SDNV::len(buf); //step over correlator
+
+ if ( cs_flags & CS_BLOCK_HAS_PARAMS )
+ buf += SDNV::len(buf); //step over params
+
+ if ( cs_flags & CS_BLOCK_HAS_RESULT ) {
+ sdnv_len = SDNV::decode(buf, len, &target_content_length);
+ buf += sdnv_len;
+ len -= sdnv_len;
+ buf += SDNV::len(buf); //step over security-result-length field
+ }
+
+ len = buf - iter->contents().buf(); //this is the length to use
+ }
+ // now set buf back to the start of the content
+ buf = iter->contents().buf();
+ }
+
+ iter->owner()->process( Ciphersuite_PI2::digest,
+ bundle,
+ block,
+ &*iter,
+ offset,
+ len,
+ r);
+ /********** end of content processing **********/
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() digest done %p", &*iter);
+
+ }
+ break; //break from switch, continue for "for" loop
+
+ case BundleProtocol::PAYLOAD_BLOCK:
+ {
+
+ /********** start preamble processing **********/
+ buf = iter->contents().buf();
+ len = iter->full_length();
+
+
+ // Process block type
+ c = *buf++;
+ len--;
+ digest( bundle, block, &*iter, &c, 1, r);
+
+ // Process flags
+ sdnv_len = SDNV::decode( buf, len, &target_flags);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ flags_save = target_flags;
+ target_flags &= mask;
+ target_flags = htonq(target_flags);
+ digest( bundle, block, &*iter, &target_flags, sizeof(target_flags), r);
+
+ // EID list is next, starting with the count although we don't digest it
+ if ( flags_save & BundleProtocol::BLOCK_FLAG_EID_REFS ) {
+ sdnv_len = SDNV::decode(buf, len, &eid_ref_count);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ // each ref is a pair of SDNVs, so process 2 * eid_ref_count text pieces
+ if ( eid_ref_count > 0 ) {
+ for ( u_int32_t i = 0; i < (2 * eid_ref_count); i++ ) {
+ sdnv_len = SDNV::decode(buf, len, &offset);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ ptr = dict + offset; //point at item in dictionary
+ plen = strlen(ptr); // length *without* NULL-terminator
+ digest( bundle, block, &*iter, ptr, plen, r);
+ }
+ }
+ }
+
+ // Process data length
+ sdnv_len = SDNV::decode(buf, len, &target_content_length);
+ buf += sdnv_len;
+ len -= sdnv_len;
+
+ target_content_length = htonq(target_content_length);
+ digest( bundle, block, &*iter, &target_content_length, sizeof(target_content_length), r);
+
+ // start of data is where to start main digest
+ offset = buf - iter->contents().buf();
+ ASSERT(offset == iter->data_offset());
+ /********** end of preamble processing **********/
+
+ /********** start content processing **********/
+
+ iter->owner()->process( Ciphersuite_PI2::digest,
+ bundle,
+ block,
+ &*iter,
+ offset,
+ len,
+ r);
+ /********** end of content processing **********/
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() PAYLOAD_BLOCK done");
+ }
+ break; //break from switch, continue for "for" loop
+
+ default:
+ continue;
+
+ } // end of switch
+ } // end of loop-through-all-the-blocks
+
+
+ err = EVP_DigestFinal_ex(&ctx, ps_digest, &rlen);
+ // XXX-pl check error -- zero is failure
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() digest 0x%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx%2.2hhx",
+ ps_digest[0], ps_digest[1], ps_digest[2], ps_digest[3], ps_digest[4], ps_digest[5], ps_digest[6], ps_digest[7], ps_digest[8], ps_digest[9], ps_digest[10],
+ ps_digest[11], ps_digest[12], ps_digest[13], ps_digest[14], ps_digest[15], ps_digest[16], ps_digest[17], ps_digest[18], ps_digest[19]);
+
+ EVP_MD_CTX_cleanup(&ctx);
+
+ db.reserve(digest_len);
+ db.set_len(digest_len);
+ memcpy(db.buf(), ps_digest, digest_len);
+
+ log_debug_p(log, "Ciphersuite_PI2::create_digest() done");
+
+}
+
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PI2::read_primary(const Bundle* bundle,
+ BlockInfo* block,
+ PrimaryBlock_ex& primary,
+ char** dict)
+{
+ u_char* buf;
+ size_t len;
+
+ size_t primary_len = block->full_length();
+
+ buf = block->writable_contents()->buf();
+ len = block->writable_contents()->len();
+
+ ASSERT(primary_len == len);
+
+ primary.version = *(u_int8_t*)buf;
+ buf += 1;
+ len -= 1;
+
+ if (primary.version != BundleProtocol::CURRENT_VERSION) {
+ log_warn_p(log, "protocol version mismatch %d != %d",
+ primary.version, BundleProtocol::CURRENT_VERSION);
+ return -1;
+ }
+
+#define PBP_READ_SDNV(location) { \
+ int sdnv_len = SDNV::decode(buf, len, location); \
+ if (sdnv_len < 0) \
+ goto tooshort; \
+ buf += sdnv_len; \
+ len -= sdnv_len; }
+
+ // Grab the SDNVs representing the flags and the block length.
+ PBP_READ_SDNV(&primary.processing_flags);
+ PBP_READ_SDNV(&primary.block_length);
+
+ log_debug_p(log, "parsed primary block: version %d length %u",
+ primary.version, block->data_length());
+
+/*
+ * it may be that the ASSERT which follows is not appropriate because we're doing this
+ * on the outbound side and it seems that data_length() is the same as full_length().
+ * But what's remaining should be the same as what is promised.
+ log_debug_p(log, "parsed primary block: version %d length %u full_length %u len remaining %zu",
+ primary.version, block->data_length(), block->full_length(), len);
+ // What remains in the buffer should now be equal to what the block-length
+ // field advertised.
+ ASSERT(len == block->data_length());
+*/
+ ASSERT(len == primary.block_length);
+
+ // Read the various SDNVs up to the start of the dictionary.
+ PBP_READ_SDNV(&primary.dest_scheme_offset);
+ PBP_READ_SDNV(&primary.dest_ssp_offset);
+ PBP_READ_SDNV(&primary.source_scheme_offset);
+ PBP_READ_SDNV(&primary.source_ssp_offset);
+ PBP_READ_SDNV(&primary.replyto_scheme_offset);
+ PBP_READ_SDNV(&primary.replyto_ssp_offset);
+ PBP_READ_SDNV(&primary.custodian_scheme_offset);
+ PBP_READ_SDNV(&primary.custodian_ssp_offset);
+ PBP_READ_SDNV(&primary.creation_time);
+ PBP_READ_SDNV(&primary.creation_sequence);
+ PBP_READ_SDNV(&primary.lifetime);
+ PBP_READ_SDNV(&primary.dictionary_length);
+ *dict = reinterpret_cast<char*>(buf);
+ if (bundle->is_fragment()) {
+ PBP_READ_SDNV(&primary.fragment_offset);
+ PBP_READ_SDNV(&primary.original_length);
+ }
+#undef PBP_READ_SDNV
+ return 0;
+
+ tooshort:
+ return -1;
+}
+
+
+} // namespace dtn
+
+#endif /* BSP_ENABLED */