servlib/security/Ciphersuite_PC3.cc
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/security/Ciphersuite_PC3.cc	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,1470 @@
+/*
+ *    Copyright 2006-7 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
+
+#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 "openssl/rand.h"
+#include "gcm/gcm.h"
+#include "security/KeySteward.h"
+
+namespace dtn {
+
+static const char * log = "/dtn/bundle/ciphersuite";
+
+//----------------------------------------------------------------------
+Ciphersuite_PC3::Ciphersuite_PC3()
+{
+}
+
+//----------------------------------------------------------------------
+u_int16_t
+Ciphersuite_PC3::cs_num(void)
+{
+    return CSNUM_PC3;
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PC3::consume(Bundle* bundle, BlockInfo* block,
+                        u_char* buf, size_t len)
+{
+    log_debug_p(log, "Ciphersuite_PC3::consume()");
+    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_PC3::validate(const Bundle*           bundle,
+                         BlockInfoVec*           block_list,
+                         BlockInfo*              block,
+                         status_report_reason_t* reception_reason,
+                         status_report_reason_t* deletion_reason)
+{
+    (void)reception_reason;
+
+
+//1. do we have security-dest? If yes, get it, otherwise get bundle-dest
+//2. does it match local_eid ??
+//3. if not, return true
+//4. if it does match, parse and validate the block
+//5. the actions must exactly reverse the transforming changes made in finalize()
+
+    Bundle*         deliberate_const_cast_bundle = const_cast<Bundle*>(bundle);
+    u_int16_t       cs_flags;
+    BP_Local_CS*    locals = dynamic_cast<BP_Local_CS*>(block->locals());
+    EndpointID      local_eid = BundleDaemon::instance()->local_eid();
+    size_t          offset;
+    size_t          len;
+    gcm_ctx_ex      ctx_ex;    // includes OpenSSL context within it
+    OpaqueContext*  r = reinterpret_cast<OpaqueContext*>(&ctx_ex);
+    bool            changed = false;
+    u_char          key[key_len];  //use AES128 16-byte key
+    u_char          salt[salt_len];       // salt for GCM
+    u_char          iv[iv_len];    // GCM "iv" length is 8 bytes
+    u_char          target_iv[iv_len];    // GCM "iv" length is 8 bytes
+    u_char          nonce[nonce_len];    // 12 bytes recommended
+    u_char          tag[tag_len];    // 128 bits recommended
+    u_char          tag_encap[tag_len];    // tag for an encapsulated block
+    u_char*         buf;
+    u_char*         ptr;
+    u_char*         data;
+    BP_Local_CS*    target_locals = NULL;
+    int             sdnv_len = 0;       // use an int to handle -1 return values
+    u_char          item_type;
+    int32_t         rem;                // use signed value
+    u_int64_t       field_length = 0LL;
+    u_int64_t       frag_offset_;   // Offset of fragment in the original bundle
+    u_int64_t       orig_length_;   // Length of original bundle
+    ret_type        ret = 0;
+    DataBuffer      db;
+     
+    log_debug_p(log, "Ciphersuite_PC3::validate() %p", block);
+    CS_FAIL_IF_NULL(locals);
+    cs_flags = locals->cs_flags();
+    
+    if ( Ciphersuite::destination_is_local_node(bundle, block) )
+    {  //yes - this is ours so go to work
+    
+        // we expect this to be the "first" block, and there might or
+        // might not be others. But we should get to this one first and,
+        // during the processing, convert any other C3 blocks to their
+        // unencapsulated form. That is, when this call is over, there
+        // should be no more blocks for us to deal with. Any remaining
+        // C3 block should be for a nested instance
+        
+        // get pieces from params -- salt, iv, range, 
+        
+        buf = locals->security_params().buf();
+        len = locals->security_params().len();
+        
+        log_debug_p(log, "Ciphersuite_PC3::validate() locals->correlator() 0x%llx", U64FMT(locals->correlator()));
+        log_debug_p(log, "Ciphersuite_PC3::validate() security params, len = %zu", len);
+        while ( len > 0 ) {
+            item_type = *buf++;
+            --len;
+            sdnv_len = SDNV::decode(buf, len, &field_length);
+            buf += sdnv_len;
+            len -= sdnv_len;
+            
+            switch ( item_type ) {
+            case CS_IV_field: 
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() iv item, len = %llu", U64FMT(field_length));
+                memcpy(iv, buf, iv_len);
+                buf += field_length;
+                len -= field_length;
+            }
+            break;
+                    
+            case CS_PC_block_salt:
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() salt item, len = %llu", U64FMT(field_length));
+                memcpy(salt, buf, nonce_len - iv_len);
+                buf += field_length;
+                len -= field_length;
+            }
+            break;
+                    
+            case CS_fragment_offset_and_length_field:
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() frag info item, len = %llu", U64FMT(field_length));
+                sdnv_len = SDNV::decode(buf, len, &frag_offset_);
+                buf += sdnv_len;
+                len -= sdnv_len;
+                sdnv_len = SDNV::decode(buf, len, &orig_length_);
+                buf += sdnv_len;
+                len -= sdnv_len;
+            }
+            break;
+                    
+            default:    // deal with improper items
+                log_err_p(log, "Ciphersuite_PC3::validate: unexpected item type %d in security_params",
+                          item_type);
+                goto fail;
+            }
+        }
+        
+        // get pieces from results -- key, icv
+        buf = locals->security_result().buf();
+        len = locals->security_result().len();
+        
+        log_debug_p(log, "Ciphersuite_PC3::validate() security result, len = %zu", len);
+        while ( len > 0 ) {
+            item_type = *buf++;
+            --len;
+            sdnv_len = SDNV::decode(buf, len, &field_length);
+            buf += sdnv_len;
+            len -= sdnv_len;
+            
+            switch ( item_type ) {
+            case CS_key_info_field: 
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() key-info item");
+                // not sure what this looks like
+                buf += field_length;
+                len -= field_length;
+            }
+            break;
+                    
+/*
+            case CS_encoded_key_field:
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() encoded key item");
+                KeySteward::decrypt(bundle, locals->security_src(), buf, field_length, db);
+                memcpy(key, db.buf(), key_len);
+                log_debug_p(log, "Ciphersuite_PC3::validate() key      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",
+                            key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7], 
+                            key[8], key[9], key[10], key[11], key[12], key[13], key[14], key[15]);
+                buf += field_length;
+                len -= field_length;
+            }
+            break;
+*/
+
+            case CS_PC_block_ICV_field:
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() icv item");
+                memcpy(tag, buf, tag_len);
+                buf += field_length;
+                len -= field_length;
+            }
+            break;
+                    
+            case CS_encap_block_field:
+            {
+                // don't think we should have one of these here,
+                // only in the correlated blocks
+                log_err_p(log, "Ciphersuite_PC3::validate: unexpected encap block in security_result");
+                goto fail;
+            }
+            break;
+                    
+            default:    // deal with improper items
+                log_err_p(log, "Ciphersuite_PC3::validate: unexpected item type %d in security_result",
+                          item_type);
+                goto fail;
+            }
+        }
+        
+        // prepare context - one time for all usage here
+        gcm_init_and_key(key, key_len, &(ctx_ex.c));
+        ctx_ex.operation = op_decrypt;
+
+        // we have the necessary pieces from params and result so now
+        // walk all the blocks and do the various processing things needed.
+        // First is to get the iterator to where we are (see note in "generate()"
+        // for why we do this)
+        
+        log_debug_p(log, "Ciphersuite_PC3::validate() walk block list");
+        for (BlockInfoVec::iterator iter = block_list->begin();
+             iter != block_list->end();
+             ++iter)
+        {
+            // step over all blocks up to and including the one which
+            // prompted this call, pointed at by "block" argument
+            if ( (&*iter) <= block )
+                continue;
+            
+            target_locals = dynamic_cast<BP_Local_CS*>(iter->locals()); //might or might not be valid        
+
+            switch ( iter->type() ) {
+                
+            case BundleProtocol::CONFIDENTIALITY_BLOCK:
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() C block %p", &*iter);
+                BlockInfo::DataBuffer    encap_block;
+                CS_FAIL_IF_NULL(target_locals);
+                // even though this isn't our block, the value will have
+                // been set when the block was finished being received
+                // (in Ciphersuite::parse)
+                log_debug_p(log, "Ciphersuite_PC3::validate() C block owner_cs_num %d", target_locals->owner_cs_num());
+                if ( target_locals->owner_cs_num() != CSNUM_PC3 )  
+                    continue;        // only decapsulate C3
+                      
+                // it's a C3 block but make sure we own it -- does the
+                // correlator match ??
+                if ( target_locals->correlator() != locals->correlator() )
+                    continue;        // not ours
+                      
+                // OK - it's ours and we now decapsulate it.
+                // Get the necessary pieces from it, such as iv
+                buf = target_locals->security_params().buf();
+                len = target_locals->security_params().len();
+                    
+                log_debug_p(log, "Ciphersuite_PC3::validate() target security params, len = %zu", len);
+                while ( len > 0 ) {
+                    item_type = *buf++;
+                    --len;
+                    sdnv_len = SDNV::decode(buf, len, &field_length);
+                    buf += sdnv_len;
+                    len -= sdnv_len;
+                        
+                    switch ( item_type ) {
+                    case CS_IV_field: 
+                    {
+                        log_debug_p(log, "Ciphersuite_PC3::validate() target iv item, len = %llu", U64FMT(field_length));
+                        memcpy(target_iv, buf, iv_len);
+                        buf += field_length;
+                        len -= field_length;
+                    }
+                    break;
+                                
+                    default:    // deal with improper items
+                        log_err_p(log, "Ciphersuite_PC3::validate: unexpected item type %d in target security_params",
+                                  item_type);
+                        goto fail;
+                    }
+                }
+                    
+                buf = target_locals->security_result().buf();
+                len = target_locals->security_result().len();
+                    
+                log_debug_p(log, "Ciphersuite_PC3::validate() target security result, len = %zu", len);
+                while ( len > 0 ) {
+                    item_type = *buf++;
+                    --len;
+                    sdnv_len = SDNV::decode(buf, len, &field_length);
+                    buf += sdnv_len;
+                    len -= sdnv_len;
+                        
+                    // we don't necessarily know what order these two fields
+                    // will be in, so collect both and decrypt afterwards
+                    switch ( item_type ) {
+                    case CS_PC_block_ICV_field: 
+                    {
+                        log_debug_p(log, "Ciphersuite_PC3::validate() target icv item, len = %llu", U64FMT(field_length));
+                        memcpy(tag_encap, buf, tag_len);
+                        buf += field_length;
+                        len -= field_length;
+                    }
+                    break;
+                                
+                    case CS_encap_block_field: 
+                    {
+                        log_debug_p(log, "Ciphersuite_PC3::validate() encap block item, len = %llu", U64FMT(field_length));
+                        encap_block.reserve(field_length);
+                        encap_block.set_len(field_length);
+                        memcpy(encap_block.buf(), buf, field_length);
+                        buf += field_length;
+                        len -= field_length;
+                    }
+                    break;
+                                
+                    default:    // deal with improper items
+                        log_err_p(log, "Ciphersuite_PC3::validate: unexpected item type %d in target security_result",
+                                  item_type);
+                        goto fail;
+                    }
+                }
+                    
+                // nonce is 12 bytes, first 4 are salt (same for all blocks)
+                // and last 8 bytes are per-block IV. The final 4 bytes in
+                // the full block-sized field are, of course, the counter
+                // which is not represented here
+                ptr = nonce;
+                    
+                memcpy(ptr, salt, nonce_len - iv_len);
+                ptr += nonce_len - iv_len;
+                memcpy(ptr, target_iv, iv_len);
+                    
+                // prepare context
+                gcm_init_message(nonce, nonce_len, &(ctx_ex.c));
+                    
+                // decrypt message
+                ret = gcm_decrypt_message(nonce, 
+                                          nonce_len, 
+                                          NULL, 
+                                          0, 
+                                          encap_block.buf(),
+                                          encap_block.len(),
+                                          tag_encap,                // tag is input, for validation against calculated tag
+                                          tag_len,
+                                          &(ctx_ex.c));
+                    
+                // check return value that the block was OK
+                if ( ret != 0 ) {
+                    log_err_p(log, "Ciphersuite_PC3::validate: gcm_decrypt_message failed, ret = %d", ret);
+                    goto fail;
+                }
+                    
+                // encap_block is the raw data of the encapsulated block
+                // and now we have to reconstitute it the way it used to be :)
+                    
+                // Parse the content as would be done for a newly-received block
+                // using the owner's consume() method 
+                    
+                // We need to stitch up the EID lists as the list in the block is broken. 
+                // The way to do this is to create a slightly-synthetic preamble
+                // with the appropriate eid-offsets in it. The pre-existing list has been
+                // preserved and carried along. But the offsets contained in the preamble
+                // refer to an outdated image of the dictionary. So we copy the offsets
+                // from the *current* block into the synthetic preamble.
+                // The list will then have the correct pointers into the dictionary, 
+                // as those will have been updated at all the intermediate nodes.
+                // The remainder of the preamble comes from the encapsulated block. 
+                    
+                data = encap_block.buf();
+                len = encap_block.len();
+                    
+                BlockInfo info(BundleProtocol::find_processor(*data));
+                u_int64_t eid_ref_count = 0LLU;
+                u_int64_t current_eid_count;
+                u_int64_t flags;
+                u_int64_t content_length = 0LLU;
+                    
+                BlockInfo::DataBuffer    preamble;
+                preamble.reserve(iter->full_length());    //can't be bigger
+                // do set_len() later
+                    
+                // copy bits and pieces from the decrypted block
+                ptr = preamble.buf();
+                rem = iter->full_length();
+                    
+                *ptr++ = *data++;                // block type
+                rem--;
+                len--;
+                sdnv_len = SDNV::decode(data, len, &flags);        // block processing flags (SDNV)
+                data += sdnv_len;
+                len -= sdnv_len;
+                log_debug_p(log, "Ciphersuite_PC3::validate() target block type %hhu flags 0x%llx", *(preamble.buf()), U64FMT(flags));
+                // Also see if there are EID refs, and if there will be any in 
+                // the resultant block
+                    
+                // EID list is next, starting with the count
+                if  ( flags & BundleProtocol::BLOCK_FLAG_EID_REFS ) {                    
+                    sdnv_len = SDNV::decode(data, len, &eid_ref_count);
+                    data += sdnv_len;
+                    len -= sdnv_len;
+                        
+                    current_eid_count = iter->eid_list().size();
+                        
+                    if ( eid_ref_count != current_eid_count ) {
+                        log_err_p(log, "Ciphersuite_PC3::validate: eid_ref_count %lld  != current_eid_count %lld", 
+                                  U64FMT(eid_ref_count), U64FMT(current_eid_count));
+                        goto fail;        // block is broken somehow
+                    }
+                }
+
+                // each ref is a pair of SDNVs, so step over 2 * eid_ref_count
+                if ( eid_ref_count > 0 ) {
+                    for ( u_int32_t i = 0; i < (2 * eid_ref_count); i++ ) {
+                        sdnv_len = SDNV::len(data);
+                        data += sdnv_len;
+                        len -= sdnv_len;
+                    }
+                }        // now we're positioned after the broken refs, if any
+                sdnv_len = SDNV::decode(data, len, &content_length);
+                data += sdnv_len;
+                len -= sdnv_len;
+                log_debug_p(log, "Ciphersuite_PC3::validate() target data content size %llu", U64FMT(content_length));
+
+                // fix up last-block flag
+                // this probably isn't the last block, but who knows ? :)
+                if ( iter->flags() & BundleProtocol::BLOCK_FLAG_LAST_BLOCK ) 
+                    flags |= BundleProtocol::BLOCK_FLAG_LAST_BLOCK;
+                else
+                    flags &= ~BundleProtocol::BLOCK_FLAG_LAST_BLOCK;
+                    
+                // put flags into the adjusted block
+                sdnv_len = SDNV::encode(flags, ptr, rem);
+                ptr += sdnv_len;
+                rem -= sdnv_len;
+                    
+                // copy the offsets from the current block
+                if ( eid_ref_count > 0 ) {
+                    u_char*        cur_ptr = iter->contents().buf();
+                    size_t        cur_len = iter->full_length();
+                        
+                    cur_ptr++;    //type field
+                    cur_len--;
+                    sdnv_len = SDNV::len(cur_ptr);    //flags
+                    cur_ptr += sdnv_len;
+                    cur_len -= sdnv_len;
+                        
+                    sdnv_len = SDNV::len(cur_ptr);    //eid ref count
+                    cur_ptr += sdnv_len;
+                    cur_len -= sdnv_len;
+                        
+                    // put eid_count into the adjusted block
+                    log_debug_p(log, "Ciphersuite_PC3::validate() eid_ref_count %lld", U64FMT(eid_ref_count));
+                    sdnv_len = SDNV::encode(eid_ref_count, ptr, rem);
+                    ptr += sdnv_len;
+                    rem -= sdnv_len;
+                        
+                    // now copy the reference pairs
+                    for ( u_int32_t i = 0; i < (2 * eid_ref_count); i++ ) {
+                        sdnv_len = SDNV::len(cur_ptr);
+                        memcpy(ptr, cur_ptr, sdnv_len);
+                        cur_ptr += sdnv_len;
+                        cur_len -= sdnv_len;
+                        ptr += sdnv_len;
+                        rem -= sdnv_len;
+                    }
+                }
+                    
+                // length of data content in block
+                sdnv_len = SDNV::encode(content_length, ptr, rem);
+                ptr += sdnv_len;
+                rem -= sdnv_len;
+                    
+                // we now have a preamble in "preamble" and the rest of the data at *data
+                size_t    preamble_size = ptr - preamble.buf();
+                preamble.set_len(preamble_size);
+                log_debug_p(log, "Ciphersuite_PC3::validate() target preamble_size %zu", preamble_size);
+                    
+                     
+                {
+                    // we're reusing the existing BlockInfo but we need to clean it first
+                    iter->~BlockInfo();
+                    /* we'd like to reinitilize the block thusly
+                     *      iter->BlockInfo(type);
+                     * but C++ gets bent so we have to achieve the desired result
+                     * in a more devious fashion using placement-new. 
+                     */
+                        
+                    log_debug_p(log, "Ciphersuite_PC3::validate() re-init target");
+                    BlockInfo* bp = &*iter;
+                    bp = new (bp) BlockInfo(BundleProtocol::find_processor(*(preamble.buf())));
+                    CS_FAIL_IF_NULL(bp);
+                }
+                    
+                // process preamble
+                log_debug_p(log, "Ciphersuite_PC3::validate() process target preamble");
+                int cc = iter->owner()->consume(deliberate_const_cast_bundle, &*iter, preamble.buf(), preamble_size);
+                if (cc < 0) {
+                    log_err_p(log, "Ciphersuite_PC3::validate: consume failed handling encapsulated preamble 0x%x, cc = %d",
+                              info.type(), cc);
+                    goto fail;
+                }
+                    
+                // process the main part of the encapsulated block
+                log_debug_p(log, "Ciphersuite_PC3::validate() process target content");
+                cc = iter->owner()->consume(deliberate_const_cast_bundle, &*iter, data, len);
+                if (cc < 0) {
+                    log_err_p(log, "Ciphersuite_PC3::validate: consume failed handling encapsulated block 0x%x, cc = %d",
+                              info.type(), cc);
+                    goto fail;
+                }
+                log_debug_p(log, "Ciphersuite_PC3::validate() decapsulation done");
+            }
+            break;
+                    
+            case BundleProtocol::PAYLOAD_BLOCK: 
+            {
+                log_debug_p(log, "Ciphersuite_PC3::validate() PAYLOAD_BLOCK");
+                u_char          tag_calc[tag_len];
+                // nonce is 12 bytes, first 4 are salt (same for all blocks)
+                // and last 8 bytes are per-block IV. The final 4 bytes in
+                // the full block-sized field are, of course, the counter
+                // which is not represented here
+                ptr = nonce;
+                    
+                memcpy(ptr, salt, salt_len);
+                ptr += salt_len;
+                memcpy(ptr, iv, iv_len);
+                log_debug_p(log, "Ciphersuite_PC3::validate() nonce    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",
+                            nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7], nonce[8], nonce[9], nonce[10], nonce[11]);
+                    
+                // prepare context
+                gcm_init_message(nonce, nonce_len, &(ctx_ex.c));
+                    
+                offset = iter->data_offset();
+                len = iter->data_length();
+
+                changed =
+                    iter->owner()->mutate( Ciphersuite_PC3::do_crypt,
+                                           deliberate_const_cast_bundle,
+                                           block,
+                                           &*iter,
+                                           offset,
+                                           len,
+                                           r );
+                
+                // collect the tag (icv) from the context
+                gcm_compute_tag( tag_calc, tag_len, &(ctx_ex.c) );
+                log_debug_p(log, "Ciphersuite_PC3::validate() tag      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",
+                            tag[0], tag[1], tag[2], tag[3], tag[4], tag[5], tag[6], tag[7], tag[8], tag[9], tag[10], tag[11], tag[12], tag[13], tag[14], tag[15]);
+                log_debug_p(log, "Ciphersuite_PC3::validate() tag_calc 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",
+                            tag_calc[0], tag_calc[1], tag_calc[2], tag_calc[3], tag_calc[4], tag_calc[5], tag_calc[6], tag_calc[7], 
+                            tag_calc[8], tag_calc[9], tag_calc[10], tag_calc[11], tag_calc[12], tag_calc[13], tag_calc[14], tag_calc[15]);
+                if (memcmp(tag, tag_calc, tag_len) != 0) {
+                    log_err_p(log, "Ciphersuite_PC3::validate: tag comparison failed");
+                    goto fail;
+                }
+                    
+            }
+            break;
+                    
+            default: 
+                continue;
+                    
+            }    // end switch
+        }        // end for
+        log_debug_p(log, "Ciphersuite_PC3::validate() walk block list done");
+        locals->set_proc_flag(CS_BLOCK_PASSED_VALIDATION |
+                              CS_BLOCK_COMPLETED_DO_NOT_FORWARD);
+    } 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_PC3::validate() %p done", block);
+    return true;
+
+ fail:
+    if ( locals !=  NULL )
+        locals->set_proc_flag(CS_BLOCK_FAILED_VALIDATION |
+                              CS_BLOCK_COMPLETED_DO_NOT_FORWARD);
+    *deletion_reason = BundleProtocol::REASON_SECURITY_FAILED;
+    return false;
+
+}
+
+//----------------------------------------------------------------------
+int
+Ciphersuite_PC3::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_PC3::prepare() - not being forwarded");
+        return BP_SUCCESS;     //it was for us so don't forward
+    }
+    
+    BlockInfo bi = BlockInfo(BundleProtocol::find_processor(BundleProtocol::CONFIDENTIALITY_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 ) {
+        
+        ASSERT(source != NULL);
+        if ( Ciphersuite::destination_is_local_node(bundle, source) )
+            return BP_SUCCESS;     //don't forward if it's for here
+
+        xmit_blocks->push_back(bi);
+        BlockInfo* bp = &(xmit_blocks->back());
+        bp->set_eid_list(source->eid_list());
+        log_debug_p(log, "Ciphersuite_PC3::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);
+        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_PC3);
+        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_PC3::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_PC3::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_PC3::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_PC3::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_PC3);
+        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_PC3::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_PC3::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_PC3::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_PC3::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;
+    u_char          key[key_len];  //use AES128 16-byte key
+    u_char          iv[iv_len];    // AES iv length
+    u_char          salt[nonce_len - iv_len];       // salt for GCM
+    u_char          fragment_item[24];               // 24 is enough for 2 max-sized SDNVs and type and length
+    u_int16_t       cs_flags = 0;
+    bool            need_correlator = false;
+    u_int64_t       correlator = 0LLU;  
+    BP_Local_CS*    locals = dynamic_cast<BP_Local_CS*>(block->locals());
+    BP_Local_CS*    target_locals = NULL;
+    u_char*         ptr;
+    size_t          temp;
+    size_t          rem;
+    DataBuffer      encrypted_key;
+    size_t          param_len = 0;
+    size_t          res_len = 0;
+    size_t          length = 0;
+    u_char*         buf = NULL;
+    int             len = 0;
+    int             sdnv_len = 0;       // use an int to handle -1 return values
+    u_int16_t       n = 0;
+    int             err = 0;
+    BlockInfo::DataBuffer* contents = NULL;
+    LocalBuffer* digest_result = NULL;
+    LocalBuffer* params = NULL;
+    
+    log_debug_p(log, "Ciphersuite_PC3::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::CONFIDENTIALITY_BLOCK,
+                          BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR |
+                          BundleProtocol::BLOCK_FLAG_REPLICATE           |
+                          (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_PC3::generate() %p done", block);
+        return BP_SUCCESS;
+    }
+    
+    // This block will have a correlator iff there are PSBs or CBs,
+    // no correlator if only a payload and no PSBs or CBs
+    for (BlockInfoVec::iterator iter = xmit_blocks->begin();
+         iter != xmit_blocks->end();
+         ++iter)
+    {
+        n++;
+        // Advance the iterator to our current position.
+        // Long-winded implementation note:-
+        // we would use "distance" but block isn't
+        // an iterator, just a pointer. Pointer arithmetic
+        // works in some systems but is not always portable
+        // so we don't do that here.
+        if ( (&*iter) <= block )
+            continue;
+        
+        if (  iter->type() == BundleProtocol::PAYLOAD_SECURITY_BLOCK ) {
+            need_correlator = true;     // yes - we need a correlator
+            break;
+        }
+        
+        if (  iter->type() == BundleProtocol::CONFIDENTIALITY_BLOCK ) {
+            target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+            CS_FAIL_IF_NULL(target_locals);    
+            if ( target_locals->owner_cs_num() == CSNUM_PC3 ) {
+                need_correlator = true;     // yes - we need a correlator
+                break;
+            }
+        }
+        
+    }
+    
+    if ( need_correlator ) {
+        correlator = create_correlator(bundle, xmit_blocks);
+        correlator |= (int)CSNUM_PC3 << 16;      // add our ciphersuite number
+        locals->set_correlator( correlator );
+        log_debug_p(log, "Ciphersuite_PC3::generate() correlator %llu", U64FMT(correlator));
+    }
+        
+    /* params field will contain
+       - salt (4 bytes), plus type and length
+       - IV (block-length, 8 bytes), plus type and length
+       - fragment offset and length, if a fragment-bundle, plus type and length
+       - key-identifier (optional, not implemented yet), plus type and length
+    */
+
+    params = locals->writable_security_params();
+    
+    // populate salt and IV
+    RAND_bytes(salt, sizeof(salt));
+    RAND_bytes(iv, sizeof(iv));
+
+    // save for finalize()
+    locals->set_salt(salt, sizeof(salt));
+    locals->set_iv(iv, sizeof(iv));
+
+    param_len = 1 + 1 + sizeof(salt);        // salt
+    param_len += 1 + 1 + sizeof(iv);            // IV
+    
+    if ( bundle->is_fragment() ) {
+        log_debug_p(log, "Ciphersuite_PC3::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;
+        
+    }
+    
+    params->reserve(param_len);    //will need more if there is a key identifier - TBD
+    params->set_len(param_len);
+    log_debug_p(log, "Ciphersuite_PC3::generate() security params, len = %zu", param_len);
+    
+    ptr = params->buf();
+    *ptr++ = CS_PC_block_salt;
+    *ptr++ = sizeof(salt);                // less than 127
+    memcpy(ptr, salt, sizeof(salt));
+    ptr += sizeof(salt);
+    *ptr++ = CS_IV_field;
+    *ptr++ = sizeof(iv);                // less than 127
+    memcpy(ptr, iv, sizeof(iv));
+    ptr += sizeof(iv);
+    
+    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
+         - encrypted key, plus type and length
+         - ICV (Integrity Check Value), plus type and length
+    */
+    
+    /* encrypt the key, keeping a local copy --
+       put it directly into the result field
+    */
+    
+    // generate actual key
+    RAND_bytes(key, sizeof(key));
+    
+    // save for finalize()
+    locals->set_key(key, sizeof(key));
+
+    log_debug_p(log, "Ciphersuite_PC3::generate() key      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",
+                key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7], 
+                key[8], key[9], key[10], key[11], key[12], key[13], key[14], key[15]);
+    err = KeySteward::encrypt(bundle, NULL, link, locals->security_dest(), key, sizeof(key), encrypted_key);
+    CS_FAIL_IF(err != 0);
+    log_debug_p(log, "Ciphersuite_PC3::generate() encrypted_key len = %zu", encrypted_key.len());
+    
+    res_len = 1 + SDNV::encoding_len(encrypted_key.len()) + encrypted_key.len();
+    res_len += 1 + 1 + tag_len;
+    
+    digest_result = locals->writable_security_result();
+    digest_result->reserve(res_len);
+    digest_result->set_len(res_len);
+    rem = res_len;
+    
+    ptr = digest_result->buf();
+    *ptr++ = Ciphersuite::CS_key_info_field;
+    rem--;
+    temp = SDNV::encode(encrypted_key.len(), ptr, rem);
+    ptr += temp;
+    rem -= temp;
+    memcpy(ptr, encrypted_key.buf(), encrypted_key.len());
+    ptr += encrypted_key.len();
+    rem -= encrypted_key.len();
+    
+    // First we need to work out the lengths and create the preamble
+    length = 0;       
+    if ( need_correlator ) {
+        log_debug_p(log, "Ciphersuite_PC3::generate() correlator %llu", U64FMT(correlator));
+        locals->set_correlator(correlator);
+        length += SDNV::encoding_len(locals->correlator());
+        cs_flags |= CS_BLOCK_HAS_CORRELATOR;
+    }
+    
+    // ciphersuite number and flags
+    cs_flags |= CS_BLOCK_HAS_PARAMS;
+    cs_flags |= CS_BLOCK_HAS_RESULT;
+    locals->set_cs_flags(cs_flags);
+    length += SDNV::encoding_len(CSNUM_PC3);
+    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::CONFIDENTIALITY_BLOCK,
+                      BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR |
+                      (last ? BundleProtocol::BLOCK_FLAG_LAST_BLOCK : 0),
+                      length);
+    
+
+    log_debug_p(log, "Ciphersuite_PC3::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 ( need_correlator ) {
+        // correlator
+        sdnv_len = SDNV::encode(locals->correlator(), buf, len);
+        CS_FAIL_IF(sdnv_len <= 0);
+        buf += sdnv_len;
+        len -= sdnv_len;
+    }
+    
+    
+    // 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_PC3::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_PC3::finalize(const Bundle*  bundle, 
+                         BlockInfoVec*  xmit_blocks,
+                         BlockInfo*     block, 
+                         const LinkRef& link)
+{
+    (void)link;
+    int             result = BP_FAIL;
+    Bundle*         deliberate_const_cast_bundle = const_cast<Bundle*>(bundle);
+    size_t          offset;
+    size_t          len;
+    size_t          length;
+    size_t          param_len;
+    size_t          res_len;
+    gcm_ctx_ex      ctx_ex;    // includes OpenSSL context within it
+    OpaqueContext*  r = reinterpret_cast<OpaqueContext*>(&ctx_ex);
+    bool            changed = false;
+    u_char          key[key_len];  //use AES128 16-byte key
+    u_char          iv[iv_len];    // GCM "iv" length is 8 bytes
+    u_char          nonce[nonce_len];    // 12 bytes recommended
+    u_char          tag[tag_len];    // 128 bits recommended
+    u_char*         buf;
+    u_char*         ptr;
+    BP_Local_CS*    locals = NULL;
+    BP_Local_CS*    target_locals = NULL;
+    u_int64_t       correlator;
+    std::vector<u_int64_t>           correlator_list;
+    std::vector<u_int64_t>::iterator cl_iter;
+    size_t          correlator_size = 0;
+    int             sdnv_len = 0;       // use an int to handle -1 return values
+    EndpointID      local_eid = BundleDaemon::instance()->local_eid();
+        
+    log_debug_p(log, "Ciphersuite_PC3::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;
+    
+    // prepare context - one time for all usage here
+    memcpy(key, locals->key().buf(), key_len);
+    gcm_init_and_key(key, key_len, &(ctx_ex.c));
+    ctx_ex.operation = op_encrypt;
+        
+    // Walk the list and process each of the blocks.
+    // We only change PS, C3 and the payload data,
+    // all others are unmodified
+    
+    // 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
+    
+    // However this causes a problem if the PS is a two-block scheme,
+    // as we'll convert the second, correlated block to C and then
+    // the PS processor won't have its second block.
+    
+    // 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
+    
+    
+    log_debug_p(log, "Ciphersuite_PC3::finalize() walk block list");
+    for (BlockInfoVec::iterator iter = xmit_blocks->begin();
+         iter != xmit_blocks->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 !!
+        if ( (&*iter) <= block ) {
+            if ( iter->type() == BundleProtocol::PAYLOAD_SECURITY_BLOCK ) {
+                //add correlator to exclude-list
+                target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+                CS_FAIL_IF_NULL(target_locals);
+                correlator_list.push_back(target_locals->correlator());
+            } else if (iter->type() == BundleProtocol::CONFIDENTIALITY_BLOCK ) {
+                target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+                CS_FAIL_IF_NULL(target_locals);
+                if ( target_locals->owner_cs_num() == CSNUM_PC3 ) {
+                    correlator_list.push_back(target_locals->correlator());
+                }
+            }
+            continue;
+        }
+        
+        
+        switch ( iter->type() ) {
+            
+        case BundleProtocol::PAYLOAD_SECURITY_BLOCK:
+        case BundleProtocol::CONFIDENTIALITY_BLOCK:
+        {
+                    
+            target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+            CS_FAIL_IF_NULL(target_locals);
+            log_debug_p(log, "Ciphersuite_PC3::finalize() PS or C block type %d cs_num %d",
+                        iter->type(), target_locals->owner_cs_num());
+            if (  iter->type() == BundleProtocol::CONFIDENTIALITY_BLOCK  &&
+                  target_locals->owner_cs_num() != CSNUM_PC3                )  
+                continue;        // only encapsulate 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 the primary
+            bool    skip_psb = false;
+            target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+            log_debug_p(log, "Ciphersuite_PC3::finalize() target_locals->cs_flags 0x%hx", target_locals->cs_flags());
+            log_debug_p(log, "Ciphersuite_PC3::finalize() 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_psb = true;
+                        break;        //break from for-loop
+                    }
+                }
+                if ( skip_psb )
+                    break;  //break from switch, continue for "for" loop
+                        
+            }
+                    
+            log_debug_p(log, "Ciphersuite_PC3::finalize() encapsulate this block, len %u eid_ref_count %zu", 
+                        iter->full_length(), iter->eid_list().size());
+            // Either it has no correlator, or it wasn't in the list.
+            // So we will encapsulate it into a C block. 
+            // We need to get the entire content and encrypt it, 
+            // then release the locals since we are changing ownership/type.
+            // First thing to do is encrypt the entire target block
+                    
+            // extract the last-block flag since we'll need it shortly
+            bool    last = iter->flags() & BundleProtocol::BLOCK_FLAG_LAST_BLOCK;
+                    
+            // nonce is 12 bytes, first 4 are salt (same for all blocks)
+            // and last 8 bytes are per-block IV. The final 4 bytes in
+            // the full block-sized field are, of course, the counter
+            // which is not represented here
+            ptr = nonce;
+                    
+            memcpy(ptr, locals->salt().buf(), nonce_len - iv_len);
+            ptr += nonce_len - iv_len;
+            RAND_bytes(iv, sizeof(iv));    // populate IV
+            memcpy(ptr, iv, iv_len);
+                    
+            // prepare context
+            gcm_init_message(nonce, nonce_len, &(ctx_ex.c));
+                    
+            // encrypt message in-place
+            gcm_encrypt_message(nonce, 
+                                nonce_len, 
+                                NULL, 
+                                0, 
+                                iter->writable_contents()->buf(),
+                                iter->full_length(),
+                                tag,
+                                tag_len,
+                                &(ctx_ex.c));
+                    
+            // copy encrypted block before it gets overwritten
+            BlockInfo::DataBuffer    encap_block;
+            size_t  encap_len = 1 + SDNV::encoding_len(iter->full_length()) + iter->full_length();
+            encap_block.reserve(encap_len);
+            encap_block.set_len(encap_len);
+            ptr = encap_block.buf();
+            *ptr++ = CS_encap_block_field;
+            sdnv_len = SDNV::encode(iter->full_length(), ptr, encap_len - 1);
+            CS_FAIL_IF(sdnv_len <= 0);                    
+            ptr += sdnv_len;
+            memcpy(ptr, iter->contents().buf(), iter->full_length());
+                    
+            // copy C3 locals to new locals block, but don't
+            // replace old locals block yet
+            BP_Local_CS* new_target_locals = new BP_Local_CS(*locals);
+            u_int16_t cs_flags = CS_BLOCK_HAS_PARAMS | CS_BLOCK_HAS_RESULT | CS_BLOCK_HAS_CORRELATOR;
+                    
+            // we must make sure we retain EID references to the existing
+            // security-source and security-dest. Since this is a follower
+            // correlated block, we don't have actual security-src and -dest
+            // as those are set in the parent. 
+                    
+            // So now we have the encrypted block in the work buffer and what
+            // remains to do is construct the actual block contents in place
+            // of the plaintext.
+                    
+            // Note that we using OUR correlator here, not the one in the
+            // original block
+            correlator_size = SDNV::encoding_len(locals->correlator());
+                    
+            // First we need to work out the lengths and create the preamble
+            //length = sizeof(num);         // ciphersuite number and flags
+            length = 0;         // ciphersuite number and flags
+            length += SDNV::encoding_len(CSNUM_PC3);
+            length += SDNV::encoding_len(locals->cs_flags());
+            length +=  correlator_size;
+            param_len = 1 + 1 + iv_len;        // 8-byte iv, sdnv fits in 1 byte
+            length += SDNV::encoding_len(param_len) + param_len;
+            res_len = 1 + 1 + tag_len + encap_len;    //16-byte tag, sdnv is 1 byte
+            length += SDNV::encoding_len(res_len) + res_len;
+                    
+            iter->writable_contents()->set_len(0);    // empty it to start with
+            iter->set_owner(BundleProtocol::find_processor(BundleProtocol::CONFIDENTIALITY_BLOCK));            // "steal this block"
+            generate_preamble(xmit_blocks, 
+                              &*iter,
+                              BundleProtocol::CONFIDENTIALITY_BLOCK,
+                              BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR |
+                              (last ? BundleProtocol::BLOCK_FLAG_LAST_BLOCK : 0),
+                              length);
+                    
+                    
+            log_debug_p(log, "Ciphersuite_PC3::finalize() preamble len %u block len %zu", iter->data_offset(), length);
+            log_debug_p(log, "Ciphersuite_PC3::finalize() owner()->block_type() %u buf()[0] %hhu", 
+                        iter->owner()->block_type(), iter->contents().buf()[0]);
+            iter->writable_contents()->reserve(iter->data_offset() + length);
+            iter->writable_contents()->set_len(iter->data_offset() + length);
+            buf = iter->writable_contents()->buf() + iter->data_offset();
+            len = length;
+                    
+            // Assemble data into block contents.
+                        
+            // ciphersuite number and flags
+            new_target_locals->set_cs_flags(cs_flags);
+            sdnv_len = SDNV::encode(CSNUM_PC3, buf, len);
+            CS_FAIL_IF(sdnv_len <= 0); 
+            buf += sdnv_len;
+            len -= sdnv_len;
+                    
+            sdnv_len = SDNV::encode(new_target_locals->cs_flags(), buf, len);
+            CS_FAIL_IF(sdnv_len <= 0); 
+            buf += sdnv_len;
+            len -= sdnv_len;
+                    
+            // correlator
+            sdnv_len = SDNV::encode(locals->correlator(), buf, len);
+            CS_FAIL_IF(sdnv_len <= 0); 
+            buf += sdnv_len;
+            len -= sdnv_len;
+                    
+            // length of security params
+            sdnv_len = SDNV::encode(param_len, buf, len);
+            CS_FAIL_IF(sdnv_len <= 0); 
+            buf += sdnv_len;
+            len -= sdnv_len;
+                    
+            // security params data - it's just the iv item
+            *buf++ = CS_IV_field;
+            --len;
+            *buf++ = iv_len;
+            --len;
+            memcpy(buf, iv, iv_len);
+            buf += iv_len;
+            len -= iv_len;
+
+            // length of security result
+            sdnv_len = SDNV::encode(res_len, buf, len);
+            CS_FAIL_IF(sdnv_len <= 0); 
+            buf += sdnv_len;
+            len -= sdnv_len;
+                    
+            // security result data - tag and the encapsulated block
+            *buf++ = CS_PC_block_ICV_field;
+            --len;
+            *buf++ = tag_len;
+            --len;
+            memcpy(buf, tag, tag_len);
+            buf += tag_len;
+            len -= tag_len;
+                    
+                    
+            memcpy(buf, encap_block.buf(), encap_block.len());
+            buf += encap_block.len();
+            len -= encap_block.len();
+            CS_FAIL_IF(len != 0);
+                    
+            // fix up the BlockInfo and related things, 
+            // remembering that "locals" was copied
+            // from the original C3 block
+                    
+            iter->set_locals(new_target_locals);    //will also decrement ref for old one
+            target_locals = dynamic_cast<BP_Local_CS*>(iter->locals());
+            log_debug_p(log, "Ciphersuite_PC3::finalize() encapsulation done");
+
+        }
+        break;
+            
+        case BundleProtocol::PAYLOAD_BLOCK:
+        {
+            // prepare context -- key supplied already
+            // nonce is 12 bytes, first 4 are salt (same for all blocks)
+            // and last 8 bytes are per-block IV. The final 4 bytes in
+            // the full block-sized field are, of course, the counter
+            // which is not represented here
+            u_char*            ptr;
+            size_t            rem;
+            u_char            type;
+            u_int64_t        field_len;
+            ptr = nonce;
+                    
+            log_debug_p(log, "Ciphersuite_PC3::finalize() PAYLOAD_BLOCK");
+            memcpy(ptr, locals->salt().buf(), salt_len);
+            ptr += salt_len;
+            memcpy(ptr, locals->iv().buf(), iv_len);
+                    
+            // prepare context
+            log_debug_p(log, "Ciphersuite_PC3::finalize() nonce    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",
+                        nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7], nonce[8], nonce[9], nonce[10], nonce[11]);
+            gcm_init_message(nonce, nonce_len, &(ctx_ex.c));
+                
+            offset = iter->data_offset();
+            len = iter->data_length();
+            changed = 
+                iter->owner()->mutate( Ciphersuite_PC3::do_crypt,
+                                       deliberate_const_cast_bundle,
+                                       block,
+                                       &*iter,
+                                       offset,
+                                       len,
+                                       r);
+                    
+            // collect the tag (icv) from the context
+            gcm_compute_tag( tag, tag_len, &(ctx_ex.c) );
+            log_debug_p(log, "Ciphersuite_PC3::finalize() tag      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",
+                        tag[0], tag[1], tag[2], tag[3], tag[4], tag[5], tag[6], tag[7], tag[8], tag[9], tag[10], tag[11], tag[12], tag[13], tag[14], tag[15]);
+                    
+            // get the result item, and step over the encrypted key item
+            LocalBuffer* result = locals->writable_security_result();
+            ptr = result->buf();
+            rem = result->len();
+            type = *ptr++;
+            CS_FAIL_IF(type != Ciphersuite::CS_key_info_field);
+            rem--;
+            sdnv_len = SDNV::decode( ptr, rem, &field_len);
+            ptr += sdnv_len;
+            rem -= sdnv_len;
+            ptr += field_len;
+            rem -= field_len;
+            CS_FAIL_IF( rem != 1 + 1 + tag_len);
+            *ptr++ = CS_PC_block_ICV_field;
+            rem--;
+            *ptr++ = tag_len;
+            rem--;
+            memcpy(ptr, tag, tag_len);
+                    
+            // now put the result item into the block contents
+            BlockInfo::DataBuffer* contents = block->writable_contents();
+            u_char* buf = contents->buf();
+            rem = contents->len();
+            buf += block->data_offset();    // we need to add data_offset as well,
+            rem -= block->data_offset();    // since we're pointing at the whole buffer
+                    
+            buf += locals->security_result_offset();    //and this offset is just within
+            rem -= 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
+            rem -= sdnv_len;
+            memcpy(buf, result->buf(), result->len());
+            log_debug_p(log, "Ciphersuite_PC3::finalize() PAYLOAD_BLOCK done");
+                    
+                    
+                    
+        }
+        break;  //break from switch, continue for "for" loop
+                
+        default:
+            continue;
+        
+        }   // end of switch        
+        
+        
+    }
+    log_debug_p(log, "Ciphersuite_PC3::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;
+}
+
+//----------------------------------------------------------------------
+bool
+Ciphersuite_PC3::do_crypt(const Bundle*    bundle,
+                         const BlockInfo* caller_block,
+                         BlockInfo*       target_block,
+                         void*            buf,
+                         size_t           len,
+                         OpaqueContext*   r)
+{    
+    (void) bundle;
+    (void) caller_block;
+    (void) target_block;
+    gcm_ctx_ex* pctx = reinterpret_cast<gcm_ctx_ex*>(r);
+    
+    log_debug_p(log, "Ciphersuite_PC3::do_crypt() operation %hhu len %zu", pctx->operation, len);
+    if (pctx->operation == op_encrypt)
+        gcm_encrypt( reinterpret_cast<u_char*>(buf), len, &(pctx->c) );
+    else    
+        gcm_decrypt( reinterpret_cast<u_char*>(buf), len, &(pctx->c) );
+
+    return (len > 0) ? true : false;
+}
+
+} // namespace dtn
+
+#endif /* BSP_ENABLED */