servlib/security/Ciphersuite_PI2.cc
changeset 0 2b3e5ec03512
--- /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 */