diff --git a/enzevalos_iphone/AppDelegate.swift b/enzevalos_iphone/AppDelegate.swift
index 53a092f80b60b7e90a55c7da2e35fd2ff0ddea8b..a1824bbd5e9dd8fd8b651077de88c461250769b7 100644
--- a/enzevalos_iphone/AppDelegate.swift
+++ b/enzevalos_iphone/AppDelegate.swift
@@ -23,6 +23,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         // Override point for customization after application launch.
         //UINavigationBar.appearance().backgroundColor = UIColor.blueColor()
 
+        createKey(userID: "TEST@example.com")
+        
         resetApp()
         if (!UserDefaults.standard.bool(forKey: "launchedBefore")) {
             self.window = UIWindow(frame: UIScreen.main.bounds)
@@ -72,10 +74,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     func resetApp() {
         if UserDefaults.standard.bool(forKey: "reset") {
-            //if UserManager.loadUserValue(Attribute.userAddr) as! String == "ullimuelle@web.de" {
+            if UserManager.loadUserValue(Attribute.userAddr) as! String == "ullimuelle@web.de" {
                 let mailhandler = MailHandler.init()
                 mailhandler.moveMails(mails: DataHandler.handler.mails, from: "INBOX", to: "ARCHIVE")
-            //}
+            }
+            
+            
+        
             DataHandler.handler.reset()
             Onboarding.credentials = nil
             Onboarding.credentialFails = 0
diff --git a/enzevalos_iphone/enzevalos_iphone-Bridging-Header.h b/enzevalos_iphone/enzevalos_iphone-Bridging-Header.h
index 29f5e8ceee3e9a1a359be3814062f14ffaf51996..c06514c75ea0ec9ea9476b1ac569ed17953526a0 100644
--- a/enzevalos_iphone/enzevalos_iphone-Bridging-Header.h
+++ b/enzevalos_iphone/enzevalos_iphone-Bridging-Header.h
@@ -15,8 +15,5 @@
 #import "PGPSignaturePacket.h"
 #import "PGPKeyID.h"
 #import "PGPPublicKeyPacket.h"
-#import "mrmailbox.h"
-#import "mrcmdline.h"
-#import "mrtools.h"
-#import "stress.h"
-#import "mre2ee_driver.h"
+#import "netpgp-extra.h"
+#import "autocryptgen.h"
diff --git a/netpgp/KeyGen.c b/netpgp/KeyGen.c
new file mode 100644
index 0000000000000000000000000000000000000000..5c40618faf40707bcb6017d0040c3f48094c4f83
--- /dev/null
+++ b/netpgp/KeyGen.c
@@ -0,0 +1,221 @@
+//
+//  KeyGen.c
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 11.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+#include "KeyGen.h"
+#include "netpgp-extra.h"
+
+/*******************************************************************************
+ * Key generatation
+ ******************************************************************************/
+
+
+static unsigned add_key_prefs(pgp_create_sig_t *sig)
+{
+    /* similar to pgp_add_key_prefs(), Mimic of GPG default settings, limited to supported algos */
+    return
+    /* Symmetric algo prefs */
+    pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_SKA) &&
+    pgp_write_scalar(sig->output, PGP_SA_AES_256, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_AES_128, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_CAST5, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_TRIPLEDES, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_IDEA, 1) &&
+    
+    /* Hash algo prefs, the first algo is the preferred algo */
+    pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_HASH) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA256, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA384, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA512, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA224, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA1, 1) && /* Edit for Autocrypt/Delta Chat: due to the weak SHA1, it should not be preferred */
+    
+    /* Compression algo prefs */
+    pgp_write_ss_header(sig->output, 2/*1+number of following items*/, PGP_PTAG_SS_PREF_COMPRESS) &&
+    pgp_write_scalar(sig->output, PGP_C_ZLIB, 1) /*&& -- not sure if Delta Chat will support bzip2 on all platforms, however, this is not that important as typical files are compressed themselves and text is not that big
+                                                  pgp_write_scalar(sig->output, PGP_C_BZIP2, 1) -- if you re-enable this, do not forget to modifiy the header count*/;
+}
+
+
+static void add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t key_expiry)
+{
+    /* similar to pgp_add_selfsigned_userid() which, however, uses different key flags */
+    pgp_create_sig_t	*sig;
+    pgp_subpacket_t	 sigpacket;
+    pgp_memory_t		*mem_sig = NULL;
+    pgp_output_t		*sigoutput = NULL;
+    
+    /* create sig for this pkt */
+    sig = pgp_create_sig_new();
+    pgp_sig_start_key_sig(sig, &skey->key.seckey.pubkey, NULL, userid, PGP_CERT_POSITIVE);
+    
+    pgp_add_creation_time(sig, time(NULL));
+    pgp_add_key_expiration_time(sig, key_expiry);
+    pgp_add_primary_userid(sig, 1);
+    pgp_add_key_flags(sig, PGP_KEYFLAG_SIGN_DATA|PGP_KEYFLAG_CERT_KEYS);
+    add_key_prefs(sig);
+    pgp_add_key_features(sig); /* will add 0x01 - modification detection */
+    
+    pgp_end_hashed_subpkts(sig);
+    
+    pgp_add_issuer_keyid(sig, skey->pubkeyid); /* the issuer keyid is not hashed by definition */
+    
+    pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+    pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey);
+    
+    /* add this packet to key */
+    sigpacket.length = pgp_mem_len(mem_sig);
+    sigpacket.raw = pgp_mem_data(mem_sig);
+    
+    /* add user id and signature to key */
+    pgp_update_userid(skey, userid, &sigpacket, &sig->sig.info);
+    if(pkey) {
+        pgp_update_userid(pkey, userid, &sigpacket, &sig->sig.info);
+    }
+    
+    /* cleanup */
+    pgp_create_sig_delete(sig);
+    pgp_output_delete(sigoutput);
+    pgp_memory_free(mem_sig);
+}
+
+
+static void add_subkey_binding_signature(pgp_subkeysig_t* p, pgp_key_t* primarykey, pgp_key_t* subkey, pgp_key_t* seckey)
+{
+    /*add "0x18: Subkey Binding Signature" packet, PGP_SIG_SUBKEY */
+    pgp_create_sig_t* sig;
+    pgp_output_t*     sigoutput = NULL;
+    pgp_memory_t*     mem_sig = NULL;
+    
+    sig = pgp_create_sig_new();
+    pgp_sig_start_key_sig(sig, &primarykey->key.pubkey, &subkey->key.pubkey, NULL, PGP_SIG_SUBKEY);
+    
+    pgp_add_creation_time(sig, time(NULL));
+    pgp_add_key_expiration_time(sig, 0);
+    pgp_add_key_flags(sig, PGP_KEYFLAG_ENC_STORAGE|PGP_KEYFLAG_ENC_COMM); /* NB: algo/hash/compression preferences are not added to subkeys */
+    
+    pgp_end_hashed_subpkts(sig);
+    
+    pgp_add_issuer_keyid(sig, seckey->pubkeyid); /* the issuer keyid is not hashed by definition */
+    
+    pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+    pgp_write_sig(sigoutput, sig, &seckey->key.seckey.pubkey, &seckey->key.seckey);
+    
+    p->subkey         = primarykey->subkeyc-1; /* index of subkey in array */
+    p->packet.length  = mem_sig->length;
+    p->packet.raw     = mem_sig->buf; mem_sig->buf = NULL; /* move ownership to packet */
+    copy_sig_info(&p->siginfo, &sig->sig.info); /* not sure, if this is okay, however, siginfo should be set up, otherwise we get "bad info-type" errors */
+    
+    pgp_create_sig_delete(sig);
+    pgp_output_delete(sigoutput);
+    free(mem_sig); /* do not use pgp_memory_free() as this would also free mem_sig->buf which is owned by the packet */
+}
+
+
+int mre2ee_driver_create_keypair(const char* addr)
+{
+    int              success = 0;
+    pgp_key_t        seckey, pubkey, subkey;
+    uint8_t          subkeyid[PGP_KEY_ID_SIZE];
+    uint8_t*         user_id = NULL;
+    pgp_memory_t     *pubmem = pgp_memory_new(), *secmem = pgp_memory_new();
+    pgp_output_t     *pubout = pgp_output_new(), *secout = pgp_output_new();
+    
+    memset(&seckey, 0, sizeof(pgp_key_t));
+    memset(&pubkey, 0, sizeof(pgp_key_t));
+    memset(&subkey, 0, sizeof(pgp_key_t));
+    
+    if( addr==NULL || pubmem==NULL || secmem==NULL || pubout==NULL || secout==NULL ) {
+        goto cleanup;
+    }
+    
+    /* Generate User ID.  For convention, use the same address as given in `Autocrypt: to=...` in angle brackets
+     (RFC 2822 grammar angle-addr, see also https://autocrypt.org/en/latest/level0.html#type-p-openpgp-based-key-data )
+     We do not add the name to the ID for the following reasons:
+     - privacy
+     - the name may be changed
+     - shorter keys
+     - the name is already taken from From:
+     - not Autocrypt:-standard */
+    user_id = (uint8_t*) printf("<%s>", addr);
+    
+    /* generate two keypairs */
+    if( !pgp_rsa_generate_keypair(&seckey, 2048/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0)
+       || !pgp_rsa_generate_keypair(&subkey, 2048/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0) ) {
+        goto cleanup;
+    }
+    
+    
+    /* Create public key, bind public subkey to public key
+     ------------------------------------------------------------------------ */
+    
+    pubkey.type = PGP_PTAG_CT_PUBLIC_KEY;
+    pgp_pubkey_dup(&pubkey.key.pubkey, &seckey.key.pubkey);
+    memcpy(pubkey.pubkeyid, seckey.pubkeyid, PGP_KEY_ID_SIZE);
+    pgp_fingerprint(&pubkey.pubkeyfpr, &seckey.key.pubkey, 0);
+    add_selfsigned_userid(&seckey, &pubkey, (const uint8_t*)user_id, 0/*never expire*/);
+    
+    EXPAND_ARRAY((&pubkey), subkey);
+    {
+        pgp_subkey_t* p = &pubkey.subkeys[pubkey.subkeyc++];
+        pgp_pubkey_dup(&p->key.pubkey, &subkey.key.pubkey);
+        pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &pubkey.key.pubkey, PGP_HASH_SHA1);
+        memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
+    }
+    
+    EXPAND_ARRAY((&pubkey), subkeysig);
+    add_subkey_binding_signature(&pubkey.subkeysigs[pubkey.subkeysigc++], &pubkey, &subkey, &seckey);
+    
+    
+    /* Create secret key, bind secret subkey to secret key
+     ------------------------------------------------------------------------ */
+    
+    EXPAND_ARRAY((&seckey), subkey);
+    {
+        pgp_subkey_t* p = &seckey.subkeys[seckey.subkeyc++];
+        pgp_seckey_dup(&p->key.seckey, &subkey.key.seckey);
+        pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &seckey.key.pubkey, PGP_HASH_SHA1);
+        memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
+    }
+    
+    EXPAND_ARRAY((&seckey), subkeysig);
+    add_subkey_binding_signature(&seckey.subkeysigs[seckey.subkeysigc++], &seckey, &subkey, &seckey);
+    
+    
+    /* Done with key generation, write binary keys to memory
+     ------------------------------------------------------------------------ */
+    
+    pgp_writer_set_memory(pubout, pubmem);
+    if( !pgp_write_xfer_key(pubout, &pubkey, 0/*armored*/)
+       || pubmem->buf == NULL || pubmem->length <= 0 ) {
+        goto cleanup;
+    }
+    
+    pgp_writer_set_memory(secout, secmem);
+    if( !pgp_write_xfer_key(secout, &seckey, 0/*armored*/)
+       || secmem->buf == NULL || secmem->length <= 0 ) {
+        goto cleanup;
+    }
+    
+    //mrkey_set_from_raw(ret_public_key, pubmem->buf, pubmem->length, MR_PUBLIC);
+    //mrkey_set_from_raw(ret_private_key, secmem->buf, secmem->length, MR_PRIVATE);
+    
+    success = 1;
+    
+cleanup:
+    if( pubout ) { pgp_output_delete(pubout); }
+    if( secout ) { pgp_output_delete(secout); }
+    if( pubmem ) { pgp_memory_free(pubmem); }
+    if( secmem ) { pgp_memory_free(secmem); }
+    pgp_key_free(&seckey); /* not: pgp_keydata_free() which will also free the pointer itself (we created it on the stack) */
+    pgp_key_free(&pubkey);
+    pgp_key_free(&subkey);
+    free(user_id);
+    return success;
+}
+
+
diff --git a/netpgp/KeyGen.h b/netpgp/KeyGen.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a2ca2b79c5f9a7dbf9d6448b903a63725b9fac2
--- /dev/null
+++ b/netpgp/KeyGen.h
@@ -0,0 +1,13 @@
+//
+//  KeyGen.h
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 11.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+#ifndef KeyGen_h
+#define KeyGen_h
+
+int mre2ee_driver_create_keypair(const char* addr)
+#endif /* KeyGen_h */
diff --git a/netpgp/PGPKeyGen.m b/netpgp/PGPKeyGen.m
new file mode 100644
index 0000000000000000000000000000000000000000..d72fe0999ddfc4ed2eb83278a7d05217ccbd709a
--- /dev/null
+++ b/netpgp/PGPKeyGen.m
@@ -0,0 +1,10 @@
+//
+//  PGPKeyGen.m
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 12.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
diff --git a/netpgp/PGPKeyGeneration.swift b/netpgp/PGPKeyGeneration.swift
new file mode 100644
index 0000000000000000000000000000000000000000..d09ab75d37804abc02dbceb2a3acab4a5d5e59a8
--- /dev/null
+++ b/netpgp/PGPKeyGeneration.swift
@@ -0,0 +1,78 @@
+//
+//  PGPKeyGeneration.swift
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 10.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+import Foundation
+
+
+public func createKey(userID:String){
+    var adr: UInt8
+    adr = 8
+   var pk: Array<CChar> = Array(repeating: 32, count: 4048)
+   var sk: Array<CChar> = Array(repeating: 32, count: 4048)
+   
+    print("CreateKey")
+    mre2ee_driver_create_keypair(&adr, &pk, &sk)
+    print("###########")
+    
+    
+    
+}
+
+public func generateAutocryptSecretKey(userID: String){
+    var secretKey = pgp_key_t()
+    var subKey = pgp_key_t()
+    var publicKey = pgp_key_t()
+    
+    memset(&secretKey, 0, MemoryLayout<pgp_key_t>.size)
+    memset(&subKey, 0, MemoryLayout<pgp_key_t>.size)
+    memset(&publicKey, 0, MemoryLayout<pgp_key_t>.size)
+
+    pgp_rsa_generate_keypair(&secretKey, 2048, 65537, nil, nil, nil, 0)
+    pgp_rsa_generate_keypair(&subKey, 2048, 65537, nil, nil, nil, 0)
+    
+    publicKey.type = PGP_PTAG_CT_PUBLIC_KEY
+    publicKey.key.pubkey = secretKey.key.pubkey
+
+    pgp_fingerprint(&publicKey.pubkeyfpr, &secretKey.key.pubkey, PGP_HASH_SHA256)
+    
+    bindSigKey(key: &publicKey, sigKey: &subKey, secKey: &secretKey)
+    
+    print("PK: \(publicKey.key.pubkey.birthtime)")
+    
+}
+
+
+private func bindSigKey( key: inout pgp_key_t, sigKey: inout pgp_key_t, secKey: inout pgp_key_t){
+    var primKey = key
+    var subKey = sigKey
+    var signature = pgp_create_sig_new()
+    var resultSig: UnsafeMutablePointer<pgp_output_t>?
+    
+    var mem: UnsafeMutablePointer<pgp_memory_t>?
+    
+    pgp_sig_start_key_sig(signature, &primKey.key.pubkey, &subKey.key.pubkey, nil, PGP_SIG_SUBKEY)
+    pgp_add_creation_time(signature, time(nil))
+    pgp_add_key_expiration_time(signature, 0)
+    pgp_add_key_flags(signature,UInt8(PGP_KEYFLAG_ENC_COMM.rawValue)) // add  PGP_KEYFLAG_ENC_STORAGE ???
+    
+    pgp_end_hashed_subpkts(signature)
+    pgp_add_issuer_keyid(signature, &secKey.pubkeyid.0)
+    
+    pgp_setup_memory_write(&resultSig, &mem, 128)
+    pgp_write_sig(resultSig, signature, &secKey.key.seckey.pubkey, &secKey.key.seckey)
+
+    //TODO: Consider pk???
+    
+    pgp_create_sig_delete(signature)
+    pgp_output_delete(resultSig)
+    free(mem)
+}
+
+
+
+
diff --git a/netpgp/autocryptgen.c b/netpgp/autocryptgen.c
new file mode 100644
index 0000000000000000000000000000000000000000..c040aeae001081070a4c4688b0c1376ca8b1c1b5
--- /dev/null
+++ b/netpgp/autocryptgen.c
@@ -0,0 +1,280 @@
+//
+//  autocryptgen.c
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 11.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+#include "autocryptgen.h"
+
+
+/* portable replacement for strdup(3) */
+char *
+netpgp_strdup(const char *s)
+{
+    size_t	 len;
+    char	*cp;
+    
+    len = strlen(s);
+    if ((cp = calloc(1, len + 1)) != NULL) {
+        (void) memcpy(cp, s, len);
+        cp[len] = 0x0;
+    }
+    return cp;
+}
+
+
+char *
+pgp_export_key(pgp_io_t *io, const pgp_key_t *keydata, uint8_t *passphrase)
+{
+    pgp_output_t	*output;
+    pgp_memory_t	*mem;
+    char		*cp;
+    
+    //__PGP_USED(io);
+    pgp_setup_memory_write(&output, &mem, 128);
+    pgp_write_xfer_key(output, keydata, 1);
+    
+    /* TODO deal with passphrase again
+     pgp_write_xfer_seckey(output, keydata, passphrase,
+					strlen((char *)passphrase), 1);
+     */
+    cp = netpgp_strdup(pgp_mem_data(mem));
+    pgp_teardown_memory_write(output, mem);
+    return cp;
+}
+
+
+
+/*******************************************************************************
+ * Key generatation
+ ******************************************************************************/
+
+
+static unsigned add_key_prefs(pgp_create_sig_t *sig)
+{
+    /* similar to pgp_add_key_prefs(), Mimic of GPG default settings, limited to supported algos */
+    return
+    /* Symmetric algo prefs */
+    pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_SKA) &&
+    pgp_write_scalar(sig->output, PGP_SA_AES_256, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_AES_128, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_CAST5, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_TRIPLEDES, 1) &&
+    pgp_write_scalar(sig->output, PGP_SA_IDEA, 1) &&
+    
+    /* Hash algo prefs, the first algo is the preferred algo */
+    pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_HASH) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA256, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA384, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA512, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA224, 1) &&
+    pgp_write_scalar(sig->output, PGP_HASH_SHA1, 1) && /* Edit for Autocrypt/Delta Chat: due to the weak SHA1, it should not be preferred */
+    
+    /* Compression algo prefs */
+    pgp_write_ss_header(sig->output, 2/*1+number of following items*/, PGP_PTAG_SS_PREF_COMPRESS) &&
+    pgp_write_scalar(sig->output, PGP_C_ZLIB, 1) /*&& -- not sure if Delta Chat will support bzip2 on all platforms, however, this is not that important as typical files are compressed themselves and text is not that big
+                                                  pgp_write_scalar(sig->output, PGP_C_BZIP2, 1) -- if you re-enable this, do not forget to modifiy the header count*/;
+}
+
+
+static void add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t key_expiry)
+{
+    /* similar to pgp_add_selfsigned_userid() which, however, uses different key flags */
+    pgp_create_sig_t	*sig;
+    pgp_subpacket_t	 sigpacket;
+    pgp_memory_t		*mem_sig = NULL;
+    pgp_output_t		*sigoutput = NULL;
+    
+    /* create sig for this pkt */
+    sig = pgp_create_sig_new();
+    pgp_sig_start_key_sig(sig, &skey->key.seckey.pubkey, NULL, userid, PGP_CERT_POSITIVE);
+    
+    pgp_add_creation_time(sig, time(NULL));
+    pgp_add_key_expiration_time(sig, key_expiry);
+    pgp_add_primary_userid(sig, 1);
+    pgp_add_key_flags(sig, PGP_KEYFLAG_SIGN_DATA|PGP_KEYFLAG_CERT_KEYS);
+    add_key_prefs(sig);
+    pgp_add_key_features(sig); /* will add 0x01 - modification detection */
+    
+    pgp_end_hashed_subpkts(sig);
+    
+    pgp_add_issuer_keyid(sig, skey->pubkeyid); /* the issuer keyid is not hashed by definition */
+    
+    pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+    pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey);
+    
+    /* add this packet to key */
+    sigpacket.length = pgp_mem_len(mem_sig);
+    sigpacket.raw = pgp_mem_data(mem_sig);
+    
+    /* add user id and signature to key */
+    pgp_update_userid(skey, userid, &sigpacket, &sig->sig.info);
+    if(pkey) {
+        pgp_update_userid(pkey, userid, &sigpacket, &sig->sig.info);
+    }
+    
+    /* cleanup */
+    pgp_create_sig_delete(sig);
+    pgp_output_delete(sigoutput);
+    pgp_memory_free(mem_sig);
+}
+
+
+static void add_subkey_binding_signature(pgp_subkeysig_t* p, pgp_key_t* primarykey, pgp_key_t* subkey, pgp_key_t* seckey)
+{
+    /*add "0x18: Subkey Binding Signature" packet, PGP_SIG_SUBKEY */
+    pgp_create_sig_t* sig;
+    pgp_output_t*     sigoutput = NULL;
+    pgp_memory_t*     mem_sig = NULL;
+    
+    sig = pgp_create_sig_new();
+    pgp_sig_start_key_sig(sig, &primarykey->key.pubkey, &subkey->key.pubkey, NULL, PGP_SIG_SUBKEY);
+    
+    pgp_add_creation_time(sig, time(NULL));
+    pgp_add_key_expiration_time(sig, 0);
+    pgp_add_key_flags(sig, PGP_KEYFLAG_ENC_STORAGE|PGP_KEYFLAG_ENC_COMM); /* NB: algo/hash/compression preferences are not added to subkeys */
+    
+    pgp_end_hashed_subpkts(sig);
+    
+    pgp_add_issuer_keyid(sig, seckey->pubkeyid); /* the issuer keyid is not hashed by definition */
+    
+    pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+    pgp_write_sig(sigoutput, sig, &seckey->key.seckey.pubkey, &seckey->key.seckey);
+    
+    p->subkey         = primarykey->subkeyc-1; /* index of subkey in array */
+    p->packet.length  = mem_sig->length;
+    p->packet.raw     = mem_sig->buf; mem_sig->buf = NULL; /* move ownership to packet */
+    copy_sig_info(&p->siginfo, &sig->sig.info); /* not sure, if this is okay, however, siginfo should be set up, otherwise we get "bad info-type" errors */
+    
+    pgp_create_sig_delete(sig);
+    pgp_output_delete(sigoutput);
+    free(mem_sig); /* do not use pgp_memory_free() as this would also free mem_sig->buf which is owned by the packet */
+}
+
+
+int mre2ee_driver_create_keypair(uint8_t* adr, char* pk, char* sk)//const char* addr)
+{
+    int              success = 0;
+    pgp_key_t        seckey, pubkey, subkey;
+    uint8_t          subkeyid[PGP_KEY_ID_SIZE];
+    uint8_t*         user_id = NULL;
+    pgp_memory_t     *pubmem = pgp_memory_new(), *secmem = pgp_memory_new();
+    pgp_output_t     *pubout = pgp_output_new(), *secout = pgp_output_new();
+    
+    memset(&seckey, 0, sizeof(pgp_key_t));
+    memset(&pubkey, 0, sizeof(pgp_key_t));
+    memset(&subkey, 0, sizeof(pgp_key_t));
+    
+    if( //addr==NULL ||
+       pubmem==NULL || secmem==NULL || pubout==NULL || secout==NULL ) {
+        goto cleanup;
+    }
+    
+    /* Generate User ID.  For convention, use the same address as given in `Autocrypt: to=...` in angle brackets
+     (RFC 2822 grammar angle-addr, see also https://autocrypt.org/en/latest/level0.html#type-p-openpgp-based-key-data )
+     We do not add the name to the ID for the following reasons:
+     - privacy
+     - the name may be changed
+     - shorter keys
+     - the name is already taken from From:
+     - not Autocrypt:-standard */
+    //user_id = 0; //addr// (uint8_t*) mr_mprintf("<%s>", addr); // (uint8_t)atoi
+    user_id = adr;
+    /* generate two keypairs */
+    if( !pgp_rsa_generate_keypair(&seckey, 2048/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0)
+       || !pgp_rsa_generate_keypair(&subkey, 2048/*bits*/, 65537UL/*e*/, NULL, NULL, NULL, 0) ) {
+        goto cleanup;
+    }
+    
+    
+    /* Create public key, bind public subkey to public key
+     ------------------------------------------------------------------------ */
+    
+    pubkey.type = PGP_PTAG_CT_PUBLIC_KEY;
+    pgp_pubkey_dup(&pubkey.key.pubkey, &seckey.key.pubkey);
+    memcpy(pubkey.pubkeyid, seckey.pubkeyid, PGP_KEY_ID_SIZE);
+    pgp_fingerprint(&pubkey.pubkeyfpr, &seckey.key.pubkey, 0);
+    add_selfsigned_userid(&seckey, &pubkey, (const uint8_t*)user_id, 0/*never expire*/);
+    
+    EXPAND_ARRAY((&pubkey), subkey);
+    {
+        pgp_subkey_t* p = &pubkey.subkeys[pubkey.subkeyc++];
+        pgp_pubkey_dup(&p->key.pubkey, &subkey.key.pubkey);
+        pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &pubkey.key.pubkey, PGP_HASH_SHA1);
+        memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
+    }
+    
+    EXPAND_ARRAY((&pubkey), subkeysig);
+    add_subkey_binding_signature(&pubkey.subkeysigs[pubkey.subkeysigc++], &pubkey, &subkey, &seckey);
+    
+    
+    /* Create secret key, bind secret subkey to secret key
+     ------------------------------------------------------------------------ */
+    
+    EXPAND_ARRAY((&seckey), subkey);
+    {
+        pgp_subkey_t* p = &seckey.subkeys[seckey.subkeyc++];
+        pgp_seckey_dup(&p->key.seckey, &subkey.key.seckey);
+        pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &seckey.key.pubkey, PGP_HASH_SHA1);
+        memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE);
+    }
+    
+    EXPAND_ARRAY((&seckey), subkeysig);
+    add_subkey_binding_signature(&seckey.subkeysigs[seckey.subkeysigc++], &seckey, &subkey, &seckey);
+    
+    
+    /* Done with key generation, write binary keys to memory
+     ------------------------------------------------------------------------ */
+    
+    pgp_writer_set_memory(pubout, pubmem);
+    if( !pgp_write_xfer_key(pubout, &pubkey, 1/*armored*/)
+       || pubmem->buf == NULL || pubmem->length <= 0 ) {
+        goto cleanup;
+    }
+    
+    pgp_writer_set_memory(secout, secmem);
+    if( !pgp_write_xfer_key(secout, &seckey, 1/*armored*/)
+       || secmem->buf == NULL || secmem->length <= 0 ) {
+        goto cleanup;
+    }
+    
+    //pgp_filewrite(pk,  pubmem->buf, pubmem->length, 1);
+    //pgp_filewrite(sk,  secmem->buf, secmem->length, 1);
+
+    char s [5000];
+    for (int i = 0; i < 5000; i++) {
+        s[i] = i;
+    }
+    pgp_write(pubout, s, 1);
+    
+    printf("My new key %s \n", s);
+
+    printf("My key??? %s \n",pgp_mem_data(pubmem));
+    
+    pgp_io_t s_io;
+
+    memset(&s_io, 0, sizeof(pgp_io_t));
+    s_io.outs = stdout;
+    s_io.errs = stderr;
+    s_io.res  = stderr;
+    
+    //memcpy(pk, pubout, pubmem->length);
+   // memcpy(sk, secout, secmem->length);
+    pgp_export_key(&s_io, &pubkey, NULL);
+
+    success = 1;
+    
+cleanup:
+    if( pubout ) { pgp_output_delete(pubout); }
+    if( secout ) { pgp_output_delete(secout); }
+    if( pubmem ) { pgp_memory_free(pubmem); }
+    if( secmem ) { pgp_memory_free(secmem); }
+    pgp_key_free(&seckey); /* not: pgp_keydata_free() which will also free the pointer itself (we created it on the stack) */
+    pgp_key_free(&pubkey);
+    pgp_key_free(&subkey);
+    //free(user_id);
+    return success;
+}
+
diff --git a/netpgp/autocryptgen.h b/netpgp/autocryptgen.h
new file mode 100644
index 0000000000000000000000000000000000000000..df109384346b133b9bc24f5c6eb6f0d0416c0fe8
--- /dev/null
+++ b/netpgp/autocryptgen.h
@@ -0,0 +1,18 @@
+//
+//  autocryptgen.h
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 11.06.17.
+//  Copyright © 2017 fu-berlin. All rights reserved.
+//
+
+#ifndef autocryptgen_h
+#define autocryptgen_h
+
+#include <stdio.h>
+#include "netpgp-extra.h"
+
+
+int mre2ee_driver_create_keypair(uint8_t* adr, char* pk, char* sk);//const char* addr);
+
+#endif /* autocryptgen_h */
diff --git a/netpgp/compress.c b/netpgp/compress.c
new file mode 100644
index 0000000000000000000000000000000000000000..3186a019af52371dd7c8a788c37c7f30ef1a2745
--- /dev/null
+++ b/netpgp/compress.c
@@ -0,0 +1,490 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include <string.h>
+
+#include "netpgp/packet-parse.h"
+#include "netpgp/errors.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/crypto.h"
+#include "netpgp/memory.h"
+#include "netpgp/writer.h"
+
+#define DECOMPRESS_BUFFER	1024
+
+typedef struct {
+	pgp_compression_type_t type;
+	pgp_region_t   *region;
+	uint8_t   	in[DECOMPRESS_BUFFER];
+	uint8_t   	out[DECOMPRESS_BUFFER];
+	z_stream        zstream;/* ZIP and ZLIB */
+	size_t          offset;
+	int             inflate_ret;
+} z_decompress_t;
+
+#ifdef HAVE_BZLIB_H
+typedef struct {
+	pgp_compression_type_t type;
+	pgp_region_t   *region;
+	char            in[DECOMPRESS_BUFFER];
+	char            out[DECOMPRESS_BUFFER];
+	bz_stream       bzstream;	/* BZIP2 */
+	size_t          offset;
+	int             inflate_ret;
+} bz_decompress_t;
+#endif
+
+typedef struct {
+	z_stream        stream;
+	uint8_t  	*src;
+	uint8_t  	*dst;
+} compress_t;
+
+/*
+ * \todo re       move code duplication between this and
+ * bzip2_compressed_data_reader
+ */
+static int
+zlib_compressed_data_reader(pgp_stream_t *stream, void *dest, size_t length,
+			    pgp_error_t **errors,
+			    pgp_reader_t *readinfo,
+			    pgp_cbdata_t *cbinfo)
+{
+	z_decompress_t *z = pgp_reader_get_arg(readinfo);
+	size_t           len;
+	size_t		 cc;
+	char		*cdest = dest;
+
+	if (z->type != PGP_C_ZIP && z->type != PGP_C_ZLIB) {
+		(void) fprintf(stderr,
+			"zlib_compressed_data_reader: weird type %d\n",
+			z->type);
+		return 0;
+	}
+
+	if (z->inflate_ret == Z_STREAM_END &&
+	    z->zstream.next_out == &z->out[z->offset]) {
+		return 0;
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr,
+			"zlib_compressed_data_reader: length %" PRIsize "d\n",
+			length);
+	}
+	for (cc = 0 ; cc < length ; cc += len) {
+		if (&z->out[z->offset] == z->zstream.next_out) {
+			int             ret;
+
+			z->zstream.next_out = z->out;
+			z->zstream.avail_out = sizeof(z->out);
+			z->offset = 0;
+			if (z->zstream.avail_in == 0) {
+				unsigned        n = z->region->length;
+
+				if (!z->region->indeterminate) {
+					n -= z->region->readc;
+					if (n > sizeof(z->in)) {
+						n = sizeof(z->in);
+					}
+				} else {
+					n = sizeof(z->in);
+				}
+				if (!pgp_stacked_limited_read(stream, z->in, n,
+						z->region,
+						errors, readinfo, cbinfo)) {
+					return -1;
+				}
+
+				z->zstream.next_in = z->in;
+				z->zstream.avail_in = (z->region->indeterminate) ?
+					z->region->last_read : n;
+			}
+			ret = inflate(&z->zstream, Z_SYNC_FLUSH);
+			if (ret == Z_STREAM_END) {
+				if (!z->region->indeterminate &&
+				    z->region->readc != z->region->length) {
+					PGP_ERROR_1(cbinfo->errors,
+						PGP_E_P_DECOMPRESSION_ERROR,
+						"%s",
+						"Compressed stream ended before packet end.");
+				}
+			} else if (ret != Z_OK) {
+				(void) fprintf(stderr, "ret=%d\n", ret);
+				PGP_ERROR_1(cbinfo->errors,
+					PGP_E_P_DECOMPRESSION_ERROR, "%s",
+					z->zstream.msg);
+			}
+			z->inflate_ret = ret;
+		}
+		if (z->zstream.next_out <= &z->out[z->offset]) {
+			(void) fprintf(stderr, "Out of memory in buffer\n");
+			return 0;
+		}
+		len = (size_t)(z->zstream.next_out - &z->out[z->offset]);
+        size_t left_in_cdest = length - cc;
+		if (len > left_in_cdest) {
+			len = left_in_cdest;
+		}
+		(void) memcpy(&cdest[cc], &z->out[z->offset], len);
+		z->offset += len;
+	}
+
+	return (int)length;
+}
+
+#ifdef HAVE_BZLIB_H
+/* \todo remove code duplication between this and zlib_compressed_data_reader */
+static int
+bzip2_compressed_data_reader(pgp_stream_t *stream, void *dest, size_t length,
+			     pgp_error_t **errors,
+			     pgp_reader_t *readinfo,
+			     pgp_cbdata_t *cbinfo)
+{
+	bz_decompress_t *bz = pgp_reader_get_arg(readinfo);
+	size_t		len;
+	size_t		 cc;
+	char		*cdest = dest;
+
+	if (bz->type != PGP_C_BZIP2) {
+		(void) fprintf(stderr, "Weird type %d\n", bz->type);
+		return 0;
+	}
+	if (bz->inflate_ret == BZ_STREAM_END &&
+	    bz->bzstream.next_out == &bz->out[bz->offset]) {
+		return 0;
+	}
+	for (cc = 0 ; cc < length ; cc += len) {
+		if (&bz->out[bz->offset] == bz->bzstream.next_out) {
+			int             ret;
+
+			bz->bzstream.next_out = (char *) bz->out;
+			bz->bzstream.avail_out = sizeof(bz->out);
+			bz->offset = 0;
+			if (bz->bzstream.avail_in == 0) {
+				unsigned        n = bz->region->length;
+
+				if (!bz->region->indeterminate) {
+					n -= bz->region->readc;
+					if (n > sizeof(bz->in))
+						n = sizeof(bz->in);
+				} else
+					n = sizeof(bz->in);
+
+				if (!pgp_stacked_limited_read(stream,
+						(uint8_t *) bz->in,
+						n, bz->region,
+						errors, readinfo, cbinfo))
+					return -1;
+
+				bz->bzstream.next_in = bz->in;
+				bz->bzstream.avail_in =
+					(bz->region->indeterminate) ?
+					 bz->region->last_read : n;
+			}
+			ret = BZ2_bzDecompress(&bz->bzstream);
+			if (ret == BZ_STREAM_END) {
+				if (!bz->region->indeterminate &&
+				    bz->region->readc != bz->region->length)
+					PGP_ERROR_1(cbinfo->errors,
+						PGP_E_P_DECOMPRESSION_ERROR,
+						"%s",
+						"Compressed stream ended before packet end.");
+			} else if (ret != BZ_OK) {
+				PGP_ERROR_1(cbinfo->errors,
+					PGP_E_P_DECOMPRESSION_ERROR,
+					"Invalid return %d from BZ2_bzDecompress", ret);
+			}
+			bz->inflate_ret = ret;
+		}
+		if (bz->bzstream.next_out <= &bz->out[bz->offset]) {
+			(void) fprintf(stderr, "Out of bz memroy\n");
+			return 0;
+		}
+		len = (size_t)(bz->bzstream.next_out - &bz->out[bz->offset]);
+		if (len > length) {
+			len = length;
+		}
+		(void) memcpy(&cdest[cc], &bz->out[bz->offset], len);
+		bz->offset += len;
+	}
+
+	return (int)length;
+}
+#endif
+
+/**
+ * \ingroup Core_Compress
+ *
+ * \param *region 	Pointer to a region
+ * \param *stream 	How to parse
+ * \param type Which compression type to expect
+*/
+
+int
+pgp_decompress(pgp_region_t *region, pgp_stream_t *stream,
+	       pgp_compression_type_t type)
+{
+	z_decompress_t z;
+#ifdef HAVE_BZLIB_H
+	bz_decompress_t bz;
+#endif
+	const int	printerrors = 1;
+	int             ret;
+
+	switch (type) {
+	case PGP_C_ZIP:
+	case PGP_C_ZLIB:
+		(void) memset(&z, 0x0, sizeof(z));
+
+		z.region = region;
+		z.offset = 0;
+		z.type = type;
+
+		z.zstream.next_in = Z_NULL;
+		z.zstream.avail_in = 0;
+		z.zstream.next_out = z.out;
+		z.zstream.zalloc = Z_NULL;
+		z.zstream.zfree = Z_NULL;
+		z.zstream.opaque = Z_NULL;
+
+		break;
+
+#ifdef HAVE_BZLIB_H
+	case PGP_C_BZIP2:
+		(void) memset(&bz, 0x0, sizeof(bz));
+
+		bz.region = region;
+		bz.offset = 0;
+		bz.type = type;
+
+		bz.bzstream.next_in = NULL;
+		bz.bzstream.avail_in = 0;
+		bz.bzstream.next_out = bz.out;
+		bz.bzstream.bzalloc = NULL;
+		bz.bzstream.bzfree = NULL;
+		bz.bzstream.opaque = NULL;
+#endif
+
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG,
+			"Compression algorithm %d is not yet supported", type);
+		return 0;
+	}
+
+	switch (type) {
+	case PGP_C_ZIP:
+		/* LINTED */ /* this is a lint problem in zlib.h header */
+		ret = (int)inflateInit2(&z.zstream, -15);
+		break;
+
+	case PGP_C_ZLIB:
+		/* LINTED */ /* this is a lint problem in zlib.h header */
+		ret = (int)inflateInit(&z.zstream);
+		break;
+
+#ifdef HAVE_BZLIB_H
+	case PGP_C_BZIP2:
+		ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0);
+		break;
+#endif
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG,
+			"Compression algorithm %d is not yet supported", type);
+		return 0;
+	}
+
+	switch (type) {
+	case PGP_C_ZIP:
+	case PGP_C_ZLIB:
+		if (ret != Z_OK) {
+			PGP_ERROR_1(&stream->errors,
+				PGP_E_P_DECOMPRESSION_ERROR,
+"Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret);
+			return 0;
+		}
+		pgp_reader_push(stream, zlib_compressed_data_reader,
+					NULL, &z);
+		break;
+
+#ifdef HAVE_BZLIB_H
+	case PGP_C_BZIP2:
+		if (ret != BZ_OK) {
+			PGP_ERROR_1(&stream->errors,
+				PGP_E_P_DECOMPRESSION_ERROR,
+"Cannot initialise BZIP2 stream for decompression: error=%d", ret);
+			return 0;
+		}
+		pgp_reader_push(stream, bzip2_compressed_data_reader,
+					NULL, &bz);
+		break;
+#endif
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG,
+			"Compression algorithm %d is not yet supported", type);
+		return 0;
+	}
+
+	ret = pgp_parse(stream, !printerrors);
+
+	pgp_reader_pop(stream);
+
+	return ret;
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Writes Compressed packet
+\param data Data to write out
+\param len Length of data
+\param output Write settings
+\return 1 if OK; else 0
+*/
+
+unsigned
+pgp_writez(pgp_output_t *out, const uint8_t *data, const unsigned len)
+{
+	compress_t	*zip;
+	size_t		 sz_in;
+	size_t		 sz_out;
+	int              ret;
+	int              r = 0;
+
+	/* compress the data */
+	const int       level = Z_DEFAULT_COMPRESSION;	/* \todo allow varying
+							 * levels */
+
+	if ((zip = calloc(1, sizeof(*zip))) == NULL) {
+		(void) fprintf(stderr, "pgp_writez: bad alloc\n");
+		return 0;
+	}
+	zip->stream.zalloc = Z_NULL;
+	zip->stream.zfree = Z_NULL;
+	zip->stream.opaque = NULL;
+
+	/* all other fields set to zero by use of calloc */
+
+	/* LINTED */ /* this is a lint problem in zlib.h header */
+	if ((int)deflateInit(&zip->stream, level) != Z_OK) {
+		(void) fprintf(stderr, "pgp_writez: can't initialise\n");
+		return 0;
+	}
+	/* do necessary transformation */
+	/* copy input to maintain const'ness of src */
+	if (zip->src != NULL || zip->dst != NULL) {
+		(void) fprintf(stderr, "pgp_writez: non-null streams\n");
+		return 0;
+	}
+
+	sz_in = len * sizeof(uint8_t);
+	sz_out = ((101 * sz_in) / 100) + 12;	/* from zlib webpage */
+	if ((zip->src = calloc(1, sz_in)) == NULL) {
+		free(zip);
+		(void) fprintf(stderr, "pgp_writez: bad alloc2\n");
+		return 0;
+	}
+	if ((zip->dst = calloc(1, sz_out)) == NULL) {
+		free(zip->src);
+		free(zip);
+		(void) fprintf(stderr, "pgp_writez: bad alloc3\n");
+		return 0;
+	}
+	(void) memcpy(zip->src, data, len);
+
+	/* setup stream */
+	zip->stream.next_in = zip->src;
+	zip->stream.avail_in = (unsigned)sz_in;
+	zip->stream.total_in = 0;
+
+	zip->stream.next_out = zip->dst;
+	zip->stream.avail_out = (unsigned)sz_out;
+	zip->stream.total_out = 0;
+
+	do {
+		r = deflate(&zip->stream, Z_FINISH);
+	} while (r != Z_STREAM_END);
+
+	/* write it out */
+	ret = pgp_write_ptag(out, PGP_PTAG_CT_COMPRESSED) &&
+		pgp_write_length(out, (unsigned)(zip->stream.total_out + 1))&&
+		pgp_write_scalar(out, PGP_C_ZLIB, 1) &&
+		pgp_write(out, zip->dst, (unsigned)zip->stream.total_out);
+
+	free(zip->src);
+	free(zip->dst);
+	free(zip);
+	return ret;
+}
diff --git a/netpgp/create.c b/netpgp/create.c
new file mode 100644
index 0000000000000000000000000000000000000000..a15a0e5863659df73fd3a6db2fae2512110317b1
--- /dev/null
+++ b/netpgp/create.c
@@ -0,0 +1,1376 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_OPENSSL_CAST_H
+#include <openssl/cast.h>
+#endif
+
+#include "netpgp/create.h"
+#include "netpgp/keyring.h"
+#include "netpgp/packet.h"
+#include "netpgp/signature.h"
+#include "netpgp/writer.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/memory.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/netpgpdigest.h"
+
+/**
+ * \ingroup Core_Create
+ * \param length
+ * \param type
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_ss_header(pgp_output_t *output,
+			size_t length,
+			pgp_content_enum type)
+{
+	return pgp_write_length(output, (unsigned int)length) &&
+		pgp_write_scalar(output, (unsigned)(type -
+				(unsigned)PGP_PTAG_SIG_SUBPKT_BASE), 1);
+}
+
+/*
+ * XXX: the general idea of _fast_ is that it doesn't copy stuff the safe
+ * (i.e. non _fast_) version will, and so will also need to be freed.
+ */
+
+/**
+ * \ingroup Core_Create
+ *
+ * pgp_fast_create_userid() sets id->userid to the given userid.
+ * This is fast because it is only copying a char*. However, if userid
+ * is changed or freed in the future, this could have injurious results.
+ * \param id
+ * \param userid
+ */
+
+void
+pgp_fast_create_userid(uint8_t **id, uint8_t *userid)
+{
+	*id = userid;
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \brief Writes a User Id packet
+ * \param id
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+unsigned
+pgp_write_struct_userid(pgp_output_t *output, const uint8_t *id)
+{
+	return pgp_write_ptag(output, PGP_PTAG_CT_USER_ID) &&
+		pgp_write_length(output, (unsigned)strlen((const char *) id)) &&
+		pgp_write(output, id, (unsigned)strlen((const char *) id));
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \brief Write a User Id packet.
+ * \param userid
+ * \param output
+ *
+ * \return return value from pgp_write_struct_userid()
+ */
+unsigned
+pgp_write_userid(const uint8_t *userid, pgp_output_t *output)
+{
+	return pgp_write_struct_userid(output, userid);
+}
+
+/**
+\ingroup Core_MPI
+*/
+static unsigned
+mpi_length(const BIGNUM *bn)
+{
+	return (unsigned)(2 + (BN_num_bits(bn) + 7) / 8);
+}
+
+static unsigned
+pubkey_length(const pgp_pubkey_t *key)
+{
+	switch (key->alg) {
+	case PGP_PKA_DSA:
+		return mpi_length(key->key.dsa.p) + mpi_length(key->key.dsa.q) +
+			mpi_length(key->key.dsa.g) + mpi_length(key->key.dsa.y);
+
+	case PGP_PKA_RSA:
+		return mpi_length(key->key.rsa.n) + mpi_length(key->key.rsa.e);
+
+	case PGP_PKA_ELGAMAL:
+		return mpi_length(key->key.elgamal.p) +
+               mpi_length(key->key.elgamal.g) +
+               mpi_length(key->key.elgamal.y);
+
+	default:
+		(void) fprintf(stderr,
+			"pubkey_length: unknown key algorithm\n");
+	}
+	return 0;
+}
+
+static unsigned
+seckey_length(const pgp_seckey_t *key)
+{
+	int             len;
+
+	switch (key->pubkey.alg) {
+	case PGP_PKA_DSA:
+		return (unsigned)(mpi_length(key->key.dsa.x) + pubkey_length(&key->pubkey));
+	case PGP_PKA_RSA:
+		len = mpi_length(key->key.rsa.d) + mpi_length(key->key.rsa.p) +
+			mpi_length(key->key.rsa.q) + mpi_length(key->key.rsa.u);
+
+		return (unsigned)(len + pubkey_length(&key->pubkey));
+
+	case PGP_PKA_ELGAMAL:
+		return (unsigned)(
+                mpi_length(key->key.dsa.x) + pubkey_length(&key->pubkey));
+
+	default:
+		(void) fprintf(stderr,
+			"seckey_length: unknown key algorithm\n");
+	}
+	return 0;
+}
+
+/**
+ * \ingroup Core_Create
+ * \param key
+ * \param t
+ * \param n
+ * \param e
+*/
+void
+pgp_fast_create_rsa_pubkey(pgp_pubkey_t *key, time_t t,
+			       BIGNUM *n, BIGNUM *e)
+{
+	key->version = PGP_V4;
+	key->birthtime = t;
+	key->alg = PGP_PKA_RSA;
+	key->key.rsa.n = n;
+	key->key.rsa.e = e;
+}
+
+/*
+ * Note that we support v3 keys here because they're needed for for
+ * verification - the writer doesn't allow them, though
+ */
+static unsigned
+write_pubkey_body(const pgp_pubkey_t *key, pgp_output_t *output)
+{
+	if (!(pgp_write_scalar(output, (unsigned)key->version, 1) &&
+	      pgp_write_scalar(output, (unsigned)key->birthtime, 4))) {
+		return 0;
+	}
+
+	if (key->version != 4 &&
+	    !pgp_write_scalar(output, key->days_valid, 2)) {
+		return 0;
+	}
+
+	if (!pgp_write_scalar(output, (unsigned)key->alg, 1)) {
+		return 0;
+	}
+
+	switch (key->alg) {
+	case PGP_PKA_DSA:
+		return pgp_write_mpi(output, key->key.dsa.p) &&
+			pgp_write_mpi(output, key->key.dsa.q) &&
+			pgp_write_mpi(output, key->key.dsa.g) &&
+			pgp_write_mpi(output, key->key.dsa.y);
+
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		return pgp_write_mpi(output, key->key.rsa.n) &&
+			pgp_write_mpi(output, key->key.rsa.e);
+
+	case PGP_PKA_ELGAMAL:
+		return pgp_write_mpi(output, key->key.elgamal.p) &&
+			pgp_write_mpi(output, key->key.elgamal.g) &&
+			pgp_write_mpi(output, key->key.elgamal.y);
+
+	default:
+		(void) fprintf(stderr,
+			"write_pubkey_body: bad algorithm\n");
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Note that we support v3 keys here because they're needed for
+ * verification.
+ */
+static unsigned
+write_seckey_body(const pgp_seckey_t *key,
+		      const uint8_t *passphrase,
+		      const size_t pplen,
+		      pgp_output_t *output)
+{
+	/* RFC4880 Section 5.5.3 Secret-Key Packet Formats */
+
+	pgp_crypt_t   crypted;
+	pgp_hash_t    hash;
+	unsigned	done = 0;
+	unsigned	i = 0;
+	uint8_t		sesskey[CAST_KEY_LENGTH];
+
+	if (!write_pubkey_body(&key->pubkey, output)) {
+		return 0;
+	}
+    if (!pgp_write_scalar(output, (unsigned)key->s2k_usage, 1)) {
+        return 0;
+    }
+	if (key->s2k_usage != PGP_S2KU_NONE) {
+        if (key->s2k_usage != PGP_S2KU_ENCRYPTED_AND_HASHED) {
+            (void) fprintf(stderr, "write_seckey_body: s2k usage\n");
+            return 0;
+        }
+
+        if (key->alg != PGP_SA_CAST5) {
+            (void) fprintf(stderr, "write_seckey_body: algorithm\n");
+            return 0;
+        }
+        if (!pgp_write_scalar(output, (unsigned)key->alg, 1)) {
+            return 0;
+        }
+
+        if (key->s2k_specifier != PGP_S2KS_SIMPLE &&
+            key->s2k_specifier != PGP_S2KS_SALTED) {
+            /* = 1 \todo could also be iterated-and-salted */
+            (void) fprintf(stderr, "write_seckey_body: s2k spec\n");
+            return 0;
+        }
+        if (!pgp_write_scalar(output, (unsigned)key->s2k_specifier, 1)) {
+            return 0;
+        }
+        if (!pgp_write_scalar(output, (unsigned)key->hash_alg, 1)) {
+            return 0;
+        }
+
+        switch (key->s2k_specifier) {
+        case PGP_S2KS_SIMPLE:
+            /* nothing more to do */
+            break;
+
+        case PGP_S2KS_SALTED:
+            /* 8-octet salt value */
+            pgp_random(__UNCONST(&key->salt[0]), PGP_SALT_SIZE);
+            if (!pgp_write(output, key->salt, PGP_SALT_SIZE)) {
+                return 0;
+            }
+            break;
+
+            /*
+             * \todo case PGP_S2KS_ITERATED_AND_SALTED: // 8-octet salt
+             * value // 1-octet count break;
+             */
+
+        default:
+            (void) fprintf(stderr,
+                "invalid/unsupported s2k specifier %d\n",
+                key->s2k_specifier);
+            return 0;
+        }
+
+        if (!pgp_write(output, &key->iv[0], pgp_block_size(key->alg))) {
+            return 0;
+        }
+
+        /*
+         * create the session key for encrypting the algorithm-specific
+         * fields
+         */
+
+        switch (key->s2k_specifier) {
+        case PGP_S2KS_SIMPLE:
+        case PGP_S2KS_SALTED:
+            /* RFC4880: section 3.7.1.1 and 3.7.1.2 */
+
+            for (done = 0, i = 0; done < CAST_KEY_LENGTH; i++) {
+                unsigned 	hashsize;
+                unsigned 	j;
+                unsigned	needed;
+                unsigned	size;
+                uint8_t		zero = 0;
+                uint8_t		*hashed;
+
+                /* Hard-coded SHA1 for session key */
+                pgp_hash_any(&hash, PGP_HASH_SHA1);
+                hashsize = pgp_hash_size(key->hash_alg);
+                needed = CAST_KEY_LENGTH - done;
+                size = MIN(needed, hashsize);
+                if ((hashed = calloc(1, hashsize)) == NULL) {
+                    (void) fprintf(stderr, "write_seckey_body: bad alloc\n");
+                    return 0;
+                }
+                if (!hash.init(&hash)) {
+                    (void) fprintf(stderr, "write_seckey_body: bad alloc\n");
+                    free(hashed);
+                    return 0;
+                }
+
+                /* preload if iterating  */
+                for (j = 0; j < i; j++) {
+                    /*
+                     * Coverity shows a DEADCODE error on this
+                     * line. This is expected since the hardcoded
+                     * use of SHA1 and CAST5 means that it will
+                     * not used. This will change however when
+                     * other algorithms are supported.
+                     */
+                    hash.add(&hash, &zero, 1);
+                }
+
+                if (key->s2k_specifier == PGP_S2KS_SALTED) {
+                    hash.add(&hash, key->salt, PGP_SALT_SIZE);
+                }
+                hash.add(&hash, passphrase, (unsigned)pplen);
+                hash.finish(&hash, hashed);
+
+                /*
+                 * if more in hash than is needed by session key, use
+                 * the leftmost octets
+                 */
+                (void) memcpy(&sesskey[i * hashsize],
+                        hashed, (unsigned)size);
+                done += (unsigned)size;
+                free(hashed);
+                if (done > CAST_KEY_LENGTH) {
+                    (void) fprintf(stderr,
+                        "write_seckey_body: short add\n");
+                    return 0;
+                }
+            }
+
+            break;
+
+            /*
+             * \todo case PGP_S2KS_ITERATED_AND_SALTED: * 8-octet salt
+             * value * 1-octet count break;
+             */
+
+        default:
+            (void) fprintf(stderr,
+                "invalid/unsupported s2k specifier %d\n",
+                key->s2k_specifier);
+            return 0;
+        }
+
+        /* use this session key to encrypt */
+
+        pgp_crypt_any(&crypted, key->alg);
+        crypted.set_iv(&crypted, key->iv);
+        crypted.set_crypt_key(&crypted, sesskey);
+        pgp_encrypt_init(&crypted);
+
+        if (pgp_get_debug_level(__FILE__)) {
+            hexdump(stderr, "writing: iv=", key->iv, pgp_block_size(key->alg));
+            hexdump(stderr, "key= ", sesskey, CAST_KEY_LENGTH);
+            (void) fprintf(stderr, "\nturning encryption on...\n");
+        }
+        pgp_push_enc_crypt(output, &crypted);
+    }else{
+        pgp_push_sum16_writer(output);
+    }
+
+	switch (key->pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!pgp_write_mpi(output, key->key.rsa.d) ||
+		    !pgp_write_mpi(output, key->key.rsa.p) ||
+		    !pgp_write_mpi(output, key->key.rsa.q) ||
+		    !pgp_write_mpi(output, key->key.rsa.u)) {
+			if (pgp_get_debug_level(__FILE__)) {
+				(void) fprintf(stderr,
+					"4 x mpi not written - problem\n");
+			}
+			return 0;
+		}
+		break;
+	case PGP_PKA_DSA:
+		return pgp_write_mpi(output, key->key.dsa.x);
+	case PGP_PKA_ELGAMAL:
+		return pgp_write_mpi(output, key->key.elgamal.x);
+	default:
+		return 0;
+	}
+
+
+	if (key->s2k_usage != PGP_S2KU_NONE) {
+
+        if (!pgp_write(output, key->checkhash, PGP_CHECKHASH_SIZE)) {
+            return 0;
+        }
+        pgp_writer_pop(output);
+    }else{
+        uint16_t checksum = pgp_pop_sum16_writer(output);
+        if (!pgp_write_scalar(output, checksum, 2)) {
+            return 0;
+        }
+    }
+
+	return 1;
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \brief Writes a Public Key packet
+ * \param key
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+unsigned
+pgp_write_struct_pubkey_ptag(
+        pgp_output_t *output,
+        const pgp_pubkey_t *key,
+	    pgp_content_enum ptag)
+{
+	return pgp_write_ptag(output, ptag) &&
+		pgp_write_length(output, 1 + 4 + 1 + pubkey_length(key)) &&
+		write_pubkey_body(key, output);
+}
+unsigned
+pgp_write_struct_pubkey(pgp_output_t *output, const pgp_pubkey_t *key)
+{
+    return pgp_write_struct_pubkey_ptag(output, key, PGP_PTAG_CT_PUBLIC_KEY);
+}
+
+/**
+   \ingroup HighLevel_KeyWrite
+
+   \brief Writes a transferable PGP public key to the given output stream.
+
+   \param keydata Key to be written
+   \param armoured Flag is set for armoured output
+   \param output Output stream
+
+*/
+
+unsigned
+pgp_write_xfer_key(pgp_output_t *output,
+            const pgp_key_t *key,
+            const unsigned armoured)
+{
+    unsigned          directsigidx = 0;
+    pgp_directsig_t  *directsigp;
+    unsigned          uididx = 0;
+    unsigned          uidsigidx = 0;
+    uint8_t         **uidp;
+    pgp_uidsig_t     *uidsigp;
+    pgp_subkey_t     *subkeyp;
+    unsigned          subkeyidx = 0;
+    unsigned          subkeysigidx = 0;
+    pgp_subkeysig_t  *subkeysigp;
+
+    #if 0 ////// -- we don't need armored keys, EDIT BY MR
+    if (armoured) {
+        pgp_writer_push_armoured(output, PGP_PGP_PUBLIC_KEY_BLOCK);
+    }
+    #endif //////
+
+    /* primary key */
+    if (key->type == PGP_PTAG_CT_PUBLIC_KEY) {
+        if (!pgp_write_struct_pubkey(output, &key->key.pubkey)) {
+            return 0;
+        }
+    }else{
+        if (!pgp_write_struct_seckey(&key->key.seckey, (const uint8_t *)"", 0, output)) {
+            return 0;
+        }
+    }
+
+    directsigp = key->directsigs;
+    for (directsigidx = 0 ; directsigidx < key->directsigc;
+            directsigidx++, directsigp++)
+    {
+        if (!pgp_write(output, directsigp->packet.raw,
+                       (unsigned)directsigp->packet.length)) {
+            return 0;
+        }
+    }
+
+    /* Loop over key's user ids*/
+    uidp = key->uids;
+    for (uididx = 0 ; uididx < key->uidc; uididx++, uidp++)
+    {
+        if (!pgp_write_struct_userid(output, *uidp)) {
+            return 0;
+        }
+        /* Loop over key's user ids sigs */
+        uidsigp = key->uidsigs;
+        for (uidsigidx = 0 ; uidsigidx < key->uidsigc; uidsigidx++, uidsigp++)
+        {
+            /* matching selected user id */
+            if(uidsigp->uid == uididx)
+            {
+                if (!pgp_write(output, uidsigp->packet.raw,
+                               (unsigned)uidsigp->packet.length)) {
+                    return 0;
+                }
+            }
+        }
+    }
+
+    /* TODO attibutes */
+
+    /* Loop over key's subkeys */
+    subkeyp = key->subkeys;
+    for (subkeyidx = 0 ; subkeyidx < key->subkeyc; subkeyidx++, subkeyp++)
+    {
+        if (key->type == PGP_PTAG_CT_PUBLIC_KEY) {
+            if (!pgp_write_struct_pubkey_ptag(
+                    output, &subkeyp->key.pubkey,
+                    PGP_PTAG_CT_PUBLIC_SUBKEY)) {
+                return 0;
+            }
+        }else{
+            if (!pgp_write_struct_seckey_ptag(
+                    /* TODO support passphrase again */
+                    &subkeyp->key.seckey, (const uint8_t *)"", 0, output,
+                    PGP_PTAG_CT_SECRET_SUBKEY)) {
+                return 0;
+            }
+        }
+
+        /* Loop over key's subkeys sigs */
+        subkeysigp = key->subkeysigs;
+        for (subkeysigidx = 0 ; subkeysigidx < key->subkeysigc;
+                subkeysigidx++, subkeysigp++)
+        {
+            /* matching selected subkey */
+            if(subkeysigp->subkey == subkeyidx)
+            {
+                if (!pgp_write(output, subkeysigp->packet.raw,
+                               (unsigned)subkeysigp->packet.length)) {
+                    return 0;
+                }
+            }
+        }
+    }
+
+    if (armoured) {
+        pgp_writer_info_finalise(&output->errors, &output->writer);
+        pgp_writer_pop(output);
+    }
+    return 1;
+}
+
+/**
+   \ingroup HighLevel_KeyWrite
+
+   \brief Writes a transferable PGP secret key to the given output stream.
+
+   \param keydata Key to be written
+   \param passphrase
+   \param pplen
+   \param armoured Flag is set for armoured output
+   \param output Output stream
+
+*/
+/* TODO have encrypted key export again
+unsigned
+pgp_write_xfer_seckey(pgp_output_t *output,
+				const pgp_key_t *key,
+				const uint8_t *passphrase,
+				const size_t pplen,
+				unsigned armoured)
+*/
+
+/**
+ * \ingroup Core_WritePackets
+ * \brief Writes one RSA public key packet.
+ * \param t Creation time
+ * \param n RSA public modulus
+ * \param e RSA public encryption exponent
+ * \param output Writer settings
+ *
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_rsa_pubkey(time_t t, const BIGNUM *n,
+			 const BIGNUM *e,
+			 pgp_output_t *output)
+{
+	pgp_pubkey_t key;
+
+	pgp_fast_create_rsa_pubkey(&key, t, __UNCONST(n), __UNCONST(e));
+	return pgp_write_struct_pubkey(output, &key);
+}
+
+/**
+ * \ingroup Core_Create
+ * \param out
+ * \param key
+ * \param make_packet
+ */
+
+void
+pgp_build_pubkey(pgp_memory_t *out, const pgp_pubkey_t *key,
+		     unsigned make_packet)
+{
+	pgp_output_t *output;
+
+	output = pgp_output_new();
+	pgp_memory_init(out, 128);
+	pgp_writer_set_memory(output, out);
+	write_pubkey_body(key, output);
+	if (make_packet) {
+		pgp_memory_make_packet(out, PGP_PTAG_CT_PUBLIC_KEY);
+	}
+	pgp_output_delete(output);
+}
+
+/**
+ * \ingroup Core_Create
+ *
+ * Create an RSA secret key structure. If a parameter is marked as
+ * [OPTIONAL], then it can be omitted and will be calculated from
+ * other params - or, in the case of e, will default to 0x10001.
+ *
+ * Parameters are _not_ copied, so will be freed if the structure is
+ * freed.
+ *
+ * \param key The key structure to be initialised.
+ * \param t
+ * \param d The RSA parameter d (=e^-1 mod (p-1)(q-1)) [OPTIONAL]
+ * \param p The RSA parameter p
+ * \param q The RSA parameter q (q > p)
+ * \param u The RSA parameter u (=p^-1 mod q) [OPTIONAL]
+ * \param n The RSA public parameter n (=p*q) [OPTIONAL]
+ * \param e The RSA public parameter e */
+
+void
+pgp_fast_create_rsa_seckey(pgp_seckey_t *key, time_t t,
+			     BIGNUM *d, BIGNUM *p, BIGNUM *q, BIGNUM *u,
+			       BIGNUM *n, BIGNUM *e)
+{
+	pgp_fast_create_rsa_pubkey(&key->pubkey, t, n, e);
+
+	/* XXX: calculate optionals */
+	key->key.rsa.d = d;
+	key->key.rsa.p = p;
+	key->key.rsa.q = q;
+	key->key.rsa.u = u;
+
+	key->s2k_usage = PGP_S2KU_NONE;
+
+	/* XXX: sanity check and add errors... */
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \brief Writes a Secret Key packet.
+ * \param key The secret key
+ * \param passphrase The passphrase
+ * \param pplen Length of passphrase
+ * \param output
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_write_struct_seckey_ptag(const pgp_seckey_t *key,
+			    const uint8_t *passphrase,
+			    const size_t pplen,
+			    pgp_output_t *output,
+	            pgp_content_enum ptag)
+{
+	int             length = 0;
+
+	if (key->pubkey.version != 4) {
+		(void) fprintf(stderr,
+			"pgp_write_struct_seckey: public key version\n");
+		return 0;
+	}
+
+	/* Ref: RFC4880 Section 5.5.3 */
+
+	/* pubkey, excluding MPIs */
+	length += 1 + 4 + 1;
+
+	/* s2k usage */
+	length += 1;
+
+	switch (key->s2k_usage) {
+	case PGP_S2KU_NONE:
+		/* nothing to add */
+		break;
+
+	case PGP_S2KU_ENCRYPTED_AND_HASHED:	/* 254 */
+	case PGP_S2KU_ENCRYPTED:	/* 255 */
+
+		/* Ref: RFC4880 Section 3.7 */
+		length += 1;	/* s2k_specifier */
+
+		switch (key->s2k_specifier) {
+		case PGP_S2KS_SIMPLE:
+			length += 1;	/* hash algorithm */
+			break;
+
+		case PGP_S2KS_SALTED:
+			length += 1 + 8;	/* hash algorithm + salt */
+			break;
+
+		case PGP_S2KS_ITERATED_AND_SALTED:
+			length += 1 + 8 + 1;	/* hash algorithm, salt +
+						 * count */
+			break;
+
+		default:
+			(void) fprintf(stderr,
+				"pgp_write_struct_seckey: s2k spec\n");
+			return 0;
+		}
+		break;
+
+	default:
+		(void) fprintf(stderr,
+			"pgp_write_struct_seckey: s2k usage\n");
+		return 0;
+	}
+
+	/* IV */
+	if (key->s2k_usage) {
+		length += pgp_block_size(key->alg);
+	}
+	/* checksum or hash */
+	switch (key->s2k_usage) {
+	case PGP_S2KU_NONE:
+	case PGP_S2KU_ENCRYPTED:
+		length += 2;
+		break;
+
+	case PGP_S2KU_ENCRYPTED_AND_HASHED:
+		length += PGP_CHECKHASH_SIZE;
+		break;
+
+	default:
+		(void) fprintf(stderr,
+			"pgp_write_struct_seckey: s2k cksum usage\n");
+		return 0;
+	}
+
+	/* secret key and public key MPIs */
+	length += (unsigned)seckey_length(key);
+
+	return pgp_write_ptag(output, ptag) &&
+		/* pgp_write_length(output,1+4+1+1+seckey_length(key)+2) && */
+		pgp_write_length(output, (unsigned)length) &&
+		write_seckey_body(key, passphrase, pplen, output);
+}
+
+unsigned
+pgp_write_struct_seckey(const pgp_seckey_t *key,
+			    const uint8_t *passphrase,
+			    const size_t pplen,
+			    pgp_output_t *output)
+{
+    return  pgp_write_struct_seckey_ptag(
+            key, passphrase, pplen, output, PGP_PTAG_CT_SECRET_KEY);
+}
+/**
+ * \ingroup Core_Create
+ *
+ * \brief Create a new pgp_output_t structure.
+ *
+ * \return the new structure.
+ * \note It is the responsiblity of the caller to call pgp_output_delete().
+ * \sa pgp_output_delete()
+ */
+pgp_output_t *
+pgp_output_new(void)
+{
+	return calloc(1, sizeof(pgp_output_t));
+}
+
+/**
+ * \ingroup Core_Create
+ * \brief Delete an pgp_output_t strucut and associated resources.
+ *
+ * Delete an pgp_output_t structure. If a writer is active, then
+ * that is also deleted.
+ *
+ * \param info the structure to be deleted.
+ */
+void
+pgp_output_delete(pgp_output_t *output)
+{
+	pgp_writer_info_delete(&output->writer);
+	free(output);
+}
+
+/**
+ \ingroup Core_Create
+ \brief Calculate the checksum for a session key
+ \param sesskey Session Key to use
+ \param cs Checksum to be written
+ \return 1 if OK; else 0
+*/
+unsigned
+pgp_calc_sesskey_checksum(pgp_pk_sesskey_t *sesskey, uint8_t cs[2])
+{
+	uint32_t   checksum = 0;
+	unsigned    i;
+
+	if (!pgp_is_sa_supported(sesskey->symm_alg)) {
+		return 0;
+	}
+
+	for (i = 0; i < pgp_key_size(sesskey->symm_alg); i++) {
+		checksum += sesskey->key[i];
+	}
+	checksum = checksum % 65536;
+
+	cs[0] = (uint8_t)((checksum >> 8) & 0xff);
+	cs[1] = (uint8_t)(checksum & 0xff);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "nm buf checksum:", cs, 2);
+	}
+	return 1;
+}
+
+static unsigned
+create_unencoded_m_buf(pgp_pk_sesskey_t *sesskey, pgp_crypt_t *cipherinfo, uint8_t *m_buf)
+{
+	unsigned	i;
+
+	/* m_buf is the buffer which will be encoded in PKCS#1 block
+	* encoding to form the "m" value used in the Public Key
+	* Encrypted Session Key Packet as defined in RFC Section 5.1
+	* "Public-Key Encrypted Session Key Packet"
+	 */
+	m_buf[0] = sesskey->symm_alg;
+	for (i = 0; i < cipherinfo->keysize ; i++) {
+		/* XXX - Flexelint - Warning 679: Suspicious Truncation in arithmetic expression combining with pointer */
+		m_buf[1 + i] = sesskey->key[i];
+	}
+
+	return pgp_calc_sesskey_checksum(sesskey,
+				m_buf + 1 + cipherinfo->keysize);
+}
+
+/**
+\ingroup Core_Create
+\brief implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC
+\param M
+\param mLen
+\param pubkey
+\param EM
+\return 1 if OK; else 0
+*/
+unsigned
+encode_m_buf(const uint8_t *M, size_t mLen, const pgp_pubkey_t * pubkey,
+	     uint8_t *EM)
+{
+	unsigned    k;
+	unsigned        i;
+
+	/* implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC */
+	switch (pubkey->alg) {
+	case PGP_PKA_RSA:
+		k = (unsigned)BN_num_bytes(pubkey->key.rsa.n);
+		if (mLen > k - 11) {
+			(void) fprintf(stderr, "encode_m_buf: message too long\n");
+			return 0;
+		}
+		break;
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		k = (unsigned)BN_num_bytes(pubkey->key.elgamal.p);
+		if (mLen > k - 11) {
+			(void) fprintf(stderr, "encode_m_buf: message too long\n");
+			return 0;
+		}
+		break;
+	default:
+		(void) fprintf(stderr, "encode_m_buf: pubkey algorithm\n");
+		return 0;
+	}
+	/* these two bytes defined by RFC */
+	EM[0] = 0x00;
+	EM[1] = 0x02;
+	/* add non-zero random bytes of length k - mLen -3 */
+	for (i = 2; i < (k - mLen) - 1; ++i) {
+		do {
+			pgp_random(EM + i, 1);
+		} while (EM[i] == 0);
+	}
+	if (i < 8 + 2) {
+		(void) fprintf(stderr, "encode_m_buf: bad i len\n");
+		return 0;
+	}
+	EM[i++] = 0;
+	(void) memcpy(EM + i, M, mLen);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "Encoded Message:", EM, mLen);
+	}
+	return 1;
+}
+
+/**
+ \ingroup Core_Create
+\brief Creates an pgp_pk_sesskey_t struct from keydata
+\param key Keydata to use
+\return pgp_pk_sesskey_t struct
+\note It is the caller's responsiblity to free the returned pointer
+\note Currently hard-coded to use CAST5
+\note Currently hard-coded to use RSA
+*/
+pgp_pk_sesskey_t *
+pgp_create_pk_sesskey(pgp_key_t *key, const char *ciphername, pgp_pk_sesskey_t *initial_sesskey)
+{
+	/*
+         * Creates a random session key and encrypts it for the given key
+         *
+         * Encryption used is PK,
+         * can be any, we're hardcoding RSA for now
+         */
+
+    pgp_pubkey_t	*pubkey;
+	pgp_pk_sesskey_t	*sesskey;
+	pgp_symm_alg_t	 cipher;
+	const uint8_t		*id;
+	pgp_crypt_t		 cipherinfo;
+	uint8_t			*unencoded_m_buf;
+	uint8_t			*encoded_m_buf;
+	size_t			 sz_encoded_m_buf;
+
+    pubkey = pgp_key_get_enckey(key, &id);
+    if( pubkey == NULL ) { // avoid crashes if the key cannot encode, EDIT BY MR (bp)
+		return NULL;
+    }
+
+	/* allocate unencoded_m_buf here */
+	(void) memset(&cipherinfo, 0x0, sizeof(cipherinfo));
+	pgp_crypt_any(&cipherinfo,
+		cipher = pgp_str_to_cipher((ciphername) ? ciphername : "cast5"));
+	unencoded_m_buf = calloc(1, cipherinfo.keysize + 1 + 2);
+	if (unencoded_m_buf == NULL) {
+		(void) fprintf(stderr,
+			"pgp_create_pk_sesskey: can't allocate\n");
+		return NULL;
+	}
+	switch(pubkey->alg) {
+	case PGP_PKA_RSA:
+		sz_encoded_m_buf = BN_num_bytes(pubkey->key.rsa.n);
+		break;
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		sz_encoded_m_buf = BN_num_bytes(pubkey->key.elgamal.p);
+		break;
+	default:
+		sz_encoded_m_buf = 0;
+		break;
+	}
+	if ((encoded_m_buf = calloc(1, sz_encoded_m_buf)) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_create_pk_sesskey: can't allocate\n");
+		free(unencoded_m_buf);
+		return NULL;
+	}
+	if ((sesskey = calloc(1, sizeof(*sesskey))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_create_pk_sesskey: can't allocate\n");
+		free(unencoded_m_buf);
+		free(encoded_m_buf);
+		return NULL;
+	}
+	if (key->type != PGP_PTAG_CT_PUBLIC_KEY) {
+		(void) fprintf(stderr,
+			"pgp_create_pk_sesskey: bad type\n");
+		free(unencoded_m_buf);
+		free(encoded_m_buf);
+		free(sesskey);
+		return NULL;
+	}
+	sesskey->version = PGP_PKSK_V3;
+	(void) memcpy(sesskey->key_id, id, sizeof(sesskey->key_id));
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "Encrypting for keyid", id, sizeof(sesskey->key_id));
+	}
+	switch (pubkey->alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		break;
+	default:
+		(void) fprintf(stderr,
+			"pgp_create_pk_sesskey: bad pubkey algorithm\n");
+		free(unencoded_m_buf);
+		free(encoded_m_buf);
+		free(sesskey);
+		return NULL;
+	}
+	sesskey->alg = pubkey->alg;
+
+	sesskey->symm_alg = cipher;
+    if(initial_sesskey){
+        if(initial_sesskey->symm_alg != cipher){
+            return NULL;
+        }
+        memcpy(sesskey->key, initial_sesskey->key, cipherinfo.keysize);
+    }else{
+    	pgp_random(sesskey->key, cipherinfo.keysize);
+    }
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sesskey created", sesskey->key,
+			cipherinfo.keysize + 1 + 2);
+	}
+	if (create_unencoded_m_buf(sesskey, &cipherinfo, &unencoded_m_buf[0]) == 0) {
+		free(unencoded_m_buf);
+		free(encoded_m_buf);
+		free(sesskey);
+		return NULL;
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "uuencoded m buf", unencoded_m_buf, cipherinfo.keysize + 1 + 2);
+	}
+	encode_m_buf(unencoded_m_buf, cipherinfo.keysize + 1 + 2, pubkey, encoded_m_buf);
+
+	/* and encrypt it */
+	switch (key->key.pubkey.alg) {
+	case PGP_PKA_RSA:
+		if (!pgp_rsa_encrypt_mpi(encoded_m_buf, sz_encoded_m_buf, pubkey,
+				&sesskey->params)) {
+			free(unencoded_m_buf);
+			free(encoded_m_buf);
+			free(sesskey);
+			return NULL;
+		}
+		break;
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		if (!pgp_elgamal_encrypt_mpi(encoded_m_buf, sz_encoded_m_buf, pubkey,
+				&sesskey->params)) {
+			free(unencoded_m_buf);
+			free(encoded_m_buf);
+			free(sesskey);
+			return NULL;
+		}
+		break;
+	default:
+		/* will not get here - for lint only */
+		break;
+	}
+	free(unencoded_m_buf);
+	free(encoded_m_buf);
+	return sesskey;
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Writes Public Key Session Key packet
+\param info Write settings
+\param pksk Public Key Session Key to write out
+\return 1 if OK; else 0
+*/
+unsigned
+pgp_write_pk_sesskey(pgp_output_t *output, pgp_pk_sesskey_t *pksk)
+{
+	/* XXX - Flexelint - Pointer parameter 'pksk' (line 1076) could be declared as pointing to const */
+	if (pksk == NULL) {
+		(void) fprintf(stderr,
+			"pgp_write_pk_sesskey: NULL pksk\n");
+		return 0;
+	}
+	switch (pksk->alg) {
+	case PGP_PKA_RSA:
+		return pgp_write_ptag(output, PGP_PTAG_CT_PK_SESSION_KEY) &&
+			pgp_write_length(output, (unsigned)(1 + 8 + 1 +
+				BN_num_bytes(pksk->params.rsa.encrypted_m) + 2)) &&
+			pgp_write_scalar(output, (unsigned)pksk->version, 1) &&
+			pgp_write(output, pksk->key_id, 8) &&
+			pgp_write_scalar(output, (unsigned)pksk->alg, 1) &&
+			pgp_write_mpi(output, pksk->params.rsa.encrypted_m)
+			/* ??	&& pgp_write_scalar(output, 0, 2); */
+			;
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		return pgp_write_ptag(output, PGP_PTAG_CT_PK_SESSION_KEY) &&
+			pgp_write_length(output, (unsigned)(1 + 8 + 1 +
+				BN_num_bytes(pksk->params.elgamal.g_to_k) + 2 +
+				BN_num_bytes(pksk->params.elgamal.encrypted_m) + 2)) &&
+			pgp_write_scalar(output, (unsigned)pksk->version, 1) &&
+			pgp_write(output, pksk->key_id, 8) &&
+			pgp_write_scalar(output, (unsigned)pksk->alg, 1) &&
+			pgp_write_mpi(output, pksk->params.elgamal.g_to_k) &&
+			pgp_write_mpi(output, pksk->params.elgamal.encrypted_m)
+			/* ??	&& pgp_write_scalar(output, 0, 2); */
+			;
+	default:
+		(void) fprintf(stderr,
+			"pgp_write_pk_sesskey: bad algorithm\n");
+		return 0;
+	}
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Writes MDC packet
+\param hashed Hash for MDC
+\param output Write settings
+\return 1 if OK; else 0
+*/
+
+unsigned
+pgp_write_mdc(pgp_output_t *output, const uint8_t *hashed)
+{
+	/* write it out */
+	return pgp_write_ptag(output, PGP_PTAG_CT_MDC) &&
+		pgp_write_length(output, PGP_SHA1_HASH_SIZE) &&
+		pgp_write(output, hashed, PGP_SHA1_HASH_SIZE);
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Writes Literal Data packet from buffer
+\param data Buffer to write out
+\param maxlen Max length of buffer
+\param type Literal Data Type
+\param output Write settings
+\return 1 if OK; else 0
+*/
+unsigned
+pgp_write_litdata(pgp_output_t *output,
+			const uint8_t *data,
+			const int maxlen,
+			const pgp_litdata_enum type)
+{
+	/*
+         * RFC4880 does not specify a meaning for filename or date.
+         * It is implementation-dependent.
+         * We will not implement them.
+         */
+	/* \todo do we need to check text data for <cr><lf> line endings ? */
+	return pgp_write_ptag(output, PGP_PTAG_CT_LITDATA) &&
+		pgp_write_length(output, (unsigned)(1 + 1 + 4 + maxlen)) &&
+		pgp_write_scalar(output, (unsigned)type, 1) &&
+		pgp_write_scalar(output, 0, 1) &&
+		pgp_write_scalar(output, 0, 4) &&
+		pgp_write(output, data, (unsigned)maxlen);
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Writes Literal Data packet from contents of file
+\param filename Name of file to read from
+\param type Literal Data Type
+\param output Write settings
+\return 1 if OK; else 0
+*/
+
+unsigned
+pgp_fileread_litdata(const char *filename,
+				 const pgp_litdata_enum type,
+				 pgp_output_t *output)
+{
+	pgp_memory_t	*mem;
+	unsigned   	 ret;
+	int		 len;
+
+	mem = pgp_memory_new();
+	if (!pgp_mem_readfile(mem, filename)) {
+		(void) fprintf(stderr, "pgp_mem_readfile of '%s' failed\n", filename);
+		return 0;
+	}
+	len = (int)pgp_mem_len(mem);
+	ret = pgp_write_litdata(output, pgp_mem_data(mem), len, type);
+	pgp_memory_free(mem);
+	return ret;
+}
+
+/**
+   \ingroup HighLevel_General
+
+   \brief Writes contents of buffer into file
+
+   \param filename Filename to write to
+   \param buf Buffer to write to file
+   \param len Size of buffer
+   \param overwrite Flag to set whether to overwrite an existing file
+   \return 1 if OK; 0 if error
+*/
+
+int
+pgp_filewrite(const char *filename, const char *buf,
+			const size_t len, const unsigned overwrite)
+{
+	int		flags;
+	int		fd;
+
+	flags = O_WRONLY | O_CREAT;
+	if (overwrite) {
+		flags |= O_TRUNC;
+	} else {
+		flags |= O_EXCL;
+	}
+#ifdef O_BINARY
+	flags |= O_BINARY;
+#endif
+	fd = open(filename, flags, 0600);
+	if (fd < 0) {
+		(void) fprintf(stderr, "can't open '%s'\n", filename);
+		return 0;
+	}
+	if (write(fd, buf, len) != (int)len) {
+		(void) close(fd);
+		return 0;
+	}
+
+	return (close(fd) == 0);
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Write Symmetrically Encrypted packet
+\param data Data to encrypt
+\param len Length of data
+\param output Write settings
+\return 1 if OK; else 0
+\note Hard-coded to use AES256
+*/
+unsigned
+pgp_write_symm_enc_data(const uint8_t *data,
+				       const int len,
+				       pgp_output_t * output)
+{
+	pgp_crypt_t	crypt_info;
+	uint8_t		*encrypted = (uint8_t *) NULL;
+	size_t		encrypted_sz;
+	int             done = 0;
+
+	/* \todo assume AES256 for now */
+	pgp_crypt_any(&crypt_info, PGP_SA_AES_256);
+	pgp_encrypt_init(&crypt_info);
+
+	encrypted_sz = (size_t)(len + crypt_info.blocksize + 2);
+	if ((encrypted = calloc(1, encrypted_sz)) == NULL) {
+		(void) fprintf(stderr, "can't allocate %" PRIsize "d\n",
+			encrypted_sz);
+		return 0;
+	}
+
+	done = (int)pgp_encrypt_se(&crypt_info, encrypted, data, (unsigned)len);
+	if (done != len) {
+		(void) fprintf(stderr,
+			"pgp_write_symm_enc_data: done != len\n");
+		return 0;
+	}
+
+	return pgp_write_ptag(output, PGP_PTAG_CT_SE_DATA) &&
+		pgp_write_length(output, (unsigned)(1 + encrypted_sz)) &&
+		pgp_write(output, data, (unsigned)len);
+}
+
+/**
+\ingroup Core_WritePackets
+\brief Write a One Pass Signature packet
+\param seckey Secret Key to use
+\param hash_alg Hash Algorithm to use
+\param sig_type Signature type
+\param output Write settings
+\return 1 if OK; else 0
+*/
+unsigned
+pgp_write_one_pass_sig(pgp_output_t *output,
+			const pgp_seckey_t *seckey,
+			const pgp_hash_alg_t hash_alg,
+			const pgp_sig_type_t sig_type)
+{
+	uint8_t   keyid[PGP_KEY_ID_SIZE];
+
+	pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, PGP_HASH_SHA1); /* XXX - hardcoded */
+	return pgp_write_ptag(output, PGP_PTAG_CT_1_PASS_SIG) &&
+		pgp_write_length(output, 1 + 1 + 1 + 1 + 8 + 1) &&
+		pgp_write_scalar(output, 3, 1)	/* version */ &&
+		pgp_write_scalar(output, (unsigned)sig_type, 1) &&
+		pgp_write_scalar(output, (unsigned)hash_alg, 1) &&
+		pgp_write_scalar(output, (unsigned)seckey->pubkey.alg, 1) &&
+		pgp_write(output, keyid, 8) &&
+		pgp_write_scalar(output, 1, 1);
+}
diff --git a/netpgp/crypto.c b/netpgp/crypto.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d2f976c28ae227b370143be1c73f42baea3e688
--- /dev/null
+++ b/netpgp/crypto.c
@@ -0,0 +1,763 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#include "netpgp/types.h"
+#include "netpgp/crypto.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/memory.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/signature.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/validate.h"
+
+/**
+\ingroup Core_MPI
+\brief Decrypt and unencode MPI
+\param buf Buffer in which to write decrypted unencoded MPI
+\param buflen Length of buffer
+\param encmpi
+\param seckey
+\return length of MPI
+\note only RSA at present
+*/
+int
+pgp_decrypt_decode_mpi(uint8_t *buf,
+				unsigned buflen,
+				const BIGNUM *g_to_k,
+				const BIGNUM *encmpi,
+				const pgp_seckey_t *seckey)
+{
+	unsigned        mpisize;
+	uint8_t		encmpibuf[NETPGP_BUFSIZ];
+	uint8_t		mpibuf[NETPGP_BUFSIZ];
+	uint8_t		gkbuf[NETPGP_BUFSIZ];
+	int             i;
+	int             n;
+
+	mpisize = (unsigned)BN_num_bytes(encmpi);
+	/* MPI can't be more than 65,536 */
+	if (mpisize > sizeof(encmpibuf)) {
+		(void) fprintf(stderr, "mpisize too big %u\n", mpisize);
+		return -1;
+	}
+	switch (seckey->pubkey.alg) {
+	case PGP_PKA_RSA:
+		BN_bn2bin(encmpi, encmpibuf);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "encrypted", encmpibuf, 16);
+		}
+		n = pgp_rsa_private_decrypt(mpibuf, encmpibuf,
+					(unsigned)(BN_num_bits(encmpi) + 7) / 8,
+					&seckey->key.rsa, &seckey->pubkey.key.rsa);
+		if (n == -1) {
+			(void) fprintf(stderr, "ops_rsa_private_decrypt failure\n");
+			return -1;
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "decrypted", mpibuf, 16);
+		}
+		if (n <= 0) {
+			return -1;
+		}
+		/* Decode EME-PKCS1_V1_5 (RFC 2437). */
+		if (mpibuf[0] != 0 || mpibuf[1] != 2) {
+			return -1;
+		}
+		/* Skip the random bytes. */
+		for (i = 2; i < n && mpibuf[i]; ++i) {
+		}
+		if (i == n || i < 10) {
+			return -1;
+		}
+		/* Skip the zero */
+		i += 1;
+		/* this is the unencoded m buf */
+		if ((unsigned) (n - i) <= buflen) {
+			(void) memcpy(buf, mpibuf + i, (unsigned)(n - i)); /* XXX - Flexelint */
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "decoded m", buf, (size_t)(n - i));
+		}
+		return n - i;
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		(void) BN_bn2bin(g_to_k, gkbuf);
+		(void) BN_bn2bin(encmpi, encmpibuf);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "encrypted", encmpibuf, 16);
+		}
+		n = pgp_elgamal_private_decrypt(mpibuf, gkbuf, encmpibuf,
+					(unsigned)BN_num_bytes(encmpi),
+					&seckey->key.elgamal, &seckey->pubkey.key.elgamal);
+		if (n == -1) {
+			(void) fprintf(stderr, "ops_elgamal_private_decrypt failure\n");
+			return -1;
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "decrypted", mpibuf, 16);
+		}
+		if (n <= 0) {
+			return -1;
+		}
+		/* Decode EME-PKCS1_V1_5 (RFC 2437). */
+		if (mpibuf[0] != 2) {
+			fprintf(stderr, "mpibuf mismatch\n");
+			return -1;
+		}
+		/* Skip the random bytes. */
+		for (i = 1; i < n && mpibuf[i]; ++i) {
+		}
+		if (i == n || i < 10) {
+			fprintf(stderr, "175 n %d\n", n);
+			return -1;
+		}
+		/* Skip the zero */
+		i += 1;
+		/* this is the unencoded m buf */
+		if ((unsigned) (n - i) <= buflen) {
+			(void) memcpy(buf, mpibuf + i, (unsigned)(n - i)); /* XXX - Flexelint */
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "decoded m", buf, (size_t)(n - i));
+		}
+		return n - i;
+	default:
+		(void) fprintf(stderr, "pubkey algorithm wrong\n");
+		return -1;
+	}
+}
+
+/**
+\ingroup Core_MPI
+\brief RSA-encrypt an MPI
+*/
+unsigned
+pgp_rsa_encrypt_mpi(const uint8_t *encoded_m_buf,
+		    const size_t sz_encoded_m_buf,
+		    const pgp_pubkey_t * pubkey,
+		    pgp_pk_sesskey_params_t * skp)
+{
+
+	uint8_t   encmpibuf[NETPGP_BUFSIZ];
+	int             n;
+
+	if (sz_encoded_m_buf != (size_t)BN_num_bytes(pubkey->key.rsa.n)) {
+		(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
+		return 0;
+	}
+
+	n = pgp_rsa_public_encrypt(encmpibuf, encoded_m_buf,
+				sz_encoded_m_buf, &pubkey->key.rsa);
+	if (n == -1) {
+		(void) fprintf(stderr, "pgp_rsa_public_encrypt failure\n");
+		return 0;
+	}
+
+	if (n <= 0)
+		return 0;
+
+	skp->rsa.encrypted_m = BN_bin2bn(encmpibuf, n, NULL);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "encrypted mpi", encmpibuf, 16);
+	}
+	return 1;
+}
+
+/**
+\ingroup Core_MPI
+\brief Elgamal-encrypt an MPI
+*/
+unsigned
+pgp_elgamal_encrypt_mpi(const uint8_t *encoded_m_buf,
+		    const size_t sz_encoded_m_buf,
+		    const pgp_pubkey_t * pubkey,
+		    pgp_pk_sesskey_params_t * skp)
+{
+
+	uint8_t   encmpibuf[NETPGP_BUFSIZ];
+	uint8_t   g_to_k[NETPGP_BUFSIZ];
+	int             n;
+
+	if (sz_encoded_m_buf != (size_t)BN_num_bytes(pubkey->key.elgamal.p)) {
+		(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
+		return 0;
+	}
+
+	n = pgp_elgamal_public_encrypt(g_to_k, encmpibuf, encoded_m_buf,
+				sz_encoded_m_buf, &pubkey->key.elgamal);
+	if (n == -1) {
+		(void) fprintf(stderr, "pgp_elgamal_public_encrypt failure\n");
+		return 0;
+	}
+
+	if (n <= 0)
+		return 0;
+
+	skp->elgamal.g_to_k = BN_bin2bn(g_to_k, n / 2, NULL);
+	skp->elgamal.encrypted_m = BN_bin2bn(encmpibuf, n / 2, NULL);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "encrypted mpi", encmpibuf, 16);
+	}
+	return 1;
+}
+
+static pgp_cb_ret_t
+write_parsed_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	*content = &pkt->u;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		printf("write_parsed_cb: ");
+		//pgp_print_packet(&cbinfo->printstate, pkt);
+	}
+	if (pkt->tag != PGP_PTAG_CT_UNARMOURED_TEXT && cbinfo->printstate.skipping) {
+		puts("...end of skip");
+		cbinfo->printstate.skipping = 0;
+	}
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_UNARMOURED_TEXT:
+		printf("PGP_PTAG_CT_UNARMOURED_TEXT\n");
+		if (!cbinfo->printstate.skipping) {
+			puts("Skipping...");
+			cbinfo->printstate.skipping = 1;
+		}
+		if (fwrite(content->unarmoured_text.data, 1,
+		       content->unarmoured_text.length, stdout) != content->unarmoured_text.length) {
+			fprintf(stderr, "unable to write unarmoured text data\n");
+			cbinfo->printstate.skipping = 1;
+		}
+		break;
+
+	case PGP_PTAG_CT_PK_SESSION_KEY:
+		return pgp_pk_sesskey_cb(pkt, cbinfo);
+
+	case PGP_GET_SECKEY:
+		if (cbinfo->sshseckey) {
+			*content->get_seckey.seckey = cbinfo->sshseckey;
+			return PGP_KEEP_MEMORY;
+		}
+		return pgp_get_seckey_cb(pkt, cbinfo);
+
+	case PGP_GET_PASSPHRASE:
+		if (cbinfo->cryptinfo.getpassphrase) {
+            return cbinfo->cryptinfo.getpassphrase(pkt, cbinfo);
+        }
+        break;
+
+	case PGP_PTAG_CT_LITDATA_BODY:
+		return pgp_litdata_cb(pkt, cbinfo);
+
+	case PGP_PTAG_CT_ARMOUR_HEADER:
+	case PGP_PTAG_CT_ARMOUR_TRAILER:
+	case PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
+	case PGP_PTAG_CT_COMPRESSED:
+	case PGP_PTAG_CT_LITDATA_HEADER:
+	case PGP_PTAG_CT_SE_IP_DATA_BODY:
+	case PGP_PTAG_CT_SE_IP_DATA_HEADER:
+	case PGP_PTAG_CT_SE_DATA_BODY:
+	case PGP_PTAG_CT_SE_DATA_HEADER:
+		/* Ignore these packets  */
+		/* They're handled in parse_packet() */
+		/* and nothing else needs to be done */
+		break;
+
+	default:
+		if (pgp_get_debug_level(__FILE__)) {
+			fprintf(stderr, "Unexpected packet tag=%d (0x%x)\n",
+				pkt->tag,
+				pkt->tag);
+		}
+		break;
+	}
+
+	return PGP_RELEASE_MEMORY;
+}
+
+/**
+\ingroup HighLevel_Crypto
+Encrypt a file
+\param infile Name of file to be encrypted
+\param outfile Name of file to write to. If NULL, name is constructed from infile
+\param pubkey Public Key to encrypt file for
+\param use_armour Write armoured text, if set
+\param allow_overwrite Allow output file to be overwrwritten if it exists
+\return 1 if OK; else 0
+*/
+#if 0 //////
+unsigned
+pgp_encrypt_file(pgp_io_t *io,
+			const char *infile,
+			const char *outfile,
+			const pgp_key_t *key,
+			const unsigned use_armour,
+			const unsigned allow_overwrite,
+			const char *cipher)
+{
+	pgp_output_t	*output;
+	pgp_memory_t	*inmem;
+    pgp_keyring_t *rcpts;
+	int		 fd_out;
+
+	__PGP_USED(io);
+	inmem = pgp_memory_new();
+	if (!pgp_mem_readfile(inmem, infile)) {
+		return 0;
+	}
+	fd_out = pgp_setup_file_write(&output, outfile, allow_overwrite);
+	if (fd_out < 0) {
+		pgp_memory_free(inmem);
+		return 0;
+	}
+
+	/* set armoured/not armoured here */
+	if (use_armour) {
+		pgp_writer_push_armor_msg(output);
+	}
+
+    if ((rcpts = calloc(1, sizeof(*rcpts))) == NULL) {
+		(void) fprintf(io->errs,
+			"netpgp_encrypt_buf: out of memory to create recipients list\n");
+		return 0;
+	}
+    pgp_keyring_add(rcpts, key);
+    if(rcpts->keys == NULL){
+		(void) fprintf(io->errs,
+			"netpgp_encrypt_buf: out of memory to add recipient\n");
+		return 0;
+	}
+	/* Push the encrypted writer */
+	if (!pgp_push_enc_se_ip(output, rcpts, cipher, 0)) {
+		pgp_memory_free(inmem);
+		return 0;
+	}
+    pgp_keyring_free(rcpts);
+
+	/* This does the writing */
+	pgp_write(output, pgp_mem_data(inmem), (unsigned)pgp_mem_len(inmem));
+
+	/* tidy up */
+	pgp_memory_free(inmem);
+	pgp_teardown_file_write(output, fd_out);
+
+	return 1;
+}
+#endif //////
+
+/* encrypt the contents of the input buffer, and return the mem structure */
+pgp_memory_t *
+pgp_encrypt_buf(pgp_io_t *io,
+			const void *input,
+			const size_t insize,
+			const pgp_keyring_t *pubkeys,
+			const unsigned use_armour,
+			const char *cipher,
+            unsigned raw)
+{
+	pgp_output_t	*output;
+	pgp_memory_t	*outmem;
+
+	__PGP_USED(io);
+	if (input == NULL) {
+		(void) fprintf(io->errs,
+			"pgp_encrypt_buf: null memory\n");
+		return 0;
+	}
+
+	pgp_setup_memory_write(&output, &outmem, insize);
+
+	/* set armoured/not armoured here */
+	if (use_armour) {
+		pgp_writer_push_armor_msg(output);
+	}
+
+	/* Push the encrypted writer */
+	pgp_push_enc_se_ip(output, pubkeys, cipher, raw);
+
+	/* This does the writing */
+	pgp_write(output, input, (unsigned)insize);
+
+	/* tidy up */
+	pgp_writer_close(output);
+	pgp_output_delete(output);
+
+	return outmem;
+}
+
+/**
+   \ingroup HighLevel_Crypto
+   \brief Decrypt a file.
+   \param infile Name of file to be decrypted
+   \param outfile Name of file to write to. If NULL, the filename is constructed from the input filename, following GPG conventions.
+   \param keyring Keyring to use
+   \param use_armour Expect armoured text, if set
+   \param allow_overwrite Allow output file to overwritten, if set.
+   \param getpassfunc Callback to use to get passphrase
+*/
+#if 0 //////
+unsigned
+pgp_decrypt_file(pgp_io_t *io,
+			const char *infile,
+			const char *outfile,
+			pgp_keyring_t *secring,
+			pgp_keyring_t *pubring,
+			const unsigned use_armour,
+			const unsigned allow_overwrite,
+			const unsigned sshkeys,
+			void *passfp,
+			int numtries,
+			pgp_cbfunc_t *getpassfunc)
+{
+	pgp_stream_t	*parse = NULL;
+	const int	 printerrors = 1;
+	char		*filename = NULL;
+	int		 fd_in;
+	int		 fd_out;
+
+	/* setup for reading from given input file */
+	fd_in = pgp_setup_file_read(io, &parse, infile,
+				    NULL,
+				    write_parsed_cb,
+				    0);
+	if (fd_in < 0) {
+		perror(infile);
+		return 0;
+	}
+	/* setup output filename */
+	if (outfile) {
+		fd_out = pgp_setup_file_write(&parse->cbinfo.output, outfile,
+				allow_overwrite);
+		if (fd_out < 0) {
+			perror(outfile);
+			pgp_teardown_file_read(parse, fd_in);
+			return 0;
+		}
+	} else {
+		const int	suffixlen = 4;
+		const char     *suffix = infile + strlen(infile) - suffixlen;
+		unsigned	filenamelen;
+
+		if (strcmp(suffix, ".gpg") == 0 ||
+		    strcmp(suffix, ".asc") == 0) {
+			filenamelen = (unsigned)(strlen(infile) - strlen(suffix));
+			if ((filename = calloc(1, filenamelen + 1)) == NULL) {
+				(void) fprintf(stderr, "can't allocate %" PRIsize "d bytes\n",
+					(size_t)(filenamelen + 1));
+				return 0;
+			}
+			(void) strncpy(filename, infile, filenamelen);
+			filename[filenamelen] = 0x0;
+		}
+
+		fd_out = pgp_setup_file_write(&parse->cbinfo.output,
+					filename, allow_overwrite);
+		if (fd_out < 0) {
+			perror(filename);
+			free(filename);
+			pgp_teardown_file_read(parse, fd_in);
+			return 0;
+		}
+	}
+
+	/* \todo check for suffix matching armour param */
+
+	/* setup for writing decrypted contents to given output file */
+
+	/* setup keyring and passphrase callback */
+	parse->cbinfo.cryptinfo.secring = secring;
+	parse->cbinfo.passfp = passfp;
+	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
+	parse->cbinfo.cryptinfo.pubring = pubring;
+	parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
+	parse->cbinfo.numtries = numtries;
+
+	/* Set up armour/passphrase options */
+	if (use_armour) {
+		pgp_reader_push_dearmour(parse);
+	}
+
+	/* Do it */
+	pgp_parse(parse, printerrors);
+
+	/* Unsetup */
+	if (use_armour) {
+		pgp_reader_pop_dearmour(parse);
+	}
+
+	/* if we didn't get the passphrase, unlink output file */
+	if (!parse->cbinfo.gotpass) {
+		(void) unlink((filename) ? filename : outfile);
+	}
+
+	if (filename) {
+		pgp_teardown_file_write(parse->cbinfo.output, fd_out);
+		free(filename);
+	}
+	pgp_teardown_file_read(parse, fd_in);
+	/* \todo cleardown crypt */
+
+	return 1;
+}
+#endif //////
+
+/* decrypt an area of memory */
+pgp_memory_t *
+pgp_decrypt_buf(pgp_io_t *io,
+			const void *input,
+			const size_t insize,
+			pgp_keyring_t *secring,
+			pgp_keyring_t *pubring,
+			const unsigned use_armour,
+			const unsigned sshkeys,
+			void *passfp,
+			int numtries,
+			pgp_cbfunc_t *getpassfunc)
+{
+	pgp_stream_t	*parse = NULL;
+	pgp_memory_t	*outmem;
+	pgp_memory_t	*inmem;
+	const int	 printerrors = 1;
+
+	if (input == NULL) {
+		(void) fprintf(io->errs,
+			"pgp_encrypt_buf: null memory\n");
+		return 0;
+	}
+
+	inmem = pgp_memory_new();
+	pgp_memory_add(inmem, input, insize);
+
+	/* set up to read from memory */
+	pgp_setup_memory_read(io, &parse, inmem,
+				    NULL,
+				    write_parsed_cb,
+				    0);
+
+	/* setup for writing decrypted contents to given output file */
+	pgp_setup_memory_write(&parse->cbinfo.output, &outmem, insize);
+
+	/* setup keyring and passphrase callback */
+	parse->cbinfo.cryptinfo.secring = secring;
+	parse->cbinfo.cryptinfo.pubring = pubring;
+	parse->cbinfo.passfp = passfp;
+	parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
+	parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
+	parse->cbinfo.numtries = numtries;
+
+	/* Set up armour/passphrase options */
+	if (use_armour) {
+		pgp_reader_push_dearmour(parse);
+	}
+
+	/* Do it */
+	pgp_parse(parse, printerrors);
+
+	/* Unsetup */
+	if (use_armour) {
+		pgp_reader_pop_dearmour(parse);
+	}
+
+	/* tidy up */
+	pgp_teardown_memory_read(parse, inmem);
+
+	pgp_writer_close(parse->cbinfo.output);
+	pgp_output_delete(parse->cbinfo.output);
+
+	/* if we didn't get the passphrase, return NULL */
+	return (parse->cbinfo.gotpass) ? outmem : NULL;
+}
+
+/* Special callback for decrypt and validate */
+static pgp_cb_ret_t
+pgp_decrypt_and_validate_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+    pgp_cb_ret_t ret_write_cb = PGP_RELEASE_MEMORY;
+    pgp_cb_ret_t ret_validate_cb = PGP_RELEASE_MEMORY;
+
+    ret_write_cb = write_parsed_cb(pkt, cbinfo);
+
+    /* Filter pkt sent to validate callback */
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_LITDATA_BODY:
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
+	case PGP_PTAG_CT_SIGNATURE:	/* V3 sigs */
+	case PGP_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
+        ret_validate_cb = validate_data_cb(pkt, cbinfo);
+		break;
+	default:
+		break;
+    }
+
+    /* Only validate_data_cb takes some arguments.
+     * Otherwise, stacked callbacks would have been necessary
+     */
+    return (ret_write_cb == PGP_KEEP_MEMORY ||
+            ret_validate_cb == PGP_KEEP_MEMORY) ?
+           PGP_KEEP_MEMORY : PGP_RELEASE_MEMORY;
+}
+
+/* decrypt and validate an area of memory */
+pgp_memory_t *
+pgp_decrypt_and_validate_buf(pgp_io_t *io,
+			pgp_validation_t *result,
+			const void *input,
+			const size_t insize,
+			pgp_keyring_t *secring,
+			pgp_keyring_t *pubring,
+			const unsigned use_armour,
+            key_id_t **recipients_key_ids,
+            unsigned *recipients_count)
+{
+    // historical code bloat...
+    const unsigned sshkeys = 0;
+    void *passfp = NULL;
+    int numtries = -1;
+    pgp_cbfunc_t *getpassfunc = NULL;
+
+    validate_data_cb_t	 validation;
+	pgp_stream_t	*stream = NULL;
+	pgp_memory_t	*outmem;
+	pgp_memory_t	*inmem;
+	const int	 printerrors = 1;
+
+	if (input == NULL) {
+		(void) fprintf(io->errs,
+			"pgp_encrypt_buf: null memory\n");
+		return 0;
+	}
+
+	inmem = pgp_memory_new();
+	pgp_memory_add(inmem, input, insize);
+
+	/* set up to read from memory */
+	pgp_setup_memory_read(io, &stream, inmem,
+				    &validation,
+				    pgp_decrypt_and_validate_cb,
+				    1);
+
+	/* Set verification reader and handling options */
+	(void) memset(&validation, 0x0, sizeof(validation));
+	validation.result = result;
+	validation.keyring = pubring;
+	validation.mem = pgp_memory_new();
+	pgp_memory_init(validation.mem, 128);
+
+	/* setup for writing decrypted contents */
+	pgp_setup_memory_write(&stream->cbinfo.output, &outmem, insize);
+
+	/* setup keyring and passphrase callback */
+	stream->cbinfo.cryptinfo.secring = secring;
+	stream->cbinfo.cryptinfo.pubring = pubring;
+	stream->cbinfo.passfp = passfp;
+	stream->cbinfo.cryptinfo.getpassphrase = getpassfunc;
+	stream->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
+	stream->cbinfo.numtries = numtries;
+
+	/* Set up armour */
+	if (use_armour) {
+		pgp_reader_push_dearmour(stream);
+	}
+
+	/* Do it */
+	pgp_parse(stream, printerrors);
+
+	/* Unsetup */
+	if (use_armour) {
+		pgp_reader_pop_dearmour(stream);
+	}
+
+    *recipients_count = stream->cbinfo.cryptinfo.recipients_key_idsc;
+    if (*recipients_count == 0) {
+        *recipients_key_ids = NULL;
+    } else {
+        *recipients_key_ids = calloc(sizeof(key_id_t),*recipients_count);
+        if( *recipients_key_ids != NULL)
+        {
+            memcpy(*recipients_key_ids,
+                  stream->cbinfo.cryptinfo.recipients_key_idss,
+                  sizeof(key_id_t) * *recipients_count);
+        }
+    }
+
+    if( *recipients_key_ids == NULL)
+    {
+        pgp_memory_free(outmem);
+        *recipients_count = 0;
+        outmem = NULL;
+    }
+
+	/* tidy up */
+    pgp_writer_close(stream->cbinfo.output);
+    pgp_output_delete(stream->cbinfo.output);
+
+	pgp_teardown_memory_read(stream, inmem);
+	pgp_memory_free(validation.mem);
+
+	return outmem;
+}
diff --git a/netpgp/keyring.c b/netpgp/keyring.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a33921e7be110f73fa99b15a9210264660f0aae
--- /dev/null
+++ b/netpgp/keyring.c
@@ -0,0 +1,1647 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "netpgp/types.h"
+#include "netpgp/keyring.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/signature.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/packet.h"
+#include "netpgp/crypto.h"
+#include "netpgp/validate.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/netpgpdigest.h"
+
+
+
+/**
+   \ingroup HighLevel_Keyring
+
+   \brief Creates a new pgp_key_t struct
+
+   \return A new pgp_key_t struct, initialised to zero.
+
+   \note The returned pgp_key_t struct must be freed after use with pgp_keydata_free.
+*/
+
+pgp_key_t  *
+pgp_keydata_new(void)
+{
+	return calloc(1, sizeof(pgp_key_t));
+}
+
+
+/**
+ \ingroup HighLevel_Keyring
+
+ \brief Frees key's allocated memory
+
+ \param keydata Key to be freed.
+
+ \note This does not free the keydata itself, but any other memory alloc-ed by it.
+*/
+void
+pgp_key_free(pgp_key_t *key)
+{
+	unsigned        n;
+
+	if (key->type == PGP_PTAG_CT_PUBLIC_KEY) {
+		pgp_pubkey_free(&key->key.pubkey);
+	} else {
+		pgp_seckey_free(&key->key.seckey);
+	}
+
+	for (n = 0; n < key->directsigc; ++n) {
+        pgp_free_sig_info(&key->directsigs[n].siginfo);
+		pgp_subpacket_free(&key->directsigs[n].packet);
+	}
+    FREE_ARRAY(key, directsig);
+
+	for (n = 0; n < key->uidc; ++n) {
+		pgp_userid_free(&key->uids[n]);
+	}
+    FREE_ARRAY(key, uid);
+
+	for (n = 0; n < key->uidsigc; ++n) {
+        pgp_free_sig_info(&key->uidsigs[n].siginfo);
+		pgp_subpacket_free(&key->uidsigs[n].packet);
+	}
+    FREE_ARRAY(key, uidsig);
+
+	for (n = 0; n < key->subkeyc; ++n) {
+        if (key->type == PGP_PTAG_CT_PUBLIC_KEY) {
+            pgp_pubkey_free(&key->subkeys[n].key.pubkey);
+        } else {
+            pgp_seckey_free(&key->subkeys[n].key.seckey);
+        }
+	}
+    FREE_ARRAY(key, subkey);
+
+	for (n = 0; n < key->subkeysigc; ++n) {
+        pgp_free_sig_info(&key->subkeysigs[n].siginfo);
+		pgp_subpacket_free(&key->subkeysigs[n].packet);
+	}
+    FREE_ARRAY(key, subkeysig);
+}
+
+/**
+ \ingroup HighLevel_Keyring
+
+ \brief Frees keydata and its memory
+
+ \param keydata Key to be freed.
+
+ \note This frees the keydata itself, as well as any other memory alloc-ed by it.
+*/
+void
+pgp_keydata_free(pgp_key_t *keydata)
+{
+    pgp_key_free(keydata);
+	free(keydata);
+}
+
+static unsigned siginfo_in_time(pgp_sig_info_t *siginfo){
+	time_t	now;
+	now = time(NULL);
+    /* in sig validity time frame */
+    return now >= siginfo->birthtime && (
+            siginfo->key_expiry == 0 ||
+            now < siginfo->birthtime +
+                  siginfo->key_expiry);
+}
+
+const int32_t
+pgp_key_find_uid_cond(
+        const pgp_key_t *key,
+        unsigned(*uidcond) ( uint8_t *, void *),
+        void *uidcondarg,
+        unsigned(*sigcond) ( const pgp_sig_info_t *, void *),
+        void *sigcondarg,
+        time_t *youngest,
+        unsigned checkrevoke,
+        unsigned checkexpiry)
+{
+    unsigned    	 	 uididx = 0;
+    unsigned    	 	 uidsigidx = 0;
+    int32_t    	 	 res = -1; /* Not found */
+    int32_t    	 	 lastgood;
+    uint8_t			**uidp;
+    pgp_uidsig_t    *uidsigp;
+    time_t           yngst = 0;
+
+    /* If not maximum age given, take default */
+    if(!youngest)
+        youngest = &yngst;
+
+    /* Loop over key's user ids*/
+    uidp = key->uids;
+    for (uididx = 0 ; uididx < key->uidc; uididx++, uidp++)
+    {
+        if(uidcond && !uidcond(*uidp, uidcondarg)) continue;
+
+        lastgood = res;
+        /* Loop over key's user ids sigs */
+        uidsigp = key->uidsigs;
+        for (uidsigidx = 0 ; uidsigidx < key->uidsigc; uidsigidx++, uidsigp++)
+        {
+            /* matching selected user id */
+            if(uidsigp->uid == uididx)
+            {
+                /* if uid is revoked */
+                /* revoke on secret keys has no effect*/
+                if(uidsigp->siginfo.type == PGP_SIG_REV_CERT)
+                {
+                    /* ignore revocation if secret */
+                    if(!checkrevoke)
+                        continue;
+
+                    /* revert to last good candidate */
+                    res = lastgood;
+                    break; /* jump to next uid */
+                }
+
+                /* in sig validity time frame */
+                if(!checkexpiry || siginfo_in_time(&uidsigp->siginfo))
+                {
+                    /* sig cond is ok ? */
+                    if(!sigcond || sigcond(&uidsigp->siginfo, sigcondarg))
+                    {
+                        /* youngest signature is deciding */
+                        if(uidsigp->siginfo.birthtime > *youngest)
+                        {
+                            *youngest = uidsigp->siginfo.birthtime;
+                            res = uididx;
+                        }
+                    }
+                }
+            }
+        }
+    }
+	return res;
+}
+
+/*
+ *  Returns :
+ *   -2 not found
+ *   -1 match is priamary key
+ *   >=0 index of matching valid subkey
+ * */
+const int32_t
+pgp_key_find_key_conds(
+        pgp_key_t *key,
+        unsigned(*keycond) ( const pgp_pubkey_t *, const uint8_t *, void*),
+        void *keycondarg,
+        unsigned(*sigcond) ( const pgp_sig_info_t *, void*),
+        void *sigcondarg,
+        unsigned checkrevoke,
+        unsigned checkexpiry)
+{
+    unsigned    	 	 subkeyidx = 0;
+    unsigned    	 	 subkeysigidx = 0;
+    unsigned    	 	 directsigidx = 0;
+    int32_t    	 	 res = -2; /* Not found */
+    int32_t    	 	 lastgood;
+    pgp_subkey_t	*subkeyp;
+    pgp_subkeysig_t    *subkeysigp;
+    pgp_directsig_t    *directsigp;
+    time_t	youngest;
+
+	youngest = 0;
+
+    /* check pubkey first */
+    if(!keycond || keycond(pgp_key_get_pubkey(key),
+                           key->pubkeyid, keycondarg)){
+
+        int32_t uidres;
+
+        /* Loop over key's direct sigs */
+        directsigp = key->directsigs;
+
+        for (directsigidx = 0 ; directsigidx < key->directsigc;
+                directsigidx++, directsigp++)
+        {
+            /* if direct is revoked */
+            if(directsigp->siginfo.type == PGP_SIG_REV_KEY)
+            {
+                /* ignore revocation if secret */
+                if(!checkrevoke)
+                    continue;
+
+                return -2; /* Key is globally revoked, no result */
+            }
+
+            /* in sig validity time frame */
+            if(!checkexpiry || siginfo_in_time(&directsigp->siginfo))
+            {
+                /* condition on sig is ok */
+                if(!sigcond || sigcond(&directsigp->siginfo, sigcondarg))
+                {
+                    /* youngest signature is deciding */
+                    if(directsigp->siginfo.birthtime > youngest)
+                    {
+                        youngest = directsigp->siginfo.birthtime;
+                        res = -1; /* Primary key is a candidate */
+                    }
+                }
+            }
+        }
+
+        uidres = pgp_key_find_uid_cond(
+                key, NULL, NULL, sigcond, sigcondarg, &youngest,
+                checkrevoke, checkexpiry);
+
+        /* if matching uid sig, then primary is matching key */
+        if(uidres != -1){
+            res = -1;
+        }
+    }
+
+    /* Loop over key's subkeys */
+    subkeyp = key->subkeys;
+    for (subkeyidx = 0 ; subkeyidx < key->subkeyc; subkeyidx++, subkeyp++)
+    {
+        lastgood = res;
+
+        subkeysigp = key->subkeysigs;
+
+        /* Skip this subkey if key condition not met */
+        if(keycond && !keycond(&subkeyp->key.pubkey, subkeyp->id, keycondarg))
+            continue;
+
+        /* Loop over key's subkeys sigs */
+        for (subkeysigidx = 0 ; subkeysigidx < key->subkeysigc;
+                subkeysigidx++, subkeysigp++)
+        {
+            /* matching selected subkey */
+            if(subkeysigp->subkey == subkeyidx)
+            {
+                /* if subkey is revoked */
+                if(subkeysigp->siginfo.type == PGP_SIG_REV_SUBKEY)
+                {
+                    /* ignore revocation if secret */
+                    if(!checkrevoke)
+                        continue;
+
+                    /* revert to last good candidate */
+                    res = lastgood;
+                    break; /* jump to next subkey */
+                }
+
+                /* in sig validity time frame */
+                if(!checkexpiry || siginfo_in_time(&subkeysigp->siginfo))
+                {
+                    /* subkey sig condition is ok */
+                    if(!sigcond || sigcond(&subkeysigp->siginfo, sigcondarg))
+                    {
+                        /* youngest signature is deciding */
+                        if(subkeysigp->siginfo.birthtime > youngest)
+                        {
+                            youngest = subkeysigp->siginfo.birthtime;
+                            res = subkeyidx;
+                        }
+                    }
+                }
+            }
+        }
+    }
+	return res;
+}
+
+/**
+ \ingroup HighLevel_KeyGeneral
+
+ \brief Returns the public key in the given keydata.
+ \param keydata
+
+  \return Pointer to public key
+
+  \note This is not a copy, do not free it after use.
+*/
+
+pgp_pubkey_t *
+pgp_key_get_pubkey(pgp_key_t *keydata)
+{
+	return (keydata->type == PGP_PTAG_CT_PUBLIC_KEY) ?
+				&keydata->key.pubkey :
+				&keydata->key.seckey.pubkey;
+}
+
+pgp_pubkey_t *
+pgp_key_get_subpubkey(pgp_key_t *key, int32_t subkeyidx)
+{
+	return (key->type == PGP_PTAG_CT_PUBLIC_KEY) ?
+				&key->subkeys[subkeyidx].key.pubkey :
+				&key->subkeys[subkeyidx].key.seckey.pubkey;
+}
+
+pgp_seckey_t *
+pgp_key_get_subseckey(pgp_key_t *key, int32_t subkeyidx)
+{
+	return (key->type == PGP_PTAG_CT_SECRET_KEY) ?
+				&key->subkeys[subkeyidx].key.seckey :
+				NULL;
+}
+static pgp_pubkey_t *
+key_get_pubkey_from_subidx(
+        pgp_key_t *key,
+        const uint8_t **id,
+        int32_t subkeyidx)
+{
+    if(subkeyidx == -2){
+        return NULL;
+    }
+
+    if(subkeyidx != -1)
+    {
+        if(id)
+            *id = key->subkeys[subkeyidx].id;
+
+	    return pgp_key_get_subpubkey(key, subkeyidx);
+
+    }
+
+    if(id)
+        *id = key->pubkeyid;
+
+	return pgp_key_get_pubkey(key);
+}
+
+static pgp_seckey_t *
+key_get_seckey_from_subidx(
+        pgp_key_t *key,
+        const uint8_t **id,
+        int32_t subkeyidx)
+{
+    if(subkeyidx == -2){
+        return NULL;
+    }
+
+    if(subkeyidx != -1)
+    {
+        if(id)
+            *id = key->subkeys[subkeyidx].id;
+
+	    return pgp_key_get_subseckey(key, subkeyidx);
+
+    }
+
+    if(id)
+        *id = key->pubkeyid;
+
+	return pgp_get_seckey(key);
+}
+
+static unsigned is_signing_role(const pgp_sig_info_t *siginfo, void *arg)
+{
+    return siginfo->key_flags & PGP_KEYFLAG_SIGN_DATA;
+}
+
+/* Get a pub key to check signature */
+pgp_pubkey_t *
+pgp_key_get_sigkey(pgp_key_t *key)
+{
+    int32_t subkeyidx =
+        pgp_key_find_key_conds(key, NULL, NULL, &is_signing_role, NULL, 0, 0);
+    return key_get_pubkey_from_subidx(key, NULL, subkeyidx);
+}
+
+/* Get a sec key to write a signature */
+pgp_seckey_t *
+pgp_key_get_certkey(pgp_key_t *key)
+{
+    int32_t subkeyidx =
+        pgp_key_find_key_conds(key, NULL, NULL, &is_signing_role, NULL, 1, 0);
+    return key_get_seckey_from_subidx(key, NULL, subkeyidx);
+}
+
+static unsigned is_encryption_role(const pgp_sig_info_t *siginfo, void *arg)
+{
+    return siginfo->key_flags & PGP_KEYFLAG_ENC_COMM;
+}
+
+pgp_pubkey_t *
+pgp_key_get_enckey(pgp_key_t *key, const uint8_t **id)
+{
+    int32_t subkeyidx =
+        pgp_key_find_key_conds(key, NULL, NULL, &is_encryption_role, NULL, 1, 0);
+
+    return key_get_pubkey_from_subidx(key, id, subkeyidx);
+}
+
+pgp_seckey_t *
+pgp_key_get_deckey(pgp_key_t *key, const uint8_t **id)
+{
+    int32_t subkeyidx =
+        pgp_key_find_key_conds(key, NULL, NULL, &is_encryption_role, NULL, 0, 0);
+
+    return key_get_seckey_from_subidx(key, id, subkeyidx);
+}
+
+static unsigned primary_uid_sigcond(const pgp_sig_info_t *siginfo, void *arg)
+{
+    return siginfo->primary_userid;
+}
+
+const int32_t pgp_key_get_uid0(pgp_key_t *key)
+{
+    int32_t res =
+        pgp_key_find_uid_cond(key, NULL, NULL, &primary_uid_sigcond, NULL, NULL, 1, 0);
+
+    /* arbitrarily use youngest uid if no primary is found */
+    return res == -1 ?
+        pgp_key_find_uid_cond(key, NULL, NULL, NULL, NULL, NULL, 1, 0):
+        res;
+}
+
+const uint8_t *pgp_key_get_primary_userid(pgp_key_t *key)
+{
+    const int32_t uid0 = pgp_key_get_uid0(key);
+    if( uid0 >= 0 && key->uids && key->uidc > uid0)
+    {
+        return key->uids[uid0];
+    }
+    return NULL;
+}
+
+unsigned key_bit_len(const pgp_pubkey_t *key)
+{
+	switch (key->alg) {
+	case PGP_PKA_DSA:
+		return BN_num_bits(key->key.dsa.p);
+
+	case PGP_PKA_RSA:
+		return BN_num_bits(key->key.rsa.n);
+
+	case PGP_PKA_ELGAMAL:
+		return BN_num_bits(key->key.elgamal.p);
+
+	default:
+        return 0;
+	}
+}
+
+unsigned key_is_weak(
+        const pgp_pubkey_t *key,
+        const uint8_t *keyid,
+        void *arg)
+{
+    unsigned kbl;
+    pgp_key_rating_t *res;
+
+    res = (pgp_key_rating_t*)arg;
+    kbl = key_bit_len(key);
+
+    if(kbl == 0)
+    {
+        *res = PGP_INVALID;
+    }
+    else if(kbl < 1024)
+    {
+        *res = PGP_TOOSHORT;
+    }
+    else if(kbl == 1024 && key->alg == PGP_PKA_RSA)
+    {
+        *res = PGP_WEAK;
+    }
+
+    return 0;
+}
+
+const pgp_key_rating_t pgp_key_get_rating(pgp_key_t *key)
+{
+    /* keys exist in rings only if valid */
+    pgp_key_rating_t res = PGP_VALID;
+
+    pgp_key_find_key_conds(key, &key_is_weak, (void*)&res, NULL, NULL, 0, 0);
+
+    if(res == PGP_VALID)
+    {
+        if(pgp_key_find_key_conds(
+                    key, NULL, NULL, NULL, NULL, 1, 0) == -2)
+        {
+            return PGP_REVOKED;
+        }
+        if(pgp_key_find_key_conds(
+                    key, NULL, NULL, NULL, NULL, 0, 1) == -2)
+        {
+            return PGP_EXPIRED;
+        }
+    }
+
+    return res;
+}
+/**
+\ingroup HighLevel_KeyGeneral
+
+\brief Check whether this is a secret key or not.
+*/
+
+unsigned
+pgp_is_key_secret(pgp_key_t *data)
+{
+	return data->type != PGP_PTAG_CT_PUBLIC_KEY;
+}
+
+/**
+ \ingroup HighLevel_KeyGeneral
+
+ \brief Returns the secret key in the given keydata.
+
+ \note This is not a copy, do not free it after use.
+
+ \note This returns a const.  If you need to be able to write to this
+ pointer, use pgp_get_writable_seckey
+*/
+
+pgp_seckey_t *
+pgp_get_seckey(pgp_key_t *data)
+{
+	return (data->type == PGP_PTAG_CT_SECRET_KEY) ?
+				&data->key.seckey : NULL;
+}
+
+/**
+ \ingroup HighLevel_KeyGeneral
+
+  \brief Returns the secret key in the given keydata.
+
+  \note This is not a copy, do not free it after use.
+
+  \note If you do not need to be able to modify this key, there is an
+  equivalent read-only function pgp_get_seckey.
+*/
+
+pgp_seckey_t *
+pgp_get_writable_seckey(pgp_key_t *data)
+{
+	return (data->type == PGP_PTAG_CT_SECRET_KEY) ?
+				&data->key.seckey : NULL;
+}
+
+/* utility function to zero out memory */
+void
+pgp_forget(void *vp, unsigned size)
+{
+	(void) memset(vp, 0x0, size);
+}
+
+typedef struct {
+	FILE			*passfp;
+	const pgp_key_t	*key;
+	char			*passphrase;
+	pgp_seckey_t		*seckey;
+} decrypt_t;
+
+// FIXME : support encrypted seckeys again
+// static pgp_cb_ret_t
+// decrypt_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+// {
+// 	const pgp_contents_t	*content = &pkt->u;
+// 	decrypt_t		*decrypt;
+// 	char			 pass[MAX_PASSPHRASE_LENGTH];
+//
+// 	decrypt = pgp_callback_arg(cbinfo);
+// 	switch (pkt->tag) {
+// 	case PGP_PARSER_PTAG:
+// 	case PGP_PTAG_CT_USER_ID:
+// 	case PGP_PTAG_CT_SIGNATURE:
+// 	case PGP_PTAG_CT_SIGNATURE_HEADER:
+// 	case PGP_PTAG_CT_SIGNATURE_FOOTER:
+// 	case PGP_PTAG_CT_TRUST:
+// 		break;
+//
+// 	case PGP_GET_PASSPHRASE:
+// 		(void) pgp_getpassphrase(decrypt->passfp, pass, sizeof(pass));
+// 		*content->skey_passphrase.passphrase = netpgp_strdup(pass);
+// 		pgp_forget(pass, (unsigned)sizeof(pass));
+// 		return PGP_KEEP_MEMORY;
+//
+// 	case PGP_PARSER_ERRCODE:
+// 		switch (content->errcode.errcode) {
+// 		case PGP_E_P_MPI_FORMAT_ERROR:
+// 			/* Generally this means a bad passphrase */
+// 			fprintf(stderr, "Bad passphrase!\n");
+// 			return PGP_RELEASE_MEMORY;
+//
+// 		case PGP_E_P_PACKET_CONSUMED:
+// 			/* And this is because of an error we've accepted */
+// 			return PGP_RELEASE_MEMORY;
+// 		default:
+// 			break;
+// 		}
+// 		(void) fprintf(stderr, "parse error: %s\n",
+// 				pgp_errcode(content->errcode.errcode));
+// 		return PGP_FINISHED;
+//
+// 	case PGP_PARSER_ERROR:
+// 		fprintf(stderr, "parse error: %s\n", content->error);
+// 		return PGP_FINISHED;
+//
+// 	case PGP_PTAG_CT_SECRET_KEY:
+// 		if ((decrypt->seckey = calloc(1, sizeof(*decrypt->seckey))) == NULL) {
+// 			(void) fprintf(stderr, "decrypt_cb: bad alloc\n");
+// 			return PGP_FINISHED;
+// 		}
+// 		decrypt->seckey->checkhash = calloc(1, PGP_CHECKHASH_SIZE);
+// 		*decrypt->seckey = content->seckey; /* XXX WTF ? */
+// 		return PGP_KEEP_MEMORY;
+//
+// 	case PGP_PARSER_PACKET_END:
+// 		/* nothing to do */
+// 		break;
+//
+// 	default:
+// 		fprintf(stderr, "Unexpected tag %d (0x%x)\n", pkt->tag,
+// 			pkt->tag);
+// 		return PGP_FINISHED;
+// 	}
+//
+// 	return PGP_RELEASE_MEMORY;
+// }
+
+// FIXME : support encrypted seckeys again
+// /**
+// \ingroup Core_Keys
+// \brief Decrypts secret key from given keydata with given passphrase
+// \param key Key from which to get secret key
+// \param passphrase Passphrase to use to decrypt secret key
+// \return secret key
+// */
+// pgp_seckey_t *
+// pgp_decrypt_seckey(const pgp_key_t *key, void *passfp)
+// {
+// 	pgp_stream_t	*stream;
+// 	const int	 printerrors = 1;
+// 	decrypt_t	 decrypt;
+//
+// 	(void) memset(&decrypt, 0x0, sizeof(decrypt));
+// 	decrypt.key = key;
+// 	decrypt.passfp = passfp;
+// 	stream = pgp_new(sizeof(*stream));
+// 	pgp_keydata_reader_set(stream, key);
+// 	pgp_set_callback(stream, decrypt_cb, &decrypt);
+// 	stream->readinfo.accumulate = 1;
+// 	pgp_parse(stream, !printerrors);
+// 	return decrypt.seckey;
+// }
+
+/* \todo check where userid pointers are copied */
+/**
+\ingroup Core_Keys
+\brief Copy user id, including contents
+\param dst Destination User ID
+\param src Source User ID
+\note If dst already has a userid, it will be freed.
+*/
+uint8_t *
+pgp_copy_userid(uint8_t **dst, const uint8_t *src)
+{
+	size_t          len;
+
+	len = strlen((const char *) src);
+	if (*dst) {
+		free(*dst);
+	}
+	if ((*dst = calloc(1, len + 1)) == NULL) {
+		(void) fprintf(stderr, "pgp_copy_userid: bad alloc\n");
+	} else {
+		(void) memcpy(*dst, src, len);
+	}
+	return *dst;
+}
+
+/* \todo check where pkt pointers are copied */
+/**
+\ingroup Core_Keys
+\brief Copy packet, including contents
+\param dst Destination packet
+\param src Source packet
+\note If dst already has a packet, it will be freed.
+*/
+pgp_subpacket_t *
+pgp_copy_packet(pgp_subpacket_t *dst, const pgp_subpacket_t *src)
+{
+	if (dst->raw) {
+		free(dst->raw);
+	}
+	if ((dst->raw = calloc(1, src->length)) == NULL) {
+		(void) fprintf(stderr, "pgp_copy_packet: bad alloc\n");
+	} else {
+		dst->length = src->length;
+		(void) memcpy(dst->raw, src->raw, src->length);
+	}
+
+	return dst;
+}
+
+#if 0
+/**
+\ingroup Core_Keys
+\brief Add User ID to key
+\param key Key to which to add User ID
+\param userid User ID to add
+\return Pointer to new User ID
+*/
+uint8_t  *
+pgp_add_userid(pgp_key_t *key, const uint8_t *userid)
+{
+	uint8_t  **uidp;
+
+	EXPAND_ARRAY(key, uid);
+	/* initialise new entry in array */
+	uidp = &key->uids[key->uidc++];
+	*uidp = NULL;
+	/* now copy it */
+	return pgp_copy_userid(uidp, userid);
+}
+#endif
+
+void print_packet_hex(const pgp_subpacket_t *pkt);
+
+/**
+\ingroup Core_Keys
+\brief Add selfsigned User ID to key
+\param keydata Key to which to add user ID
+\param userid Self-signed User ID to add
+\return 1 if OK; else 0
+*/
+#if 0 //////
+unsigned
+pgp_add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t key_expiry)
+{
+	pgp_create_sig_t	*sig;
+	pgp_subpacket_t	 sigpacket;
+	pgp_memory_t		*mem_sig = NULL;
+	pgp_output_t		*sigoutput = NULL;
+
+	/*
+     * create signature packet for this userid
+     */
+
+	/* create sig for this pkt */
+	sig = pgp_create_sig_new();
+	pgp_sig_start_key_sig(sig, &skey->key.seckey.pubkey, userid, PGP_CERT_POSITIVE);
+
+	pgp_add_creation_time(sig, time(NULL));
+	pgp_add_key_expiration_time(sig, key_expiry);
+	pgp_add_issuer_keyid(sig, skey->pubkeyid);
+	pgp_add_primary_userid(sig, 1);
+    pgp_add_key_flags(sig, PGP_KEYFLAG_SIGN_DATA|PGP_KEYFLAG_ENC_COMM);
+    pgp_add_key_prefs(sig);
+    pgp_add_key_features(sig);
+
+	pgp_end_hashed_subpkts(sig);
+
+	pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+	pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey);
+
+	/* add this packet to key */
+	sigpacket.length = pgp_mem_len(mem_sig);
+	sigpacket.raw = pgp_mem_data(mem_sig);
+
+	/* add user id and signature to key */
+    pgp_update_userid(skey, userid, &sigpacket, &sig->sig.info);
+    if(pkey)
+        pgp_update_userid(pkey, userid, &sigpacket, &sig->sig.info);
+
+	/* cleanup */
+	pgp_create_sig_delete(sig);
+	pgp_output_delete(sigoutput);
+	pgp_memory_free(mem_sig);
+
+	return 1;
+}
+#endif //////
+
+unsigned
+pgp_key_revoke(pgp_key_t *skey, pgp_key_t *pkey, uint8_t code, const char *reason)
+{
+	pgp_create_sig_t	*sig;
+	pgp_subpacket_t	 sigpacket;
+	pgp_memory_t		*mem_sig = NULL;
+	pgp_output_t		*sigoutput = NULL;
+
+	sig = pgp_create_sig_new();
+	pgp_sig_start_key_rev(
+            sig, &skey->key.seckey.pubkey,
+            PGP_SIG_REV_KEY);
+
+	pgp_add_creation_time(sig, time(NULL));
+	pgp_add_issuer_keyid(sig, skey->pubkeyid);
+    pgp_add_revocation_reason(sig, code, reason);
+	pgp_end_hashed_subpkts(sig);
+
+	pgp_setup_memory_write(&sigoutput, &mem_sig, 128);
+	pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey);
+
+	sigpacket.length = pgp_mem_len(mem_sig);
+	sigpacket.raw = pgp_mem_data(mem_sig);
+
+    pgp_add_directsig(skey, &sigpacket, &sig->sig.info);
+    pgp_add_directsig(pkey, &sigpacket, &sig->sig.info);
+
+	/* cleanup */
+	pgp_create_sig_delete(sig);
+	pgp_output_delete(sigoutput);
+	pgp_memory_free(mem_sig);
+
+	return 1;
+}
+
+/**
+\ingroup Core_Keys
+\brief Initialise pgp_key_t
+\param keydata Keydata to initialise
+\param type PGP_PTAG_CT_PUBLIC_KEY or PGP_PTAG_CT_SECRET_KEY
+*/
+void
+pgp_keydata_init(pgp_key_t *keydata, const pgp_content_enum type)
+{
+	if (keydata->type != PGP_PTAG_CT_RESERVED) {
+		(void) fprintf(stderr,
+			"pgp_keydata_init: wrong keydata type\n");
+	} else if (type != PGP_PTAG_CT_PUBLIC_KEY &&
+		   type != PGP_PTAG_CT_SECRET_KEY) {
+		(void) fprintf(stderr, "pgp_keydata_init: wrong type\n");
+	} else {
+		keydata->type = type;
+	}
+}
+
+/**
+   \ingroup HighLevel_KeyringRead
+
+   \brief Reads a keyring from a file
+
+   \param keyring Pointer to an existing pgp_keyring_t struct
+   \param armour 1 if file is armoured; else 0
+   \param filename Filename of keyring to be read
+
+   \return pgp 1 if OK; 0 on error
+
+   \note Keyring struct must already exist.
+
+   \note Can be used with either a public or secret keyring.
+
+   \note You must call pgp_keyring_free() after usage to free alloc-ed memory.
+
+   \note If you call this twice on the same keyring struct, without calling
+   pgp_keyring_free() between these calls, you will introduce a memory leak.
+
+   \sa pgp_keyring_read_from_mem()
+   \sa pgp_keyring_free()
+
+*/
+#if 0 //////
+unsigned
+pgp_keyring_fileread(pgp_io_t *io,
+            pgp_keyring_t *pubring,
+            pgp_keyring_t *secring,
+			const unsigned armour,
+			const char *filename)
+{
+    return pgp_filter_keys_fileread(
+                io,
+                pubring,
+                secring,
+                NULL /*certring -> self cert */,
+                armour,
+                filename);
+}
+#endif //////
+
+/**
+   \ingroup HighLevel_KeyringRead
+
+   \brief Reads a keyring from memory
+
+   \param keyring Pointer to existing pgp_keyring_t struct
+   \param armour 1 if file is armoured; else 0
+   \param mem Pointer to a pgp_memory_t struct containing keyring to be read
+
+   \return pgp 1 if OK; 0 on error
+
+   \note Keyring struct must already exist.
+
+   \note Can be used with either a public or secret keyring.
+
+   \note You must call pgp_keyring_free() after usage to free alloc-ed memory.
+
+   \note If you call this twice on the same keyring struct, without calling
+   pgp_keyring_free() between these calls, you will introduce a memory leak.
+
+   \sa pgp_keyring_fileread
+   \sa pgp_keyring_free
+*/
+unsigned
+pgp_keyring_read_from_mem(pgp_io_t *io,
+            pgp_keyring_t *pubring,
+            pgp_keyring_t *secring,
+            const unsigned armour,
+            pgp_memory_t *mem)
+{
+   return pgp_filter_keys_from_mem(io,
+                pubring,
+                secring,
+                NULL /* certring -> self certification */,
+                armour,
+                mem);
+}
+
+/**
+   \ingroup HighLevel_KeyringRead
+
+   \brief Frees keyring's contents (but not keyring itself)
+
+   \param keyring Keyring whose data is to be freed
+
+   \note This does not free keyring itself, just the memory alloc-ed in it.
+ */
+void
+pgp_keyring_free(pgp_keyring_t *keyring)
+{
+	(void)free(keyring->keys);
+	keyring->keys = NULL;
+	keyring->keyc = keyring->keyvsize = 0;
+}
+
+void
+pgp_keyring_purge(pgp_keyring_t *keyring)
+{
+	pgp_key_t *keyp;
+    unsigned c = 0;
+	for (keyp = keyring->keys; c < keyring->keyc; c++, keyp++) {
+        pgp_key_free(keyp);
+    }
+    pgp_keyring_free(keyring);
+}
+
+static unsigned
+deletekey( pgp_keyring_t *keyring, pgp_key_t *key, unsigned from)
+{
+    /* 'from' is index of key to delete */
+
+    /* free key internals */
+    pgp_key_free(key);
+
+    /* decrement key count, vsize stays the same so no realloc needed */
+    keyring->keyc--;
+
+    /* Move following keys to fill the gap */
+	for ( ; keyring && from < keyring->keyc; from += 1) {
+		memcpy(&keyring->keys[from], &keyring->keys[from+1],
+               sizeof(pgp_key_t));
+	}
+
+	return 1;
+}
+
+unsigned key_id_match(const pgp_pubkey_t *key, const uint8_t *keyid, void *refidarg)
+{
+    uint8_t *refid = refidarg;
+    return (memcmp(keyid, refid, PGP_KEY_ID_SIZE) == 0);
+}
+/**
+   \ingroup HighLevel_KeyringFind
+
+   \brief Finds key in keyring from its Key ID
+
+   \param keyring Keyring to be searched
+   \param keyid ID of required key
+
+   \return Pointer to key, if found; NULL, if not found
+
+   \note This returns a pointer to the key inside the given keyring,
+   not a copy.  Do not free it after use.
+
+*/
+pgp_key_t *
+pgp_getkeybyid(pgp_io_t *io, const pgp_keyring_t *keyring,
+			   const uint8_t *keyid, unsigned *from,
+               pgp_pubkey_t **pubkey,
+               pgp_seckey_t **seckey,
+               unsigned checkrevoke,
+               unsigned checkexpiry)
+{
+	uint8_t	nullid[PGP_KEY_ID_SIZE];
+
+	(void) memset(nullid, 0x0, sizeof(nullid));
+	for ( ; keyring && *from < keyring->keyc; *from += 1) {
+        pgp_key_t *key = &keyring->keys[*from];
+        int32_t subkeyidx;
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(io->errs, "keyring keyid", key->pubkeyid, PGP_KEY_ID_SIZE);
+			hexdump(io->errs, "keyid", keyid, PGP_KEY_ID_SIZE);
+		}
+
+        subkeyidx = pgp_key_find_key_conds(key, &key_id_match,
+                                           (void*)keyid, NULL, NULL,
+                                           checkrevoke, checkexpiry);
+
+		if (subkeyidx != -2) {
+			if (pubkey) {
+				*pubkey = key_get_pubkey_from_subidx(key, NULL, subkeyidx);
+			}
+			if (seckey) {
+				*seckey = key_get_seckey_from_subidx(key, NULL, subkeyidx);
+			}
+			return key;
+		}
+	}
+	return NULL;
+}
+
+unsigned
+pgp_deletekeybyid(pgp_io_t *io, pgp_keyring_t *keyring,
+			   const uint8_t *keyid)
+{
+    unsigned from = 0;
+	pgp_key_t *key;
+
+	if ((key = (pgp_key_t *)pgp_getkeybyid(io, keyring, keyid,
+                                           &from, NULL, NULL, 0, 0)) == NULL) {
+		return 0;
+	}
+    /* 'from' is now index of key to delete */
+
+    deletekey(keyring, key, from);
+
+	return 1;
+}
+
+/**
+   \ingroup HighLevel_KeyringFind
+
+   \brief Finds key in keyring from its Key Fingerprint
+
+   \param keyring Keyring to be searched
+   \param fpr fingerprint of required key
+   \param fpr length of required key
+
+   \return Pointer to key, if found; NULL, if not found
+
+   \note This returns a pointer to the key inside the given keyring,
+   not a copy.  Do not free it after use.
+
+*/
+
+pgp_key_t *
+pgp_getkeybyfpr(pgp_io_t *io, const pgp_keyring_t *keyring,
+			    const uint8_t *fpr, size_t length,
+                unsigned *from,
+                pgp_pubkey_t **pubkey,
+                unsigned checkrevoke,
+                unsigned checkexpiry)
+{
+
+	for ( ; keyring && *from < keyring->keyc; *from += 1) {
+        pgp_key_t *key = &keyring->keys[*from];
+
+        pgp_fingerprint_t *kfp = &key->pubkeyfpr;
+
+		if (kfp->length == length &&
+            memcmp(kfp->fingerprint, fpr, length) == 0) {
+
+            if(checkrevoke || checkexpiry){
+                int32_t subkeyidx;
+
+                subkeyidx = pgp_key_find_key_conds(key,
+                                                   NULL, NULL,
+                                                   NULL, NULL,
+                                                   checkrevoke, checkexpiry);
+
+                if (subkeyidx == -2) return NULL;
+            }
+			if (pubkey) {
+				*pubkey = &key->key.pubkey;
+			}
+			return key;
+		}
+	}
+	return NULL;
+}
+
+unsigned
+pgp_deletekeybyfpr(pgp_io_t *io, pgp_keyring_t *keyring,
+			      const uint8_t *fpr, size_t length)
+{
+    unsigned from = 0;
+	pgp_key_t *key;
+
+	if ((key = (pgp_key_t *)pgp_getkeybyfpr(io, keyring, fpr, length,
+                                           &from, NULL,0,0)) == NULL) {
+		return 0;
+	}
+    /* 'from' is now index of key to delete */
+
+    deletekey(keyring, key, from);
+
+	return 1;
+}
+
+#if 0
+/* convert a string keyid into a binary keyid */
+static void
+str2keyid(const char *userid, uint8_t *keyid, size_t len)
+{
+	static const char	*uppers = "0123456789ABCDEF";
+	static const char	*lowers = "0123456789abcdef";
+	const char		*hi;
+	const char		*lo;
+	uint8_t			 hichar;
+	uint8_t			 lochar;
+	size_t			 j;
+	int			 i;
+
+	for (i = 0, j = 0 ; j < len && userid[i] && userid[i + 1] ; i += 2, j++) {
+		if ((hi = strchr(uppers, userid[i])) == NULL) {
+			if ((hi = strchr(lowers, userid[i])) == NULL) {
+				break;
+			}
+			hichar = (uint8_t)(hi - lowers);
+		} else {
+			hichar = (uint8_t)(hi - uppers);
+		}
+		if ((lo = strchr(uppers, userid[i + 1])) == NULL) {
+			if ((lo = strchr(lowers, userid[i + 1])) == NULL) {
+				break;
+			}
+			lochar = (uint8_t)(lo - lowers);
+		} else {
+			lochar = (uint8_t)(lo - uppers);
+		}
+		keyid[j] = (hichar << 4) | (lochar);
+	}
+	keyid[j] = 0x0;
+}
+#endif
+
+/* return the next key which matches, starting searching at *from */
+#if 0 //////
+static const pgp_key_t *
+getkeybyname(pgp_io_t *io,
+			const pgp_keyring_t *keyring,
+			const char *name,
+			unsigned *from)
+{
+	//const pgp_key_t	*kp;
+	uint8_t			**uidp;
+	unsigned    	 	 i = 0;
+	pgp_key_t		*keyp;
+	// unsigned		 savedstart;
+	regex_t			 r;
+	//uint8_t		 	 keyid[PGP_KEY_ID_SIZE + 1];
+	size_t          	 len;
+
+	if (!keyring || !name || !from) {
+		return NULL;
+	}
+	len = strlen(name);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(io->outs, "[%u] name '%s', len %zu\n",
+			*from, name, len);
+	}
+
+    /* first try name as a keyid */
+	// (void) memset(keyid, 0x0, sizeof(keyid));
+	// str2keyid(name, keyid, sizeof(keyid));
+	// if (pgp_get_debug_level(__FILE__)) {
+	// 	hexdump(io->outs, "keyid", keyid, 4);
+	// }
+	// savedstart = *from;
+	// if ((kp = pgp_getkeybyid(io, keyring, keyid, from,
+    //                          NULL, NULL, 0, 0)) != NULL) {
+	// 	return kp;
+	// }
+	// *from = savedstart;
+
+    if (pgp_get_debug_level(__FILE__) && name != NULL) {
+		(void) fprintf(io->outs, "regex match '%s' from %u\n",
+			name, *from);
+	}
+	/* match on full name or email address as a
+        - NOSUB only success/failure, no match content
+        - LITERAL ignore special chars in given string
+        - ICASE ignore case
+     */
+    if (name != NULL) {
+        (void) regcomp(&r, name, REG_NOSUB | REG_LITERAL | REG_ICASE);
+    }
+    if(keyring->keys != NULL)
+      for (keyp = &keyring->keys[*from]; *from < keyring->keyc; *from += 1, keyp++) {
+		uidp = keyp->uids;
+        if (name == NULL) {
+            return keyp;
+        } else {
+            for (i = 0 ; i < keyp->uidc; i++, uidp++) {
+                if (regexec(&r, (char *)*uidp, 0, NULL, 0) == 0) {
+                    if (pgp_get_debug_level(__FILE__)) {
+                        (void) fprintf(io->outs,
+                            "MATCHED keyid \"%s\" len %" PRIsize "u\n",
+                               (char *) *uidp, len);
+                    }
+                    regfree(&r);
+                    return keyp;
+                }
+            }
+        }
+	}
+	regfree(&r);
+	return NULL;
+}
+#endif //////
+
+/**
+   \ingroup HighLevel_KeyringFind
+
+   \brief Finds key from its User ID
+
+   \param keyring Keyring to be searched
+   \param userid User ID of required key
+
+   \return Pointer to Key, if found; NULL, if not found
+
+   \note This returns a pointer to the key inside the keyring, not a
+   copy.  Do not free it.
+
+*/
+#if 0 //////
+const pgp_key_t *
+pgp_getkeybyname(pgp_io_t *io,
+			const pgp_keyring_t *keyring,
+			const char *name)
+{
+	unsigned	from;
+
+	from = 0;
+	return getkeybyname(io, keyring, name, &from);
+}
+#endif //////
+
+#if 0 //////
+const pgp_key_t *
+pgp_getnextkeybyname(pgp_io_t *io,
+			const pgp_keyring_t *keyring,
+			const char *name,
+			unsigned *n)
+{
+	return getkeybyname(io, keyring, name, n);
+}
+#endif //////
+
+/* this interface isn't right - hook into callback for getting passphrase */
+#if 0 //////
+char *
+pgp_export_key(pgp_io_t *io, const pgp_key_t *keydata, uint8_t *passphrase)
+{
+	pgp_output_t	*output;
+	pgp_memory_t	*mem;
+	char		*cp;
+
+	__PGP_USED(io);
+	pgp_setup_memory_write(&output, &mem, 128);
+    pgp_write_xfer_key(output, keydata, 1);
+
+	/* TODO deal with passphrase again
+		pgp_write_xfer_seckey(output, keydata, passphrase,
+					strlen((char *)passphrase), 1);
+    */
+	cp = netpgp_strdup(pgp_mem_data(mem));
+	pgp_teardown_memory_write(output, mem);
+	return cp;
+}
+#endif //////
+
+/* lowlevel add to keyring */
+int
+pgp_keyring_add(pgp_keyring_t *dst, const pgp_key_t *src)
+{
+	pgp_key_t	*key;
+
+    EXPAND_ARRAY(dst, key);
+    key = &dst->keys[dst->keyc++];
+	memcpy(key, src, sizeof(*key));
+    return 1;
+}
+
+pgp_key_t *pgp_ensure_pubkey(
+        pgp_keyring_t *keyring,
+        pgp_pubkey_t *pubkey,
+        uint8_t *pubkeyid)
+{
+    pgp_key_t *key;
+    unsigned c;
+
+    if(keyring == NULL) return NULL;
+
+    /* try to find key in keyring */
+	for (c = 0; c < keyring->keyc; c += 1) {
+		if (memcmp(keyring->keys[c].pubkeyid,
+                   pubkeyid, PGP_KEY_ID_SIZE) == 0) {
+			return &keyring->keys[c];
+		}
+    }
+
+    /* if key doesn't already exist in keyring, create it */
+    EXPAND_ARRAY(keyring, key);
+    key = &keyring->keys[keyring->keyc++];
+    (void) memset(key, 0x0, sizeof(*key));
+
+    /* fill in what we already know */
+    key->type = PGP_PTAG_CT_PUBLIC_KEY;
+    pgp_pubkey_dup(&key->key.pubkey, pubkey);
+    (void) memcpy(&key->pubkeyid, pubkeyid, PGP_KEY_ID_SIZE);
+    pgp_fingerprint(&key->pubkeyfpr, pubkey, keyring->hashtype);
+
+    return key;
+}
+
+pgp_key_t *pgp_ensure_seckey(
+        pgp_keyring_t *keyring,
+        pgp_seckey_t *seckey,
+        uint8_t *pubkeyid)
+{
+    pgp_key_t *key;
+    unsigned c;
+
+    if (keyring == NULL) return NULL;
+
+    /* try to find key in keyring */
+	for (c = 0; c < keyring->keyc; c += 1) {
+		if (memcmp(keyring->keys[c].pubkeyid,
+                   pubkeyid, PGP_KEY_ID_SIZE) == 0) {
+			return &keyring->keys[c];
+		}
+    }
+
+    /* if key doesn't already exist in keyring, create it */
+    EXPAND_ARRAY(keyring, key);
+    key = &keyring->keys[keyring->keyc++];
+    (void) memset(key, 0x0, sizeof(*key));
+
+    /* fill in what we already know */
+    key->type = PGP_PTAG_CT_SECRET_KEY;
+    pgp_seckey_dup(&key->key.seckey, seckey);
+    (void) memcpy(&key->pubkeyid, pubkeyid, PGP_KEY_ID_SIZE);
+    pgp_fingerprint(&key->pubkeyfpr, &seckey->pubkey, keyring->hashtype);
+
+    return key;
+}
+
+unsigned pgp_add_directsig(
+        pgp_key_t *key,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo)
+{
+    pgp_directsig_t *directsigp;
+    unsigned directsigidx;
+
+    /* Detect duplicate direct sig */
+    directsigp = key->directsigs;
+    for (directsigidx = 0 ; directsigidx < key->directsigc;
+            directsigidx++, directsigp++)
+    {
+        if( directsigp->packet.length == sigpkt->length &&
+            memcmp(directsigp->packet.raw, sigpkt->raw, sigpkt->length) == 0)
+        {
+            /* signature already exist */
+            return 1;
+        }
+    }
+
+
+    EXPAND_ARRAY(key, directsig);
+    directsigp = &key->directsigs[key->directsigc++];
+
+    copy_sig_info(&directsigp->siginfo,
+                  siginfo);
+    pgp_copy_packet(&directsigp->packet, sigpkt);
+
+    return 0;
+}
+
+unsigned pgp_update_userid(
+        pgp_key_t *key,
+        const uint8_t *userid,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo)
+{
+	    unsigned    	 	 uididx = 0;
+        unsigned    	 	 uidsigidx = 0;
+        uint8_t			**uidp;
+        pgp_uidsig_t    *uidsigp;
+
+        /* Try to find identical userID */
+		uidp = key->uids;
+		for (uididx = 0 ; uididx < key->uidc; uididx++, uidp++)
+        {
+			if (strcmp((char *)*uidp, (char *)userid) == 0)
+            {
+                /* Found one. check for duplicate uidsig */
+                uidsigp = key->uidsigs;
+                for (uidsigidx = 0 ; uidsigidx < key->uidsigc;
+                     uidsigidx++, uidsigp++)
+                {
+                    if(uidsigp->uid == uididx &&
+                       uidsigp->packet.length == sigpkt->length &&
+                       memcmp(uidsigp->packet.raw, sigpkt->raw,
+                              sigpkt->length) == 0)
+                    {
+                            /* signature already exists */
+                            return 1;
+                    }
+                }
+				break;
+			}
+		}
+
+        /* Add a new one if none found */
+        if(uididx==key->uidc){
+            EXPAND_ARRAY(key, uid);
+            uidp = &key->uids[key->uidc++];
+            *uidp = NULL;
+            pgp_copy_userid(uidp, userid);
+        }
+
+        /* Add uid sig info, pointing to that uid */
+		EXPAND_ARRAY(key, uidsig);
+        uidsigp = &key->uidsigs[key->uidsigc++];
+		uidsigp->uid = uididx;
+
+        /* store sig info and packet */
+        copy_sig_info(&uidsigp->siginfo, siginfo);
+        pgp_copy_packet(&uidsigp->packet, sigpkt);
+
+        return 0;
+}
+
+unsigned pgp_update_subkey(
+        pgp_key_t *key,
+	    pgp_content_enum subkeytype,
+        pgp_keydata_key_t *subkey,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo)
+{
+	    unsigned    	 	 subkeyidx = 0;
+        unsigned    	 	 subkeysigidx = 0;
+        pgp_subkey_t	 *subkeyp;
+        pgp_subkeysig_t    *subkeysigp;
+        uint8_t subkeyid[PGP_KEY_ID_SIZE];
+
+        pgp_keyid(subkeyid, PGP_KEY_ID_SIZE,
+                  (subkeytype == PGP_PTAG_CT_PUBLIC_KEY) ?
+                  &subkey->pubkey:
+                  &subkey->seckey.pubkey, PGP_HASH_SHA1);
+
+        /* Try to find identical subkey ID */
+		subkeyp = key->subkeys;
+		for (subkeyidx = 0 ; subkeyidx < key->subkeyc; subkeyidx++, subkeyp++)
+        {
+            if(memcmp(subkeyid, subkeyp->id, PGP_KEY_ID_SIZE) == 0 )
+            {
+                /* Found same subkey. Detect duplicate sig */
+                subkeysigp = key->subkeysigs;
+                for (subkeysigidx = 0 ; subkeysigidx < key->subkeysigc;
+                        subkeysigidx++, subkeysigp++)
+                {
+                    if(subkeysigp->subkey == subkeyidx &&
+                       subkeysigp->packet.length == sigpkt->length &&
+                       memcmp(subkeysigp->packet.raw, sigpkt->raw,
+                              sigpkt->length) == 0)
+                    {
+                            /* signature already exists */
+                            return 1;
+                    }
+                }
+
+				break;
+			}
+		}
+        /* Add a new one if none found */
+        if(subkeyidx==key->subkeyc){
+            if(subkeytype == PGP_PTAG_CT_PUBLIC_KEY &&
+               key->type != PGP_PTAG_CT_PUBLIC_KEY){
+                /* cannot create secret subkey from public */
+                /* and may not insert public subkey in seckey */
+                return 1;
+            }
+
+            EXPAND_ARRAY(key, subkey);
+            subkeyp = &key->subkeys[key->subkeyc++];
+            /* copy subkey material */
+            if(key->type == PGP_PTAG_CT_PUBLIC_KEY) {
+                pgp_pubkey_dup(&subkeyp->key.pubkey,
+                  (subkeytype == PGP_PTAG_CT_PUBLIC_KEY) ?
+                  &subkey->pubkey:
+                  &subkey->seckey.pubkey);
+            } else {
+                pgp_seckey_dup(&subkeyp->key.seckey, &subkey->seckey);
+            }
+            /* copy subkeyID */
+            memcpy(subkeyp->id, subkeyid, PGP_KEY_ID_SIZE);
+        }
+
+        /* Add subkey sig info, pointing to that subkey */
+		EXPAND_ARRAY(key, subkeysig);
+        subkeysigp = &key->subkeysigs[key->subkeysigc++];
+		subkeysigp->subkey = subkeyidx;
+
+        /* store sig info and packet */
+        copy_sig_info(&subkeysigp->siginfo,
+                      siginfo);
+        pgp_copy_packet(&subkeysigp->packet, sigpkt);
+
+        return 0;
+}
+
+/* append one keyring to another */
+int
+pgp_append_keyring(pgp_keyring_t *keyring, pgp_keyring_t *newring)
+{
+	unsigned	i;
+
+	for (i = 0 ; i < newring->keyc ; i++) {
+		EXPAND_ARRAY(keyring, key);
+		(void) memcpy(&keyring->keys[keyring->keyc], &newring->keys[i],
+				sizeof(newring->keys[i]));
+		keyring->keyc += 1;
+	}
+	return 1;
+}
diff --git a/netpgp/misc.c b/netpgp/misc.c
new file mode 100644
index 0000000000000000000000000000000000000000..8492be3932b40b3279e29f81e6cac163406ae1e2
--- /dev/null
+++ b/netpgp/misc.c
@@ -0,0 +1,1264 @@
+/*-
+ * Copyright (c) 2009,2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_OPENSSL_RAND_H
+#include <openssl/rand.h>
+#endif
+
+#include "netpgp/errors.h"
+#include "netpgp/packet.h"
+#include "netpgp/crypto.h"
+#include "netpgp/create.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/packet-show.h"
+#include "netpgp/signature.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/memory.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/version.h"
+#include "netpgp/netpgpdigest.h"
+
+#ifdef WIN32
+#define vsnprintf _vsnprintf
+#endif
+
+/** \file
+ * \brief Error Handling
+ */
+#define ERRNAME(code)	{ code, #code }
+
+static pgp_errcode_name_map_t errcode_name_map[] = {
+	ERRNAME(PGP_E_OK),
+	ERRNAME(PGP_E_FAIL),
+	ERRNAME(PGP_E_SYSTEM_ERROR),
+	ERRNAME(PGP_E_UNIMPLEMENTED),
+
+	ERRNAME(PGP_E_R),
+	ERRNAME(PGP_E_R_READ_FAILED),
+	ERRNAME(PGP_E_R_EARLY_EOF),
+	ERRNAME(PGP_E_R_BAD_FORMAT),
+	ERRNAME(PGP_E_R_UNCONSUMED_DATA),
+
+	ERRNAME(PGP_E_W),
+	ERRNAME(PGP_E_W_WRITE_FAILED),
+	ERRNAME(PGP_E_W_WRITE_TOO_SHORT),
+
+	ERRNAME(PGP_E_P),
+	ERRNAME(PGP_E_P_NOT_ENOUGH_DATA),
+	ERRNAME(PGP_E_P_UNKNOWN_TAG),
+	ERRNAME(PGP_E_P_PACKET_CONSUMED),
+	ERRNAME(PGP_E_P_MPI_FORMAT_ERROR),
+
+	ERRNAME(PGP_E_C),
+
+	ERRNAME(PGP_E_V),
+	ERRNAME(PGP_E_V_BAD_SIGNATURE),
+	ERRNAME(PGP_E_V_NO_SIGNATURE),
+	ERRNAME(PGP_E_V_UNKNOWN_SIGNER),
+
+	ERRNAME(PGP_E_ALG),
+	ERRNAME(PGP_E_ALG_UNSUPPORTED_SYMMETRIC_ALG),
+	ERRNAME(PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG),
+	ERRNAME(PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG),
+	ERRNAME(PGP_E_ALG_UNSUPPORTED_HASH_ALG),
+
+	ERRNAME(PGP_E_PROTO),
+	ERRNAME(PGP_E_PROTO_BAD_SYMMETRIC_DECRYPT),
+	ERRNAME(PGP_E_PROTO_UNKNOWN_SS),
+	ERRNAME(PGP_E_PROTO_CRITICAL_SS_IGNORED),
+	ERRNAME(PGP_E_PROTO_BAD_PUBLIC_KEY_VRSN),
+	ERRNAME(PGP_E_PROTO_BAD_SIGNATURE_VRSN),
+	ERRNAME(PGP_E_PROTO_BAD_ONE_PASS_SIG_VRSN),
+	ERRNAME(PGP_E_PROTO_BAD_PKSK_VRSN),
+	ERRNAME(PGP_E_PROTO_DECRYPTED_MSG_WRONG_LEN),
+	ERRNAME(PGP_E_PROTO_BAD_SK_CHECKSUM),
+
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+/**
+ * \ingroup Core_Errors
+ * \brief returns error code name
+ * \param errcode
+ * \return error code name or "Unknown"
+ */
+const char     *
+pgp_errcode(const pgp_errcode_t errcode)
+{
+	return (pgp_str_from_map((int) errcode,
+			(pgp_map_t *) errcode_name_map));
+}
+
+/* generic grab new storage function */
+void *
+pgp_new(size_t size)
+{
+	void	*vp;
+
+	if ((vp = calloc(1, size)) == NULL) {
+		(void) fprintf(stderr,
+			"allocation failure for %" PRIsize "u bytes", size);
+	}
+	return vp;
+}
+
+/**
+ * \ingroup Core_Errors
+ * \brief Pushes the given error on the given errorstack
+ * \param errstack Error stack to use
+ * \param errcode Code of error to push
+ * \param sys_errno System errno (used if errcode=PGP_E_SYSTEM_ERROR)
+ * \param file Source filename where error occurred
+ * \param line Line in source file where error occurred
+ * \param fmt Comment
+ *
+ */
+
+void
+pgp_push_error(pgp_error_t **errstack, pgp_errcode_t errcode,
+		int sys_errno, const char *file, int line, const char *fmt,...)
+{
+	/* first get the varargs and generate the comment */
+	pgp_error_t  *err;
+	unsigned	maxbuf = 128;
+	va_list		args;
+	char           *comment;
+
+	if ((comment = calloc(1, maxbuf + 1)) == NULL) {
+		(void) fprintf(stderr, "calloc comment failure\n");
+		return;
+	}
+
+	va_start(args, fmt);
+	vsnprintf(comment, maxbuf + 1, fmt, args);
+	va_end(args);
+
+	/* alloc a new error and add it to the top of the stack */
+
+	if ((err = calloc(1, sizeof(*err))) == NULL) {
+		(void) fprintf(stderr, "calloc comment failure\n");
+		return;
+	}
+
+	err->next = *errstack;
+	*errstack = err;
+
+	/* fill in the details */
+	err->errcode = errcode;
+	err->sys_errno = sys_errno;
+	err->file = file;
+	err->line = line;
+
+	err->comment = comment;
+}
+
+/**
+\ingroup Core_Errors
+\brief print this error
+\param err Error to print
+*/
+void
+pgp_print_error(pgp_error_t *err)
+{
+	printf("%s:%d: ", err->file, err->line);
+	if (err->errcode == PGP_E_SYSTEM_ERROR) {
+		printf("system error %d returned from %s()\n", err->sys_errno,
+		       err->comment);
+	} else {
+		printf("%s, %s\n", pgp_errcode(err->errcode), err->comment);
+	}
+}
+
+/**
+\ingroup Core_Errors
+\brief Print all errors on stack
+\param errstack Error stack to print
+*/
+void
+pgp_print_errors(pgp_error_t *errstack)
+{
+	pgp_error_t    *err;
+
+	for (err = errstack; err != NULL; err = err->next) {
+		pgp_print_error(err);
+	}
+}
+
+/**
+\ingroup Core_Errors
+\brief Return 1 if given error is present anywhere on stack
+\param errstack Error stack to check
+\param errcode Error code to look for
+\return 1 if found; else 0
+*/
+int
+pgp_has_error(pgp_error_t *errstack, pgp_errcode_t errcode)
+{
+	pgp_error_t    *err;
+
+	for (err = errstack; err != NULL; err = err->next) {
+		if (err->errcode == errcode) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+\ingroup Core_Errors
+\brief Frees all errors on stack
+\param errstack Error stack to free
+*/
+void
+pgp_free_errors(pgp_error_t *errstack)
+{
+	pgp_error_t    *next;
+
+	while (errstack != NULL) {
+		next = errstack->next;
+		free(errstack->comment);
+		free(errstack);
+		errstack = next;
+	}
+}
+
+/* hash a 32-bit integer */
+static int
+hash_uint32(pgp_hash_t *hash, uint32_t n)
+{
+	uint8_t	ibuf[4];
+
+	ibuf[0] = (uint8_t)(n >> 24) & 0xff;
+	ibuf[1] = (uint8_t)(n >> 16) & 0xff;
+	ibuf[2] = (uint8_t)(n >> 8) & 0xff;
+	ibuf[3] = (uint8_t)n & 0xff;
+	(*hash->add)(hash, (const uint8_t *)(void *)ibuf, (unsigned)sizeof(ibuf));
+	return sizeof(ibuf);
+}
+
+/* hash a string - first length, then string itself */
+static int
+hash_string(pgp_hash_t *hash, const uint8_t *buf, uint32_t len)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "hash_string", buf, len);
+	}
+	hash_uint32(hash, len);
+	(*hash->add)(hash, buf, len);
+	return (int)(sizeof(len) + len);
+}
+
+/* hash a bignum, possibly padded - first length, then string itself */
+static int
+hash_bignum(pgp_hash_t *hash, BIGNUM *bignum)
+{
+	uint8_t	*bn;
+	size_t	 len;
+	int	 padbyte;
+
+	if (BN_is_zero(bignum)) {
+		hash_uint32(hash, 0);
+		return sizeof(len);
+	}
+	if ((len = (size_t) BN_num_bytes(bignum)) < 1) {
+		(void) fprintf(stderr, "hash_bignum: bad size\n");
+		return 0;
+	}
+	if ((bn = calloc(1, len)) == NULL) {
+		(void) fprintf(stderr, "hash_bignum: bad bn alloc\n");
+		return 0;
+	}
+	BN_bn2bin(bignum, bn + 1);
+	bn[0] = 0x0;
+	padbyte = (bn[1] & 0x80) ? 1 : 0;
+	hash_string(hash, bn + 1 - padbyte, (unsigned)(len + padbyte));
+	free(bn);
+	return (int)(sizeof(len) + len + padbyte);
+}
+
+/** \file
+ */
+
+/**
+ * \ingroup Core_Keys
+ * \brief Calculate a public key fingerprint.
+ * \param fp Where to put the calculated fingerprint
+ * \param key The key for which the fingerprint is calculated
+ */
+int
+pgp_fingerprint(pgp_fingerprint_t *fp, const pgp_pubkey_t *key, pgp_hash_alg_t hashtype)
+{
+	pgp_memory_t	*mem;
+	pgp_hash_t	 hash;
+	const char	*type;
+	uint32_t	 len;
+
+	mem = pgp_memory_new();
+	if (key->version == 2 || key->version == 3) {
+		if (key->alg != PGP_PKA_RSA &&
+		    key->alg != PGP_PKA_RSA_ENCRYPT_ONLY &&
+		    key->alg != PGP_PKA_RSA_SIGN_ONLY) {
+			(void) fprintf(stderr,
+				"pgp_fingerprint: bad algorithm\n");
+			return 0;
+		}
+		pgp_hash_md5(&hash);
+		if (!hash.init(&hash)) {
+			(void) fprintf(stderr,
+				"pgp_fingerprint: bad md5 alloc\n");
+			return 0;
+		}
+		hash_bignum(&hash, key->key.rsa.n);
+		hash_bignum(&hash, key->key.rsa.e);
+		fp->length = hash.finish(&hash, fp->fingerprint);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "v2/v3 fingerprint", fp->fingerprint, fp->length);
+		}
+	} else if (hashtype == PGP_HASH_MD5) {
+		pgp_hash_md5(&hash);
+		if (!hash.init(&hash)) {
+			(void) fprintf(stderr,
+				"pgp_fingerprint: bad md5 alloc\n");
+			return 0;
+		}
+		type = (key->alg == PGP_PKA_RSA) ? "ssh-rsa" : "ssh-dss";
+		hash_string(&hash, (const uint8_t *)(const void *)type, (unsigned)strlen(type));
+		switch(key->alg) {
+		case PGP_PKA_RSA:
+			hash_bignum(&hash, key->key.rsa.e);
+			hash_bignum(&hash, key->key.rsa.n);
+			break;
+		case PGP_PKA_DSA:
+			hash_bignum(&hash, key->key.dsa.p);
+			hash_bignum(&hash, key->key.dsa.q);
+			hash_bignum(&hash, key->key.dsa.g);
+			hash_bignum(&hash, key->key.dsa.y);
+			break;
+		default:
+			break;
+		}
+		fp->length = hash.finish(&hash, fp->fingerprint);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "md5 fingerprint", fp->fingerprint, fp->length);
+		}
+	} else {
+		pgp_build_pubkey(mem, key, 0);
+		pgp_hash_sha1(&hash);
+		if (!hash.init(&hash)) {
+			(void) fprintf(stderr,
+				"pgp_fingerprint: bad sha1 alloc\n");
+			return 0;
+		}
+		len = (unsigned)pgp_mem_len(mem);
+		pgp_hash_add_int(&hash, 0x99, 1);
+		pgp_hash_add_int(&hash, len, 2);
+		hash.add(&hash, pgp_mem_data(mem), len);
+		fp->length = hash.finish(&hash, fp->fingerprint);
+		pgp_memory_free(mem);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "sha1 fingerprint", fp->fingerprint, fp->length);
+		}
+	}
+	return 1;
+}
+
+/**
+ * \ingroup Core_Keys
+ * \brief Calculate the Key ID from the public key.
+ * \param keyid Space for the calculated ID to be stored
+ * \param key The key for which the ID is calculated
+ */
+
+int
+pgp_keyid(uint8_t *keyid, const size_t idlen, const pgp_pubkey_t *key, pgp_hash_alg_t hashtype)
+{
+	pgp_fingerprint_t finger;
+
+	if (key->version == 2 || key->version == 3) {
+		unsigned	n;
+		uint8_t		bn[NETPGP_BUFSIZ];
+
+		n = (unsigned) BN_num_bytes(key->key.rsa.n);
+		if (n > sizeof(bn)) {
+			(void) fprintf(stderr, "pgp_keyid: bad num bytes\n");
+			return 0;
+		}
+		if (key->alg != PGP_PKA_RSA &&
+		    key->alg != PGP_PKA_RSA_ENCRYPT_ONLY &&
+		    key->alg != PGP_PKA_RSA_SIGN_ONLY) {
+			(void) fprintf(stderr, "pgp_keyid: bad algorithm\n");
+			return 0;
+		}
+		BN_bn2bin(key->key.rsa.n, bn);
+		(void) memcpy(keyid, bn + n - idlen, idlen);
+	} else {
+		pgp_fingerprint(&finger, key, hashtype);
+		(void) memcpy(keyid,
+				finger.fingerprint + finger.length - idlen,
+				idlen);
+	}
+	return 1;
+}
+
+/**
+\ingroup Core_Hashes
+\brief Add to the hash
+\param hash Hash to add to
+\param n Int to add
+\param length Length of int in bytes
+*/
+void
+pgp_hash_add_int(pgp_hash_t *hash, unsigned n, unsigned length)
+{
+	uint8_t   c;
+
+	while (length--) {
+		c = n >> (length * 8);
+		hash->add(hash, &c, 1);
+	}
+}
+
+/**
+\ingroup Core_Hashes
+\brief Setup hash for given hash algorithm
+\param hash Hash to set up
+\param alg Hash algorithm to use
+*/
+unsigned
+pgp_hash_any(pgp_hash_t *hash, pgp_hash_alg_t alg)
+{
+	switch (alg) {
+	case PGP_HASH_MD5:
+		pgp_hash_md5(hash);
+		break;
+
+	case PGP_HASH_SHA1:
+		pgp_hash_sha1(hash);
+		break;
+
+	case PGP_HASH_SHA256:
+		pgp_hash_sha256(hash);
+		break;
+
+	case PGP_HASH_SHA384:
+		pgp_hash_sha384(hash);
+		break;
+
+	case PGP_HASH_SHA512:
+		pgp_hash_sha512(hash);
+		break;
+
+	case PGP_HASH_SHA224:
+		pgp_hash_sha224(hash);
+		break;
+
+	default:
+		(void) fprintf(stderr, "pgp_hash_any: bad algorithm\n");
+        return 0;
+	}
+    return 1;
+}
+
+/**
+\ingroup Core_Hashes
+\brief Returns size of hash for given hash algorithm
+\param alg Hash algorithm to use
+\return Size of hash algorithm in bytes
+*/
+unsigned
+pgp_hash_size(pgp_hash_alg_t alg)
+{
+	switch (alg) {
+	case PGP_HASH_MD5:
+		return 16;
+
+	case PGP_HASH_SHA1:
+		return 20;
+
+	case PGP_HASH_SHA256:
+		return 32;
+
+	case PGP_HASH_SHA224:
+		return 28;
+
+	case PGP_HASH_SHA512:
+		return 64;
+
+	case PGP_HASH_SHA384:
+		return 48;
+
+	default:
+		(void) fprintf(stderr, "pgp_hash_size: bad algorithm\n");
+	}
+
+	return 0;
+}
+
+/**
+\ingroup Core_Hashes
+\brief Returns hash enum corresponding to given string
+\param hash Text name of hash algorithm i.e. "SHA1"
+\returns Corresponding enum i.e. PGP_HASH_SHA1
+*/
+pgp_hash_alg_t
+pgp_str_to_hash_alg(const char *hash)
+{
+	if (hash == NULL) {
+		return PGP_DEFAULT_HASH_ALGORITHM;
+	}
+	if (netpgp_strcasecmp(hash, "SHA1") == 0) {
+		return PGP_HASH_SHA1;
+	}
+	if (netpgp_strcasecmp(hash, "MD5") == 0) {
+		return PGP_HASH_MD5;
+	}
+	if (netpgp_strcasecmp(hash, "SHA256") == 0) {
+		return PGP_HASH_SHA256;
+	}
+	/*
+        if (netpgp_strcasecmp(hash,"SHA224") == 0) {
+		return PGP_HASH_SHA224;
+	}
+        */
+	if (netpgp_strcasecmp(hash, "SHA512") == 0) {
+		return PGP_HASH_SHA512;
+	}
+	if (netpgp_strcasecmp(hash, "SHA384") == 0) {
+		return PGP_HASH_SHA384;
+	}
+	return PGP_HASH_UNKNOWN;
+}
+
+/**
+\ingroup Core_Hashes
+\brief Hash given data
+\param out Where to write the hash
+\param alg Hash algorithm to use
+\param in Data to hash
+\param length Length of data
+\return Size of hash created
+*/
+unsigned
+pgp_hash(uint8_t *out, pgp_hash_alg_t alg, const void *in, size_t length)
+{
+	pgp_hash_t      hash;
+
+	pgp_hash_any(&hash, alg);
+	if (!hash.init(&hash)) {
+		(void) fprintf(stderr, "pgp_hash: bad alloc\n");
+		/* we'll just continue here - don't want to return a 0 hash */
+		/* XXX - agc - no way to return failure */
+	}
+	hash.add(&hash, in, (unsigned)length);
+	return hash.finish(&hash, out);
+}
+
+/**
+\ingroup Core_Hashes
+\brief Calculate hash for MDC packet
+\param preamble Preamble to hash
+\param sz_preamble Size of preamble
+\param plaintext Plaintext to hash
+\param sz_plaintext Size of plaintext
+\param hashed Resulting hash
+*/
+void
+pgp_calc_mdc_hash(const uint8_t *preamble,
+			const size_t sz_preamble,
+			const uint8_t *plaintext,
+			const unsigned sz_plaintext,
+			uint8_t *hashed)
+{
+	pgp_hash_t	hash;
+	uint8_t		c;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "preamble", preamble, sz_preamble);
+		hexdump(stderr, "plaintext", plaintext, sz_plaintext);
+	}
+	/* init */
+	pgp_hash_any(&hash, PGP_HASH_SHA1);
+	if (!hash.init(&hash)) {
+		(void) fprintf(stderr, "pgp_calc_mdc_hash: bad alloc\n");
+		/* we'll just continue here - it will die anyway */
+		/* agc - XXX - no way to return failure */
+	}
+
+	/* preamble */
+	hash.add(&hash, preamble, (unsigned)sz_preamble);
+	/* plaintext */
+	hash.add(&hash, plaintext, sz_plaintext);
+	/* MDC packet tag */
+	c = MDC_PKT_TAG;
+	hash.add(&hash, &c, 1);
+	/* MDC packet len */
+	c = PGP_SHA1_HASH_SIZE;
+	hash.add(&hash, &c, 1);
+
+	/* finish */
+	hash.finish(&hash, hashed);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "hashed", hashed, PGP_SHA1_HASH_SIZE);
+	}
+}
+
+/**
+\ingroup HighLevel_Supported
+\brief Is this Hash Algorithm supported?
+\param hash_alg Hash Algorithm to check
+\return 1 if supported; else 0
+*/
+unsigned
+pgp_is_hash_alg_supported(const pgp_hash_alg_t *hash_alg)
+{
+	switch (*hash_alg) {
+	case PGP_HASH_MD5:
+	case PGP_HASH_SHA1:
+	case PGP_HASH_SHA256:
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+/* structure to map string to cipher def */
+typedef struct str2cipher_t {
+	const char	*s;	/* cipher name */
+	pgp_symm_alg_t i;	/* cipher def */
+} str2cipher_t;
+
+static str2cipher_t	str2cipher[] = {
+	{	"cast5",		PGP_SA_CAST5		},
+	{	"idea",			PGP_SA_IDEA		},
+	{	"aes128",		PGP_SA_AES_128		},
+	{	"aes256",		PGP_SA_AES_256		},
+	{	"camellia128",		PGP_SA_CAMELLIA_128	},
+	{	"camellia256",		PGP_SA_CAMELLIA_256	},
+	{	"tripledes",		PGP_SA_TRIPLEDES	},
+	{	NULL,			0			}
+};
+
+/* convert from a string to a cipher definition */
+pgp_symm_alg_t
+pgp_str_to_cipher(const char *cipher)
+{
+	str2cipher_t	*sp;
+
+	for (sp = str2cipher ; cipher && sp->s ; sp++) {
+		if (netpgp_strcasecmp(cipher, sp->s) == 0) {
+			return sp->i;
+		}
+	}
+	return PGP_SA_DEFAULT_CIPHER;
+}
+
+void
+pgp_random(void *dest, size_t length)
+{
+	RAND_bytes(dest, (int)length);
+}
+
+/**
+\ingroup HighLevel_Memory
+\brief Memory to initialise
+\param mem memory to initialise
+\param needed Size to initialise to
+*/
+void
+pgp_memory_init(pgp_memory_t *mem, size_t needed)
+{
+	uint8_t	*temp;
+
+	mem->length = 0;
+	if (mem->buf) {
+		if (mem->allocated < needed) {
+			if ((temp = realloc(mem->buf, needed)) == NULL) {
+				(void) fprintf(stderr, "pgp_memory_init: bad alloc\n");
+			} else {
+				mem->buf = temp;
+				mem->allocated = needed;
+			}
+		}
+	} else {
+		if ((mem->buf = calloc(1, needed)) == NULL) {
+			(void) fprintf(stderr, "pgp_memory_init: bad alloc\n");
+		} else {
+			mem->allocated = needed;
+		}
+	}
+}
+
+/**
+\ingroup HighLevel_Memory
+\brief Pad memory to required length
+\param mem Memory to use
+\param length New size
+*/
+void
+pgp_memory_pad(pgp_memory_t *mem, size_t length)
+{
+	uint8_t	*temp;
+
+	if (mem->allocated < mem->length) {
+		(void) fprintf(stderr, "pgp_memory_pad: bad alloc in\n");
+		return;
+	}
+	if (mem->allocated < mem->length + length) {
+		mem->allocated = mem->allocated * 2 + length;
+		temp = realloc(mem->buf, mem->allocated);
+		if (temp == NULL) {
+			(void) fprintf(stderr, "pgp_memory_pad: bad alloc\n");
+		} else {
+			mem->buf = temp;
+		}
+	}
+	if (mem->allocated < mem->length + length) {
+		(void) fprintf(stderr, "pgp_memory_pad: bad alloc out\n");
+	}
+}
+
+/**
+\ingroup HighLevel_Memory
+\brief Add data to memory
+\param mem Memory to which to add
+\param src Data to add
+\param length Length of data to add
+*/
+void
+pgp_memory_add(pgp_memory_t *mem, const uint8_t *src, size_t length)
+{
+	pgp_memory_pad(mem, length);
+	(void) memcpy(mem->buf + mem->length, src, length);
+	mem->length += length;
+}
+
+/* XXX: this could be refactored via the writer, but an awful lot of */
+/* hoops to jump through for 2 lines of code! */
+void
+pgp_memory_place_int(pgp_memory_t *mem, unsigned offset, unsigned n,
+		     size_t length)
+{
+	if (mem->allocated < offset + length) {
+		(void) fprintf(stderr,
+			"pgp_memory_place_int: bad alloc\n");
+	} else {
+		while (length-- > 0) {
+			mem->buf[offset++] = n >> (length * 8);
+		}
+	}
+}
+
+/**
+ * \ingroup HighLevel_Memory
+ * \brief Retains allocated memory and set length of stored data to zero.
+ * \param mem Memory to clear
+ * \sa pgp_memory_release()
+ * \sa pgp_memory_free()
+ */
+void
+pgp_memory_clear(pgp_memory_t *mem)
+{
+	mem->length = 0;
+}
+
+/**
+\ingroup HighLevel_Memory
+\brief Free memory and associated data
+\param mem Memory to free
+\note This does not free mem itself
+\sa pgp_memory_clear()
+\sa pgp_memory_free()
+*/
+void
+pgp_memory_release(pgp_memory_t *mem)
+{
+	if (mem->mmapped) {
+		(void) munmap(mem->buf, mem->length);
+	} else {
+		free(mem->buf);
+	}
+	mem->buf = NULL;
+	mem->length = 0;
+}
+
+void
+pgp_memory_make_packet(pgp_memory_t *out, pgp_content_enum tag)
+{
+	size_t          extra;
+
+	extra = (out->length < 192) ? 1 : (out->length < 8192 + 192) ? 2 : 5;
+	pgp_memory_pad(out, extra + 1);
+	memmove(out->buf + extra + 1, out->buf, out->length);
+
+	out->buf[0] = PGP_PTAG_ALWAYS_SET | PGP_PTAG_NEW_FORMAT | tag;
+
+	if (out->length < 192) {
+		out->buf[1] = (uint8_t)out->length;
+	} else if (out->length < 8192 + 192) {
+		out->buf[1] = (uint8_t)((out->length - 192) >> 8) + 192;
+		out->buf[2] = (uint8_t)(out->length - 192);
+	} else {
+		out->buf[1] = 0xff;
+		out->buf[2] = (uint8_t)(out->length >> 24);
+		out->buf[3] = (uint8_t)(out->length >> 16);
+		out->buf[4] = (uint8_t)(out->length >> 8);
+		out->buf[5] = (uint8_t)(out->length);
+	}
+
+	out->length += extra + 1;
+}
+
+/**
+   \ingroup HighLevel_Memory
+   \brief Create a new zeroed pgp_memory_t
+   \return Pointer to new pgp_memory_t
+   \note Free using pgp_memory_free() after use.
+   \sa pgp_memory_free()
+*/
+
+pgp_memory_t   *
+pgp_memory_new(void)
+{
+	return calloc(1, sizeof(pgp_memory_t));
+}
+
+/**
+   \ingroup HighLevel_Memory
+   \brief Free memory ptr and associated memory
+   \param mem Memory to be freed
+   \sa pgp_memory_release()
+   \sa pgp_memory_clear()
+*/
+
+void
+pgp_memory_free(pgp_memory_t *mem)
+{
+	pgp_memory_release(mem);
+	free(mem);
+}
+
+/**
+   \ingroup HighLevel_Memory
+   \brief Get length of data stored in pgp_memory_t struct
+   \return Number of bytes in data
+*/
+size_t
+pgp_mem_len(const pgp_memory_t *mem)
+{
+	return mem->length;
+}
+
+/**
+   \ingroup HighLevel_Memory
+   \brief Get data stored in pgp_memory_t struct
+   \return Pointer to data
+*/
+void *
+pgp_mem_data(pgp_memory_t *mem)
+{
+	return mem->buf;
+}
+
+/* read a gile into an pgp_memory_t */
+int
+pgp_mem_readfile(pgp_memory_t *mem, const char *f)
+{
+	struct stat	 st;
+	FILE		*fp;
+	int		 cc;
+
+	if ((fp = fopen(f, "rb")) == NULL) {
+		(void) fprintf(stderr,
+				"pgp_mem_readfile: can't open \"%s\"\n", f);
+		return 0;
+	}
+	(void) fstat(fileno(fp), &st);
+	mem->allocated = (size_t)st.st_size;
+	mem->buf = mmap(NULL, mem->allocated, PROT_READ,
+				MAP_PRIVATE | MAP_FILE, fileno(fp), 0);
+	if (mem->buf == MAP_FAILED) {
+		/* mmap failed for some reason - try to allocate memory */
+		if ((mem->buf = calloc(1, mem->allocated)) == NULL) {
+			(void) fprintf(stderr, "pgp_mem_readfile: calloc\n");
+			(void) fclose(fp);
+			return 0;
+		}
+		/* read into contents of mem */
+		for (mem->length = 0 ;
+		     (cc = (int)read(fileno(fp), &mem->buf[mem->length],
+					(size_t)(mem->allocated - mem->length))) > 0 ;
+		     mem->length += (size_t)cc) {
+		}
+	} else {
+		mem->length = mem->allocated;
+		mem->mmapped = 1;
+	}
+	(void) fclose(fp);
+	return (mem->allocated == mem->length);
+}
+
+typedef struct {
+	uint16_t  sum;
+} sum16_t;
+
+
+/**
+ * Searches the given map for the given type.
+ * Returns a human-readable descriptive string if found,
+ * returns NULL if not found
+ *
+ * It is the responsibility of the calling function to handle the
+ * error case sensibly (i.e. don't just print out the return string.
+ *
+ */
+static const char *
+str_from_map_or_null(int type, pgp_map_t *map)
+{
+	pgp_map_t      *row;
+
+	for (row = map; row->string != NULL; row++) {
+		if (row->type == type) {
+			return row->string;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * Searches the given map for the given type.
+ * Returns a readable string if found, "Unknown" if not.
+ */
+
+const char     *
+pgp_str_from_map(int type, pgp_map_t *map)
+{
+	const char     *str;
+
+	str = str_from_map_or_null(type, map);
+	return (str) ? str : "Unknown";
+}
+
+#define LINELEN	16
+
+/* show hexadecimal/ascii dump */
+void
+hexdump(FILE *fp, const char *header, const uint8_t *src, size_t length)
+{
+	size_t	i;
+	char	line[LINELEN + 1];
+
+	(void) fprintf(fp, "%s%s", (header) ? header : "", (header) ? "\n" : "");
+	(void) fprintf(fp, "[%" PRIsize "u char%s]\n", length, (length == 1) ? "" : "s");
+	for (i = 0 ; i < length ; i++) {
+		if (i % LINELEN == 0) {
+			(void) fprintf(fp, "%.5" PRIsize "u | ", i);
+		}
+		(void) fprintf(fp, "%.02x ", (uint8_t)src[i]);
+		line[i % LINELEN] = (isprint(src[i])) ? src[i] : '.';
+		if (i % LINELEN == LINELEN - 1) {
+			line[LINELEN] = 0x0;
+			(void) fprintf(fp, " | %s\n", line);
+		}
+	}
+	if (i % LINELEN != 0) {
+		for ( ; i % LINELEN != 0 ; i++) {
+			(void) fprintf(fp, "   ");
+			line[i % LINELEN] = ' ';
+		}
+		line[LINELEN] = 0x0;
+		(void) fprintf(fp, " | %s\n", line);
+	}
+}
+
+/**
+ * \ingroup HighLevel_Functions
+ * \brief Closes down OpenPGP::SDK.
+ *
+ * Close down OpenPGP:SDK, release any resources under the control of
+ * the library.
+ */
+
+void
+pgp_finish(void)
+{
+	pgp_crypto_finish();
+}
+
+static int
+sum16_reader(pgp_stream_t *stream, void *dest_, size_t length, pgp_error_t **errors,
+	     pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	const uint8_t	*dest = dest_;
+	sum16_t		*arg = pgp_reader_get_arg(readinfo);
+	int		 r;
+	int		 n;
+
+	r = pgp_stacked_read(stream, dest_, length, errors, readinfo, cbinfo);
+	if (r < 0) {
+		return r;
+	}
+	for (n = 0; n < r; ++n) {
+		arg->sum = (arg->sum + dest[n]) & 0xffff;
+	}
+	return r;
+}
+
+static void
+sum16_destroyer(pgp_reader_t *readinfo)
+{
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/**
+   \ingroup Internal_Readers_Sum16
+   \param stream Parse settings
+*/
+
+void
+pgp_reader_push_sum16(pgp_stream_t *stream)
+{
+	sum16_t    *arg;
+
+	if ((arg = calloc(1, sizeof(*arg))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_push_sum16: bad alloc\n");
+	} else {
+		pgp_reader_push(stream, sum16_reader, sum16_destroyer, arg);
+	}
+}
+
+/**
+   \ingroup Internal_Readers_Sum16
+   \param stream Parse settings
+   \return sum
+*/
+uint16_t
+pgp_reader_pop_sum16(pgp_stream_t *stream)
+{
+	uint16_t	 sum;
+	sum16_t		*arg;
+
+	arg = pgp_reader_get_arg(pgp_readinfo(stream));
+	sum = arg->sum;
+	pgp_reader_pop(stream);
+	free(arg);
+	return sum;
+}
+
+/* small useful functions for setting the file-level debugging levels */
+/* if the debugv list contains the filename in question, we're debugging it */
+#if 0 //////
+enum {
+	MAX_DEBUG_NAMES = 32
+};
+
+static int      debugc;
+static char    *debugv[MAX_DEBUG_NAMES];
+#endif //////
+
+/* set the debugging level per filename */
+#if 0 //////
+int
+pgp_set_debug_level(const char *f)
+{
+	const char     *name;
+	int             i;
+
+	if (f == NULL) {
+		f = "all";
+	}
+	if ((name = strrchr(f, '/')) == NULL) {
+		name = f;
+	} else {
+		name += 1;
+	}
+	for (i = 0; i < debugc && i < MAX_DEBUG_NAMES; i++) {
+		if (strcmp(debugv[i], name) == 0) {
+			return 1;
+		}
+	}
+	if (i == MAX_DEBUG_NAMES) {
+		return 0;
+	}
+	debugv[debugc++] = netpgp_strdup(name);
+	return 1;
+}
+#endif //////
+
+/* get the debugging level per filename */
+int
+pgp_get_debug_level(const char *f)
+{
+#if 0 //////
+	const char     *name;
+	int             i;
+
+	if ((name = strrchr(f, '/')) == NULL) {
+		name = f;
+	} else {
+		name += 1;
+	}
+	for (i = 0; i < debugc; i++) {
+		if (strcmp(debugv[i], "all") == 0 ||
+		    strcmp(debugv[i], name) == 0) {
+			return 1;
+		}
+	}
+#endif //////
+	return 0;
+}
+
+/* return the version for the library */
+#if 0 //////
+const char *
+pgp_get_info(const char *type)
+{
+	if (strcmp(type, "version") == 0) {
+		return NETPGP_VERSION_STRING;
+	}
+	if (strcmp(type, "maintainer") == 0) {
+		return NETPGP_MAINTAINER;
+	}
+	return "[unknown]";
+}
+#endif //////
+
+/* local version of asprintf so we don't have to play autoconf games */
+int
+pgp_asprintf(char **ret, const char *fmt, ...)
+{
+	va_list args;
+	char    buf[120 * 1024];	/* XXX - "huge" buffer on stack */
+	int     cc;
+
+	va_start(args, fmt);
+	cc = vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+	if ((*ret = calloc(1, (size_t)(cc + 1))) == NULL) {
+		*ret = NULL;
+		return -1;
+	}
+	(void) memcpy(*ret, buf, (size_t)cc);
+	(*ret)[cc] = 0x0;
+	return cc;
+}
+
+void
+netpgp_log(const char *fmt, ...)
+{
+	va_list	 vp;
+	time_t	 t;
+	char	 buf[BUFSIZ * 2];
+	int	 cc;
+
+	(void) time(&t);
+	cc = snprintf(buf, sizeof(buf), "%.24s: netpgp: ", ctime(&t));
+	va_start(vp, fmt);
+	(void) vsnprintf(&buf[cc], sizeof(buf) - (size_t)cc, fmt, vp);
+	va_end(vp);
+	/* do something with message */
+	/* put into log buffer? */
+}
+
+/* portable replacement for strdup(3) */
+char *
+netpgp_strdup(const char *s)
+{
+	size_t	 len;
+	char	*cp;
+
+	len = strlen(s);
+	if ((cp = calloc(1, len + 1)) != NULL) {
+		(void) memcpy(cp, s, len);
+		cp[len] = 0x0;
+	}
+	return cp;
+}
+
+/* portable replacement for strcasecmp(3) */
+int
+netpgp_strcasecmp(const char *s1, const char *s2)
+{
+	int	n;
+
+	for (n = 0 ; *s1 && *s2 && (n = tolower((uint8_t)*s1) - tolower((uint8_t)*s2)) == 0 ; s1++, s2++) {
+	}
+	return n;
+}
diff --git a/netpgp/netpgp-extra.h b/netpgp/netpgp-extra.h
new file mode 100644
index 0000000000000000000000000000000000000000..15b5353a4ea717e6e40f0285754fcb8c3d7fbf1a
--- /dev/null
+++ b/netpgp/netpgp-extra.h
@@ -0,0 +1,15 @@
+#ifndef __NETPGP_EXTRA_H__
+#define __NETPGP_EXTRA_H__
+
+#include "netpgp/config-netpgp.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/errors.h"
+#include "netpgp/defs.h"
+#include "netpgp/crypto.h"
+#include "netpgp/create.h"
+#include "netpgp/signature.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/validate.h"
+unsigned rsa_generate_keypair(pgp_key_t *keydata, const int numbits, const unsigned long e, const char *hashalg, const char *cipher);
+
+#endif // __NETPGP_EXTRA_H__
diff --git a/netpgp/netpgp/config-netpgp.h b/netpgp/netpgp/config-netpgp.h
new file mode 100644
index 0000000000000000000000000000000000000000..610b8eacd306f1b4ab804efc62ccf2251f66bfbb
--- /dev/null
+++ b/netpgp/netpgp/config-netpgp.h
@@ -0,0 +1,201 @@
+
+#ifndef __CONFIG_NETPGP_H__
+#define __CONFIG_NETPGP_H__
+
+
+/* src/lib/config.h.  Generated from config.h.in by configure.  */
+/* src/lib/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/*#undef HAVE_BZLIB_H 1*/ /* EDIT BY MR */
+
+/* Define to 1 if you have the <CommonCrypto/CommonDigest.h> header file. */
+/* #undef HAVE_COMMONCRYPTO_COMMONDIGEST_H */
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <dmalloc.h> header file. */
+/* #undef HAVE_DMALLOC_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <openssl/aes.h> header file. */
+#define HAVE_OPENSSL_AES_H 1
+
+/* Define to 1 if you have the <openssl/bn.h> header file. */
+#define HAVE_OPENSSL_BN_H 1
+
+/* Define to 1 if you have the <openssl/camellia.h> header file. */
+#define HAVE_OPENSSL_CAMELLIA_H 1
+/*#ifndef OPENSSL_NO_CAMELLIA
+#define OPENSSL_NO_CAMELLIA
+#endif*/
+
+/* Define to 1 if you have the <openssl/cast.h> header file. */
+#define HAVE_OPENSSL_CAST_H 1
+
+/* Define to 1 if you have the <openssl/des.h> header file. */
+#define HAVE_OPENSSL_DES_H 1
+
+/* Define to 1 if you have the <openssl/dsa.h> header file. */
+#define HAVE_OPENSSL_DSA_H 1
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#define HAVE_OPENSSL_ERR_H 1
+
+/* Define to 1 if you have the <openssl/idea.h> header file. */
+/* #undef HAVE_OPENSSL_IDEA_H */
+#ifndef OPENSSL_NO_IDEA
+#define OPENSSL_NO_IDEA /* we would have IDEA on android, but we don't need it for netpgp; moreover, we want the same code on different systems */
+#endif 
+
+/* Define to 1 if you have the <openssl/md5.h> header file. */
+#define HAVE_OPENSSL_MD5_H 1
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#define HAVE_OPENSSL_RAND_H 1
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+#define HAVE_OPENSSL_RSA_H 1
+
+/* Define to 1 if you have the <openssl/sha.h> header file. */
+#define HAVE_OPENSSL_SHA_H 1
+
+/* Define to 1 if the system has the type `SHA256_CTX'. */
+#define HAVE_SHA256_CTX 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "netpgp"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "Alistair Crooks <agc@netbsd.org> c0596823"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "netpgp"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "netpgp 20140220"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "netpgp"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "20140220"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "20140220"
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+
+#endif /* __CONFIG_NETPGP_H__ */
diff --git a/netpgp/netpgp/config-original-as-configured-for-ubuntu-16.04-64bit.h b/netpgp/netpgp/config-original-as-configured-for-ubuntu-16.04-64bit.h
new file mode 100644
index 0000000000000000000000000000000000000000..5163cff2cc359b35d61d9841f6a2c65f25a89507
--- /dev/null
+++ b/netpgp/netpgp/config-original-as-configured-for-ubuntu-16.04-64bit.h
@@ -0,0 +1,187 @@
+/* src/lib/config.h.  Generated from config.h.in by configure.  */
+/* src/lib/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+#define HAVE_BZLIB_H 1
+
+/* Define to 1 if you have the <CommonCrypto/CommonDigest.h> header file. */
+/* #undef HAVE_COMMONCRYPTO_COMMONDIGEST_H */
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <dmalloc.h> header file. */
+/* #undef HAVE_DMALLOC_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <openssl/aes.h> header file. */
+#define HAVE_OPENSSL_AES_H 1
+
+/* Define to 1 if you have the <openssl/bn.h> header file. */
+#define HAVE_OPENSSL_BN_H 1
+
+/* Define to 1 if you have the <openssl/camellia.h> header file. */
+#define HAVE_OPENSSL_CAMELLIA_H 1
+
+/* Define to 1 if you have the <openssl/cast.h> header file. */
+#define HAVE_OPENSSL_CAST_H 1
+
+/* Define to 1 if you have the <openssl/des.h> header file. */
+#define HAVE_OPENSSL_DES_H 1
+
+/* Define to 1 if you have the <openssl/dsa.h> header file. */
+#define HAVE_OPENSSL_DSA_H 1
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#define HAVE_OPENSSL_ERR_H 1
+
+/* Define to 1 if you have the <openssl/idea.h> header file. */
+/* #undef HAVE_OPENSSL_IDEA_H */
+
+/* Define to 1 if you have the <openssl/md5.h> header file. */
+#define HAVE_OPENSSL_MD5_H 1
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#define HAVE_OPENSSL_RAND_H 1
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+#define HAVE_OPENSSL_RSA_H 1
+
+/* Define to 1 if you have the <openssl/sha.h> header file. */
+#define HAVE_OPENSSL_SHA_H 1
+
+/* Define to 1 if the system has the type `SHA256_CTX'. */
+#define HAVE_SHA256_CTX 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "netpgp"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "Alistair Crooks <agc@netbsd.org> c0596823"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "netpgp"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "netpgp 20140220"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "netpgp"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "20140220"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "20140220"
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
diff --git a/netpgp/netpgp/config-pep.h b/netpgp/netpgp/config-pep.h
new file mode 100644
index 0000000000000000000000000000000000000000..46097add4fc87469484f7f1c6e7a9f855d465a14
--- /dev/null
+++ b/netpgp/netpgp/config-pep.h
@@ -0,0 +1,160 @@
+//
+//  config.h
+//  netpgp
+
+#ifndef netpgp_config_h
+#define netpgp_config_h
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+#define HAVE_BZLIB_H 1
+
+/* Define to 1 if you have the <CommonCrypto/CommonDigest.h> header file. */
+#define HAVE_COMMONCRYPTO_COMMONDIGEST_H 1
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <dmalloc.h> header file. */
+/* #undef HAVE_DMALLOC_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if the system has the type 'long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+/* #undef HAVE_MALLOC_H */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <openssl/aes.h> header file. */
+#define HAVE_OPENSSL_AES_H 1
+
+/* Define to 1 if you have the <openssl/bn.h> header file. */
+#define HAVE_OPENSSL_BN_H 1
+
+/* Define to 1 if you have the <openssl/camellia.h> header file. */
+/* #undef HAVE_OPENSSL_CAMELLIA_H */
+
+/* Define to 1 if you have the <openssl/cast.h> header file. */
+#define HAVE_OPENSSL_CAST_H 1
+
+/* Define to 1 if you have the <openssl/des.h> header file. */
+#define HAVE_OPENSSL_DES_H 1
+
+/* Define to 1 if you have the <openssl/dsa.h> header file. */
+#define HAVE_OPENSSL_DSA_H 1
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#define HAVE_OPENSSL_ERR_H 1
+
+/* Define to 1 if you have the <openssl/idea.h> header file. */
+/* #undef HAVE_OPENSSL_IDEA_H */
+
+/* Define to 1 if you have the <openssl/md5.h> header file. */
+#define HAVE_OPENSSL_MD5_H 1
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#define HAVE_OPENSSL_RAND_H 1
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+#define HAVE_OPENSSL_RSA_H 1
+
+/* Define to 1 if you have the <openssl/sha.h> header file. */
+#define HAVE_OPENSSL_SHA_H 1
+
+/* Define to 1 if the system has the type `SHA256_CTX'. */
+#define HAVE_SHA256_CTX 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the system has the type 'unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "netpgp"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "libetpan-devel@lists.sourceforge.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "netpgp"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "netpgp beta0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "netpgp"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "beta0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "beta0"
+
+#define OPENSSL_NO_IDEA 1
+
+#endif
diff --git a/netpgp/netpgp/create.h b/netpgp/netpgp/create.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7782d7ecaf2285cee31b31ec0e7f6424d36dcbe
--- /dev/null
+++ b/netpgp/netpgp/create.h
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+/** \file
+ */
+#ifndef CREATE_H_
+#define CREATE_H_
+
+#include "types.h"
+#include "packet.h"
+#include "crypto.h"
+#include "errors.h"
+#include "keyring.h"
+#include "writer.h"
+#include "memory.h"
+
+/**
+ * \ingroup Create
+ * This struct contains the required information about how to write this stream
+ */
+struct pgp_output_t {
+	pgp_writer_t	 writer;
+	pgp_error_t	*errors;	/* error stack */
+};
+
+pgp_output_t *pgp_output_new(void);
+void pgp_output_delete(pgp_output_t *);
+
+int pgp_filewrite(const char *, const char *, const size_t, const unsigned);
+
+void pgp_build_pubkey(pgp_memory_t *, const pgp_pubkey_t *, unsigned);
+
+unsigned pgp_calc_sesskey_checksum(pgp_pk_sesskey_t *, uint8_t *);
+unsigned pgp_write_struct_userid(pgp_output_t *, const uint8_t *);
+unsigned pgp_write_ss_header(pgp_output_t *, size_t, pgp_content_enum);
+unsigned pgp_write_struct_seckey_ptag(const pgp_seckey_t *key,
+			    const uint8_t *passphrase,
+			    const size_t pplen,
+			    pgp_output_t *output,
+	            pgp_content_enum ptag);
+unsigned pgp_write_struct_seckey(const pgp_seckey_t *,
+			    const uint8_t *,
+			    const size_t,
+			    pgp_output_t *);
+unsigned pgp_write_struct_pubkey(pgp_output_t *, const pgp_pubkey_t *);
+unsigned pgp_write_one_pass_sig(pgp_output_t *,
+				const pgp_seckey_t *,
+				const pgp_hash_alg_t,
+				const pgp_sig_type_t);
+unsigned pgp_write_litdata(pgp_output_t *, 
+				const uint8_t *,
+				const int,
+				const pgp_litdata_enum);
+pgp_pk_sesskey_t *pgp_create_pk_sesskey(pgp_key_t *, const char *, pgp_pk_sesskey_t *);
+unsigned pgp_write_pk_sesskey(pgp_output_t *, pgp_pk_sesskey_t *);
+unsigned pgp_write_xfer_key(pgp_output_t *output,
+            const pgp_key_t *key,
+            const unsigned armoured);
+
+void pgp_fast_create_userid(uint8_t **, uint8_t *);
+unsigned pgp_write_userid(const uint8_t *, pgp_output_t *);
+void pgp_fast_create_rsa_pubkey(pgp_pubkey_t *, time_t, BIGNUM *, BIGNUM *);
+unsigned pgp_write_rsa_pubkey(time_t, const BIGNUM *, const BIGNUM *,
+				pgp_output_t *);
+void pgp_fast_create_rsa_seckey(pgp_seckey_t *, time_t, BIGNUM *,
+				BIGNUM *, BIGNUM *, BIGNUM *,
+				BIGNUM *, BIGNUM *);
+unsigned encode_m_buf(const uint8_t *, size_t, const pgp_pubkey_t *,
+				uint8_t *);
+unsigned pgp_fileread_litdata(const char *, const pgp_litdata_enum,
+				pgp_output_t *);
+unsigned pgp_write_symm_enc_data(const uint8_t *, const int,
+				pgp_output_t *);
+
+#endif /* CREATE_H_ */
diff --git a/netpgp/netpgp/crypto.h b/netpgp/netpgp/crypto.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf80412cbc2515ece8a337cabd290b1d7d364cb7
--- /dev/null
+++ b/netpgp/netpgp/crypto.h
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef CRYPTO_H_
+#define CRYPTO_H_
+
+#include "keyring.h"
+#include "packet.h"
+#include "memory.h"
+#include "packet-parse.h"
+
+#include <openssl/dsa.h>
+
+#define PGP_MIN_HASH_SIZE	16
+
+/** pgp_hash_t */
+struct pgp_hash_t {
+	pgp_hash_alg_t		 alg;		/* algorithm */
+	size_t			 size;		/* size */
+	const char		*name;		/* what it's known as */
+	int			(*init)(pgp_hash_t *);
+	void			(*add)(pgp_hash_t *, const uint8_t *, unsigned);
+	unsigned		(*finish)(pgp_hash_t *, uint8_t *);
+	void		 	*data;		/* blob for data */
+};
+
+/** pgp_crypt_t */
+struct pgp_crypt_t {
+	pgp_symm_alg_t	alg;
+	size_t			blocksize;
+	size_t			keysize;
+	void 			(*set_iv)(pgp_crypt_t *, const uint8_t *);
+	void			(*set_crypt_key)(pgp_crypt_t *, const uint8_t *);
+	int			(*base_init)(pgp_crypt_t *);
+	void			(*decrypt_resync)(pgp_crypt_t *);
+	/* encrypt/decrypt one block */
+	void			(*block_encrypt)(pgp_crypt_t *, void *, const void *);
+	void			(*block_decrypt)(pgp_crypt_t *, void *, const void *);
+	/* Standard CFB encrypt/decrypt (as used by Sym Enc Int Prot packets) */
+	void 			(*cfb_encrypt)(pgp_crypt_t *, void *, const void *, size_t);
+	void			(*cfb_decrypt)(pgp_crypt_t *, void *, const void *, size_t);
+	void			(*decrypt_finish)(pgp_crypt_t *);
+	uint8_t			iv[PGP_MAX_BLOCK_SIZE];
+	uint8_t			civ[PGP_MAX_BLOCK_SIZE];
+	uint8_t			siv[PGP_MAX_BLOCK_SIZE];
+		/* siv is needed for weird v3 resync */
+	uint8_t			key[PGP_MAX_KEY_SIZE];
+	int			num;
+		/* num is offset - see openssl _encrypt doco */
+	void			*encrypt_key;
+	void			*decrypt_key;
+};
+
+typedef struct pgp_validation_t {
+	unsigned		 validc;
+	pgp_sig_info_t	*valid_sigs;
+	unsigned		 invalidc;
+	pgp_sig_info_t	*invalid_sigs;
+	unsigned		 unknownc;
+	pgp_sig_info_t	*unknown_sigs;
+	time_t			 birthtime;
+	time_t			 duration;
+} pgp_validation_t;
+
+void pgp_crypto_finish(void);
+void pgp_hash_md5(pgp_hash_t *);
+void pgp_hash_sha1(pgp_hash_t *);
+void pgp_hash_sha256(pgp_hash_t *);
+void pgp_hash_sha512(pgp_hash_t *);
+void pgp_hash_sha384(pgp_hash_t *);
+void pgp_hash_sha224(pgp_hash_t *);
+unsigned pgp_hash_any(pgp_hash_t *, pgp_hash_alg_t);
+pgp_hash_alg_t pgp_str_to_hash_alg(const char *);
+const char *pgp_text_from_hash(pgp_hash_t *);
+unsigned pgp_hash_size(pgp_hash_alg_t);
+unsigned pgp_hash(uint8_t *, pgp_hash_alg_t, const void *, size_t);
+
+void pgp_hash_add_int(pgp_hash_t *, unsigned, unsigned);
+
+unsigned pgp_dsa_verify(const uint8_t *, size_t,
+			const pgp_dsa_sig_t *,
+			const pgp_dsa_pubkey_t *);
+
+int pgp_rsa_public_decrypt(uint8_t *, const uint8_t *, size_t,
+			const pgp_rsa_pubkey_t *);
+int pgp_rsa_public_encrypt(uint8_t *, const uint8_t *, size_t,
+			const pgp_rsa_pubkey_t *);
+
+int pgp_rsa_private_encrypt(uint8_t *, const uint8_t *, size_t,
+			const pgp_rsa_seckey_t *, const pgp_rsa_pubkey_t *);
+int pgp_rsa_private_decrypt(uint8_t *, const uint8_t *, size_t,
+			const pgp_rsa_seckey_t *, const pgp_rsa_pubkey_t *);
+
+int pgp_elgamal_public_encrypt(uint8_t *, uint8_t *, const uint8_t *, size_t,
+			const pgp_elgamal_pubkey_t *);
+int pgp_elgamal_private_decrypt(uint8_t *, const uint8_t *, const uint8_t *, size_t,
+			const pgp_elgamal_seckey_t *, const pgp_elgamal_pubkey_t *);
+
+pgp_symm_alg_t pgp_str_to_cipher(const char *);
+unsigned pgp_block_size(pgp_symm_alg_t);
+unsigned pgp_key_size(pgp_symm_alg_t);
+
+int pgp_decrypt_data(pgp_content_enum, pgp_region_t *,
+			pgp_stream_t *);
+
+int pgp_crypt_any(pgp_crypt_t *, pgp_symm_alg_t);
+void pgp_decrypt_init(pgp_crypt_t *);
+void pgp_encrypt_init(pgp_crypt_t *);
+size_t pgp_decrypt_se(pgp_crypt_t *, void *, const void *, size_t);
+size_t pgp_encrypt_se(pgp_crypt_t *, void *, const void *, size_t);
+size_t pgp_decrypt_se_ip(pgp_crypt_t *, void *, const void *, size_t);
+size_t pgp_encrypt_se_ip(pgp_crypt_t *, void *, const void *, size_t);
+unsigned pgp_is_sa_supported(pgp_symm_alg_t);
+
+void pgp_reader_push_decrypt(pgp_stream_t *, pgp_crypt_t *,
+			pgp_region_t *);
+void pgp_reader_pop_decrypt(pgp_stream_t *);
+
+/* Hash everything that's read */
+void pgp_reader_push_hash(pgp_stream_t *, pgp_hash_t *);
+void pgp_reader_pop_hash(pgp_stream_t *);
+
+int pgp_decrypt_decode_mpi(uint8_t *, unsigned, const BIGNUM *,
+			const BIGNUM *, const pgp_seckey_t *);
+
+unsigned pgp_rsa_encrypt_mpi(const uint8_t *, const size_t,
+			const pgp_pubkey_t *,
+			pgp_pk_sesskey_params_t *);
+unsigned pgp_elgamal_encrypt_mpi(const uint8_t *, const size_t,
+			const pgp_pubkey_t *,
+			pgp_pk_sesskey_params_t *);
+
+/* Encrypt everything that's written */
+struct pgp_key_data;
+void pgp_writer_push_encrypt(pgp_output_t *,
+			const struct pgp_key_data *);
+
+unsigned   pgp_encrypt_file(pgp_io_t *, const char *, const char *,
+			const pgp_key_t *,
+			const unsigned, const unsigned, const char *);
+unsigned   pgp_decrypt_file(pgp_io_t *,
+			const char *,
+			const char *,
+			pgp_keyring_t *,
+			pgp_keyring_t *,
+			const unsigned,
+			const unsigned,
+			const unsigned,
+			void *,
+			int,
+			pgp_cbfunc_t *);
+
+pgp_memory_t *
+pgp_encrypt_buf(pgp_io_t *, const void *, const size_t,
+			const pgp_keyring_t *,
+			const unsigned, const char *, unsigned);
+pgp_memory_t *
+pgp_decrypt_buf(pgp_io_t *,
+			const void *,
+			const size_t,
+			pgp_keyring_t *,
+			pgp_keyring_t *,
+			const unsigned,
+			const unsigned,
+			void *,
+			int,
+			pgp_cbfunc_t *);
+
+pgp_memory_t *
+pgp_decrypt_and_validate_buf(pgp_io_t *io,
+			pgp_validation_t *result,
+			const void *input,
+			const size_t insize,
+			pgp_keyring_t *secring,
+			pgp_keyring_t *pubring,
+			const unsigned use_armour,
+            key_id_t **recipients_key_ids,
+            unsigned *recipients_count);
+
+/* Keys */
+#if 0 //////
+pgp_key_t  *pgp_rsa_new_selfsign_key(const int,
+			const unsigned long, const uint8_t *, const char *,
+			const char *);
+#endif //////
+
+unsigned    pgp_rsa_generate_keypair(pgp_key_t *,
+			const int,
+			const unsigned long,
+			const char *,
+			const char *,
+            const uint8_t *,
+            const size_t);
+
+int pgp_dsa_size(const pgp_dsa_pubkey_t *);
+pgp_dsa_sig_t *pgp_dsa_sign(uint8_t *, unsigned,
+				const pgp_dsa_seckey_t *,
+				const pgp_dsa_pubkey_t *);
+
+/** pgp_reader_t */
+struct pgp_reader_t {
+	pgp_reader_func_t	*reader; /* reader func to get parse data */
+	pgp_reader_destroyer_t	*destroyer;
+	void			*arg;	/* args to pass to reader function */
+	unsigned		 accumulate:1;	/* set to gather packet data */
+	uint8_t			*accumulated;	/* the accumulated data */
+	unsigned		 asize;	/* size of the buffer */
+	unsigned		 alength;/* used buffer */
+	unsigned		 position;	/* reader-specific offset */
+	pgp_reader_t		*next;
+	pgp_stream_t		*parent;/* parent parse_info structure */
+
+    unsigned		 partial_read:1;
+    unsigned		 coalescing:1;
+    /* used for partial length coalescing */
+    unsigned		 virtualc;
+    unsigned		 virtualoff;
+    uint8_t			*virtualpkt;
+};
+
+
+/** pgp_cryptinfo_t
+ Encrypt/decrypt settings
+*/
+struct pgp_cryptinfo_t {
+	char			*passphrase;
+	pgp_keyring_t		*secring;
+    pgp_key_t		*keydata;
+	pgp_cbfunc_t		*getpassphrase;
+	pgp_keyring_t		*pubring;
+	DYNARRAY(key_id_t, recipients_key_ids);
+};
+
+/** pgp_cbdata_t */
+struct pgp_cbdata_t {
+	pgp_cbfunc_t		*cbfunc;	/* callback function */
+	void			*arg;	/* args to pass to callback func */
+	pgp_error_t		**errors; /* address of error stack */
+	pgp_cbdata_t		*next;
+	pgp_output_t		*output;	/* when writing out parsed info */
+	pgp_io_t		*io;		/* error/output messages */
+	void			*passfp;	/* fp for passphrase input */
+	pgp_cryptinfo_t		 cryptinfo;	/* used when decrypting */
+	pgp_printstate_t	 printstate;	/* used to keep printing state */
+	pgp_seckey_t		*sshseckey;	/* secret key for ssh */
+	int			 numtries;	/* # of passphrase attempts */
+	int			 gotpass;	/* when passphrase entered */
+};
+
+/** pgp_hashtype_t */
+typedef struct {
+	pgp_hash_t	hash;	/* hashes we should hash data with */
+	uint8_t	keyid[PGP_KEY_ID_SIZE];
+} pgp_hashtype_t;
+
+#define NTAGS	0x100	/* == 256 */
+
+/** \brief Structure to hold information about a packet parse.
+ *
+ *  This information includes options about the parse:
+ *  - whether the packet contents should be accumulated or not
+ *  - whether signature subpackets should be parsed or left raw
+ *
+ *  It contains options specific to the parsing of armoured data:
+ *  - whether headers are allowed in armoured data without a gap
+ *  - whether a blank line is allowed at the start of the armoured data
+ *
+ *  It also specifies :
+ *  - the callback function to use and its arguments
+ *  - the reader function to use and its arguments
+ *
+ *  It also contains information about the current state of the parse:
+ *  - offset from the beginning
+ *  - the accumulated data, if any
+ *  - the size of the buffer, and how much has been used
+ *
+ *  It has a linked list of errors.
+ */
+
+struct pgp_stream_t {
+	uint8_t		 	ss_raw[NTAGS / 8];
+		/* 1 bit / sig-subpkt type; set to get raw data */
+	uint8_t		 	ss_parsed[NTAGS / 8];
+		/* 1 bit / sig-subpkt type; set to get parsed data */
+	pgp_reader_t	 	 readinfo;
+	pgp_cbdata_t		 cbinfo;
+	pgp_error_t		*errors;
+	void			*io;		/* io streams */
+	pgp_crypt_t		 decrypt;
+	pgp_cryptinfo_t		 cryptinfo;
+	size_t			 hashc;
+	pgp_hashtype_t		*hashes;
+	//unsigned		 reading_v3_secret:1;
+	//unsigned		 reading_mpi_len:1;
+	//unsigned		 exact_read:1;
+
+};
+
+#endif /* CRYPTO_H_ */
diff --git a/netpgp/netpgp/defs.h b/netpgp/netpgp/defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..760f1d309a4c2ed774f398ce9046086c1fe1bb1f
--- /dev/null
+++ b/netpgp/netpgp/defs.h
@@ -0,0 +1,92 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef DEFS_H_
+#define DEFS_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NEWARRAY(type,ptr,size,where,action) do {			\
+	if ((ptr = calloc(sizeof(type), (unsigned)(size))) == NULL) {	\
+		(void) fprintf(stderr, "%s: can't allocate %lu bytes\n", \
+			where, (unsigned long)(size * sizeof(type)));	\
+		action;							\
+	}								\
+} while( /* CONSTCOND */ 0)
+
+#define RENEW(type,ptr,size,where,action) do {				\
+	type *_newptr;							\
+	_newptr = realloc(ptr, (size_t)(sizeof(type) * (size)));	\
+	if (_newptr == NULL) {						\
+		(void) fprintf(stderr, "%s: can't realloc %lu bytes\n",	\
+			where, (unsigned long)(size * sizeof(type)));	\
+		action;							\
+	} else {							\
+		ptr = _newptr;						\
+	}								\
+} while( /* CONSTCOND */ 0)
+
+#define NEW(type, ptr, where, action)	NEWARRAY(type, ptr, 1, where, action)
+
+#define FREE(ptr)	(void) free(ptr)
+
+#define ALLOC(type, v, size, c, init, incr, where, action) do {		\
+	uint32_t	_newsize = size;				\
+	if (size == 0) {						\
+		_newsize = init;					\
+		NEWARRAY(type, v, _newsize, where ": new", action);	\
+	} else if (c == size) {						\
+		_newsize = size + incr;					\
+		RENEW(type, v, _newsize, where ": renew", action);	\
+	}								\
+	size = _newsize;						\
+} while( /* CONSTCOND */ 0)
+
+#define DEFINE_ARRAY(name, type)					\
+typedef struct name {							\
+	uint32_t	c;						\
+	uint32_t	size;						\
+	type	       *v;						\
+} name
+
+#endif /* !DEFS_H_ */
diff --git a/netpgp/netpgp/errors.h b/netpgp/netpgp/errors.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab518d08c67c61cc3dae8cbd44e6318a5a968ad0
--- /dev/null
+++ b/netpgp/netpgp/errors.h
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef ERRORS_H_
+#define ERRORS_H_
+
+#include <errno.h>
+
+#ifndef __printflike
+#define __printflike(n, m)	__attribute__((format(printf,n,m)))
+#endif
+
+/** error codes */
+/* Remember to add names to map in errors.c */
+typedef enum {
+	PGP_E_OK = 0x0000,	/* no error */
+	PGP_E_FAIL = 0x0001,	/* general error */
+	PGP_E_SYSTEM_ERROR = 0x0002,	/* system error, look at errno for
+					 * details */
+	PGP_E_UNIMPLEMENTED = 0x0003,	/* feature not yet implemented */
+
+	/* reader errors */
+	PGP_E_R = 0x1000,	/* general reader error */
+	PGP_E_R_READ_FAILED = PGP_E_R + 1,
+	PGP_E_R_EARLY_EOF = PGP_E_R + 2,
+	PGP_E_R_BAD_FORMAT = PGP_E_R + 3,	/* For example, malformed
+						 * armour */
+	PGP_E_R_UNSUPPORTED = PGP_E_R + 4,
+	PGP_E_R_UNCONSUMED_DATA = PGP_E_R + 5,
+
+	/* writer errors */
+	PGP_E_W = 0x2000,	/* general writer error */
+	PGP_E_W_WRITE_FAILED = PGP_E_W + 1,
+	PGP_E_W_WRITE_TOO_SHORT = PGP_E_W + 2,
+
+	/* parser errors */
+	PGP_E_P = 0x3000,	/* general parser error */
+	PGP_E_P_NOT_ENOUGH_DATA = PGP_E_P + 1,
+	PGP_E_P_UNKNOWN_TAG = PGP_E_P + 2,
+	PGP_E_P_PACKET_CONSUMED = PGP_E_P + 3,
+	PGP_E_P_MPI_FORMAT_ERROR = PGP_E_P + 4,
+	PGP_E_P_PACKET_NOT_CONSUMED = PGP_E_P + 5,
+	PGP_E_P_DECOMPRESSION_ERROR = PGP_E_P + 6,
+	PGP_E_P_NO_USERID = PGP_E_P + 7,
+
+	/* creator errors */
+	PGP_E_C = 0x4000,	/* general creator error */
+
+	/* validation errors */
+	PGP_E_V = 0x5000,	/* general validation error */
+	PGP_E_V_BAD_SIGNATURE = PGP_E_V + 1,
+	PGP_E_V_NO_SIGNATURE = PGP_E_V + 2,
+	PGP_E_V_UNKNOWN_SIGNER = PGP_E_V + 3,
+	PGP_E_V_BAD_HASH = PGP_E_V + 4,
+
+	/* Algorithm support errors */
+	PGP_E_ALG = 0x6000,	/* general algorithm error */
+	PGP_E_ALG_UNSUPPORTED_SYMMETRIC_ALG = PGP_E_ALG + 1,
+	PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG = PGP_E_ALG + 2,
+	PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG = PGP_E_ALG + 3,
+	PGP_E_ALG_UNSUPPORTED_HASH_ALG = PGP_E_ALG + 4,
+	PGP_E_ALG_UNSUPPORTED_COMPRESS_ALG = PGP_E_ALG + 5,
+
+	/* Protocol errors */
+	PGP_E_PROTO = 0x7000,	/* general protocol error */
+	PGP_E_PROTO_BAD_SYMMETRIC_DECRYPT = PGP_E_PROTO + 2,
+	PGP_E_PROTO_UNKNOWN_SS = PGP_E_PROTO + 3,
+	PGP_E_PROTO_CRITICAL_SS_IGNORED = PGP_E_PROTO + 4,
+	PGP_E_PROTO_BAD_PUBLIC_KEY_VRSN = PGP_E_PROTO + 5,
+	PGP_E_PROTO_BAD_SIGNATURE_VRSN = PGP_E_PROTO + 6,
+	PGP_E_PROTO_BAD_ONE_PASS_SIG_VRSN = PGP_E_PROTO + 7,
+	PGP_E_PROTO_BAD_PKSK_VRSN = PGP_E_PROTO + 8,
+	PGP_E_PROTO_DECRYPTED_MSG_WRONG_LEN = PGP_E_PROTO + 9,
+	PGP_E_PROTO_BAD_SK_CHECKSUM = PGP_E_PROTO + 10
+} pgp_errcode_t;
+
+/** one entry in a linked list of errors */
+typedef struct pgp_error {
+	pgp_errcode_t		errcode;
+	int			sys_errno;	/* irrelevent unless errcode ==
+					 * PGP_E_SYSTEM_ERROR */
+	char			*comment;
+	const char		*file;
+	int			 line;
+	struct pgp_error	*next;
+} pgp_error_t;
+
+const char     *pgp_errcode(const pgp_errcode_t);
+
+void 
+pgp_push_error(pgp_error_t **, pgp_errcode_t,
+		int,
+		const char *, int, const char *,...) __printflike(6, 7);
+void pgp_print_error(pgp_error_t *);
+void pgp_print_errors(pgp_error_t *);
+void pgp_free_errors(pgp_error_t *);
+int  pgp_has_error(pgp_error_t *, pgp_errcode_t);
+
+#define PGP_SYSTEM_ERROR_1(err,code,sys,fmt,arg)	do {		\
+	pgp_push_error(err,PGP_E_SYSTEM_ERROR,errno,__FILE__,__LINE__,sys);\
+	pgp_push_error(err,code,0,__FILE__,__LINE__,fmt,arg);		\
+} while(/*CONSTCOND*/0)
+
+#define PGP_MEMORY_ERROR(err) {						\
+	fprintf(stderr, "Memory error\n");				\
+}				/* \todo placeholder for better error
+				 * handling */
+#define PGP_ERROR_1(err,code,fmt,arg)	do {				\
+	pgp_push_error(err,code,0,__FILE__,__LINE__,fmt,arg);		\
+} while(/*CONSTCOND*/0)
+#define PGP_ERROR_2(err,code,fmt,arg,arg2)	do {			\
+	pgp_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2);	\
+} while(/*CONSTCOND*/0)
+#define PGP_ERROR_3(err,code,fmt,arg,arg2,arg3)	do {			\
+	pgp_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3);	\
+} while(/*CONSTCOND*/0)
+#define PGP_ERROR_4(err,code,fmt,arg,arg2,arg3,arg4)	do {		\
+	pgp_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3,arg4); \
+} while(/*CONSTCOND*/0)
+
+#endif /* ERRORS_H_ */
diff --git a/netpgp/netpgp/keyring.h b/netpgp/netpgp/keyring.h
new file mode 100644
index 0000000000000000000000000000000000000000..db6291b90177096d8b9a1ab03d67697c3c440b02
--- /dev/null
+++ b/netpgp/netpgp/keyring.h
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef KEYRING_H_
+#define KEYRING_H_
+
+#include "packet.h"
+#include "packet-parse.h"
+#include "memory.h"
+
+enum {
+	MAX_ID_LENGTH		= 128,
+	MAX_PASSPHRASE_LENGTH	= 256
+};
+
+typedef struct pgp_key_t	pgp_key_t;
+
+/** \struct pgp_keyring_t
+ * A keyring
+ */
+typedef struct pgp_keyring_t {
+	DYNARRAY(pgp_key_t,	key);
+	pgp_hash_alg_t	hashtype;
+} pgp_keyring_t;
+
+pgp_key_t *pgp_getkeybyid(pgp_io_t *,
+					const pgp_keyring_t *,
+					const uint8_t *,
+					unsigned *,
+                    pgp_pubkey_t **,
+					pgp_seckey_t **,
+                    unsigned checkrevoke,
+                    unsigned checkexpiry);
+unsigned pgp_deletekeybyid(pgp_io_t *, 
+                    pgp_keyring_t *,
+			        const uint8_t *);
+pgp_key_t *pgp_getkeybyfpr(pgp_io_t *,
+					const pgp_keyring_t *,
+			        const uint8_t *fpr,
+                    size_t length,
+                    unsigned *from,
+                    pgp_pubkey_t **,
+                    unsigned checkrevoke,
+                    unsigned checkexpiry);
+unsigned pgp_deletekeybyfpr(pgp_io_t *,
+                    pgp_keyring_t *,
+			        const uint8_t *fpr,
+                    size_t length);
+const pgp_key_t *pgp_getkeybyname(pgp_io_t *,
+					const pgp_keyring_t *,
+					const char *);
+const pgp_key_t *pgp_getnextkeybyname(pgp_io_t *,
+					const pgp_keyring_t *,
+					const char *,
+					unsigned *);
+void pgp_key_free(pgp_key_t *);
+void pgp_keydata_free(pgp_key_t *);
+void pgp_keyring_free(pgp_keyring_t *);
+void pgp_keyring_purge(pgp_keyring_t *);
+void pgp_dump_keyring(const pgp_keyring_t *);
+pgp_pubkey_t *pgp_key_get_pubkey(pgp_key_t *);
+unsigned   pgp_is_key_secret(pgp_key_t *);
+pgp_seckey_t *pgp_get_seckey(pgp_key_t *);
+pgp_seckey_t *pgp_get_writable_seckey(pgp_key_t *);
+// pgp_seckey_t *pgp_decrypt_seckey(const pgp_key_t *, void *);
+
+unsigned 
+pgp_keyring_fileread(pgp_io_t *io,
+            pgp_keyring_t *pubring,
+            pgp_keyring_t *secring,
+			const unsigned armour,
+			const char *filename);
+
+unsigned 
+pgp_keyring_read_from_mem(pgp_io_t *io,
+            pgp_keyring_t *pubring,
+            pgp_keyring_t *secring,
+            const unsigned armour,
+            pgp_memory_t *mem);
+
+int pgp_keyring_list(pgp_io_t *, const pgp_keyring_t *, const int);
+
+void pgp_forget(void *, unsigned);
+
+// uint8_t *pgp_add_userid(pgp_key_t *, const uint8_t *);
+unsigned pgp_update_userid(
+        pgp_key_t *key,
+        const uint8_t *userid,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo);
+
+// pgp_subpacket_t *pgp_add_subpacket(pgp_key_t *,
+// 						const pgp_subpacket_t *);
+// pgp_subpacket_t *pgp_replace_subpacket(pgp_key_t *,
+//                                        const pgp_subpacket_t *,
+//                                        unsigned );
+
+unsigned pgp_add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t duration);
+
+pgp_key_t  *pgp_keydata_new(void);
+void pgp_keydata_init(pgp_key_t *, const pgp_content_enum);
+
+char *pgp_export_key(pgp_io_t *, const pgp_key_t *, uint8_t *);
+
+int pgp_keyring_add(pgp_keyring_t *, const pgp_key_t *);
+// int pgp_add_to_pubring(pgp_keyring_t *, const pgp_pubkey_t *, pgp_content_enum tag);
+pgp_key_t *pgp_ensure_pubkey(
+        pgp_keyring_t *,
+        pgp_pubkey_t *,
+        uint8_t *);
+pgp_key_t *pgp_ensure_seckey(
+        pgp_keyring_t *keyring,
+        pgp_seckey_t *seckey,
+        uint8_t *pubkeyid);
+unsigned pgp_add_directsig(
+        pgp_key_t *key,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo);
+unsigned pgp_update_subkey(
+        pgp_key_t *key,
+	    pgp_content_enum subkeytype,
+        pgp_keydata_key_t *subkey,
+        const pgp_subpacket_t *sigpkt,
+        pgp_sig_info_t *siginfo);
+// int pgp_add_to_secring(pgp_keyring_t *, const pgp_seckey_t *);
+
+int pgp_append_keyring(pgp_keyring_t *, pgp_keyring_t *);
+
+pgp_subpacket_t * pgp_copy_packet(pgp_subpacket_t *, const pgp_subpacket_t *);
+uint8_t * pgp_copy_userid(uint8_t **dst, const uint8_t *src);
+
+const int32_t pgp_key_get_uid0(pgp_key_t *keydata);
+const uint8_t *pgp_key_get_primary_userid(pgp_key_t *key);
+
+
+pgp_pubkey_t * pgp_key_get_sigkey(pgp_key_t *key);
+pgp_seckey_t * pgp_key_get_certkey(pgp_key_t *key);
+pgp_pubkey_t * pgp_key_get_enckey(pgp_key_t *key, const uint8_t **id);
+pgp_seckey_t * pgp_key_get_deckey(pgp_key_t *key, const uint8_t **id);
+
+const int32_t
+pgp_key_find_uid_cond(
+        const pgp_key_t *key,
+        unsigned(*uidcond) ( uint8_t *, void *),
+        void *uidcondarg,
+        unsigned(*sigcond) ( const pgp_sig_info_t *, void *),
+        void *sigcondarg,
+        time_t *youngest,
+        unsigned checkrevoke,
+        unsigned checkexpiry);
+
+const pgp_key_rating_t pgp_key_get_rating(pgp_key_t *key);
+
+unsigned 
+pgp_key_revoke(pgp_key_t *skey, pgp_key_t *pkey, uint8_t code, const char *reason);
+
+#endif /* KEYRING_H_ */
diff --git a/netpgp/netpgp/memory.h b/netpgp/netpgp/memory.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8bd576340ad4199508ce6307706e9b033284aa5
--- /dev/null
+++ b/netpgp/netpgp/memory.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#ifndef MEMORY_H_
+#define MEMORY_H_
+
+#include <sys/types.h>
+
+#include "packet.h"
+
+/** pgp_memory_t
+ */
+typedef struct pgp_memory_t {
+	uint8_t		*buf;
+	size_t          length;
+	size_t          allocated;
+	unsigned	mmapped;
+} pgp_memory_t;
+
+
+pgp_memory_t   *pgp_memory_new(void);
+void pgp_memory_free(pgp_memory_t *);
+void pgp_memory_init(pgp_memory_t *, size_t);
+void pgp_memory_pad(pgp_memory_t *, size_t);
+void pgp_memory_add(pgp_memory_t *, const uint8_t *, size_t);
+void pgp_memory_place_int(pgp_memory_t *, unsigned, unsigned, size_t);
+void pgp_memory_make_packet(pgp_memory_t *, pgp_content_enum);
+void pgp_memory_clear(pgp_memory_t *);
+void pgp_memory_release(pgp_memory_t *);
+
+void pgp_writer_set_memory(pgp_output_t *, pgp_memory_t *);
+
+size_t pgp_mem_len(const pgp_memory_t *);
+void *pgp_mem_data(pgp_memory_t *);
+int pgp_mem_readfile(pgp_memory_t *, const char *);
+
+void pgp_random(void *, size_t);
+
+#endif /* MEMORY_H_ */
diff --git a/netpgp/netpgp/netpgpdefs.h b/netpgp/netpgp/netpgpdefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..087f981ee09573db4f5a9f57e56b033c03f707c3
--- /dev/null
+++ b/netpgp/netpgp/netpgpdefs.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@netbsd.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NETPGPDEFS_H_
+#define NETPGPDEFS_H_	1
+
+#define PRItime		"ll"
+
+#ifdef WIN32
+#define PRIsize		"I"
+#else
+#define PRIsize		"z"
+#endif
+
+/* for silencing unused parameter warnings */
+#define __PGP_USED(x)	/*LINTED*/(void)&(x)
+
+#ifndef __UNCONST
+#define __UNCONST(a)	((void *)(unsigned long)(const void *)(a))
+#endif
+
+/* number of elements in an array */
+#define PGP_ARRAY_SIZE(a)       (sizeof(a)/sizeof(*(a)))
+
+void            hexdump(FILE *, const char *, const uint8_t *, size_t);
+
+const char     *pgp_str_from_map(int, pgp_map_t *);
+
+int             pgp_set_debug_level(const char *);
+int             pgp_get_debug_level(const char *);
+
+void		*pgp_new(size_t);
+
+#define NETPGP_BUFSIZ	8192
+
+#define CALLBACK(t, cbinfo, pkt)	do {				\
+	(pkt)->tag = (t);						\
+	if (pgp_callback(pkt, cbinfo) == PGP_RELEASE_MEMORY) {	\
+		pgp_parser_content_free(pkt);				\
+	}								\
+} while(/* CONSTCOND */0)
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y)) /*EDIT BY MR*/
+#endif
+
+#endif /* !NETPGPDEFS_H_ */
diff --git a/netpgp/netpgp/netpgpdigest.h b/netpgp/netpgp/netpgpdigest.h
new file mode 100644
index 0000000000000000000000000000000000000000..be776c456d7f415ba0f27124fc383e7e12136a78
--- /dev/null
+++ b/netpgp/netpgp/netpgpdigest.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@netbsd.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NETPGPDIGEST_H_
+#define NETPGPDIGEST_H_
+
+/* header file to define the sizes for various digest arrays */
+
+#ifdef HAVE_OPENSSL_MD5_H
+#include <openssl/md5.h>
+#endif
+
+#ifdef HAVE_OPENSSL_SHA_H
+#include <openssl/sha.h>
+#endif
+
+/* Apple */
+#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H
+#undef MD5_DIGEST_LENGTH
+#undef SHA_DIGEST_LENGTH
+#define COMMON_DIGEST_FOR_OPENSSL	1
+#include <CommonCrypto/CommonDigest.h>
+#endif
+
+/* SHA1 Hash Size */
+#define PGP_SHA1_HASH_SIZE 	SHA_DIGEST_LENGTH
+#define PGP_SHA256_HASH_SIZE	SHA256_DIGEST_LENGTH
+#define PGP_SHA512_HASH_SIZE	SHA512_DIGEST_LENGTH
+#define PGP_CHECKHASH_SIZE	PGP_SHA1_HASH_SIZE
+
+#endif /* NETPGPDIGEST_H_ */
diff --git a/netpgp/netpgp/netpgpsdk.h b/netpgp/netpgp/netpgpsdk.h
new file mode 100644
index 0000000000000000000000000000000000000000..4747b26097ca5fccda47706e72acb8477c98e2a6
--- /dev/null
+++ b/netpgp/netpgp/netpgpsdk.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2009,2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NETPGPSDK_H_
+#define NETPGPSDK_H_
+
+#include "keyring.h"
+#include "crypto.h"
+#include "signature.h"
+#include "packet-show.h"
+
+#ifndef __printflike
+#define __printflike(n, m)		__attribute__((format(printf,n,m)))
+#endif
+
+void            pgp_validate_result_free(pgp_validation_t *);
+
+unsigned
+pgp_validate_all_sigs(pgp_validation_t *,
+		const pgp_keyring_t *,
+		pgp_cb_ret_t cb(const pgp_packet_t *, pgp_cbdata_t *));
+
+unsigned   pgp_check_sig(const uint8_t *,
+		unsigned, const pgp_sig_t *, const pgp_pubkey_t *);
+
+const char     *pgp_get_info(const char *type);
+
+int pgp_asprintf(char **, const char *, ...) __printflike(2, 3);
+
+void netpgp_log(const char *, ...) __printflike(1, 2);
+
+int netpgp_strcasecmp(const char *, const char *);
+char *netpgp_strdup(const char *);
+
+#endif
diff --git a/netpgp/netpgp/openssl11stub.h b/netpgp/netpgp/openssl11stub.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d759ff8808a7962ff2e352774b89822d51a9c2c
--- /dev/null
+++ b/netpgp/netpgp/openssl11stub.h
@@ -0,0 +1,133 @@
+
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+{
+    /* If the fields n and e in r are NULL, the corresponding input
+     * parameters MUST be non-NULL for n and e.  d may be
+     * left NULL (in case only the public key is used).
+     */
+    if ((r->n == NULL && n == NULL)
+        || (r->e == NULL && e == NULL))
+        return 0;
+
+    if (n != NULL) {
+        BN_free(r->n);
+        r->n = n;
+    }
+    if (e != NULL) {
+        BN_free(r->e);
+        r->e = e;
+    }
+    if (d != NULL) {
+        BN_free(r->d);
+        r->d = d;
+    }
+
+    return 1;
+}
+
+int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
+{
+    /* If the fields p and q in r are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((r->p == NULL && p == NULL)
+        || (r->q == NULL && q == NULL))
+        return 0;
+
+    if (p != NULL) {
+        BN_free(r->p);
+        r->p = p;
+    }
+    if (q != NULL) {
+        BN_free(r->q);
+        r->q = q;
+    }
+
+    return 1;
+}
+
+void RSA_get0_key(const RSA *r,
+                  const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+    if (n != NULL)
+        *n = r->n;
+    if (e != NULL)
+        *e = r->e;
+    if (d != NULL)
+        *d = r->d;
+}
+
+void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
+{
+    if (p != NULL)
+        *p = r->p;
+    if (q != NULL)
+        *q = r->q;
+}
+
+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+    if (r == NULL || s == NULL)
+        return 0;
+    BN_clear_free(sig->r);
+    BN_clear_free(sig->s);
+    sig->r = r;
+    sig->s = s;
+    return 1;
+}
+
+void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
+{
+    if (pr != NULL)
+        *pr = sig->r;
+    if (ps != NULL)
+        *ps = sig->s;
+}
+
+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+    /* If the fields p, q and g in d are NULL, the corresponding input
+     * parameters MUST be non-NULL.
+     */
+    if ((d->p == NULL && p == NULL)
+        || (d->q == NULL && q == NULL)
+        || (d->g == NULL && g == NULL))
+        return 0;
+
+    if (p != NULL) {
+        BN_free(d->p);
+        d->p = p;
+    }
+    if (q != NULL) {
+        BN_free(d->q);
+        d->q = q;
+    }
+    if (g != NULL) {
+        BN_free(d->g);
+        d->g = g;
+    }
+
+    return 1;
+}
+
+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
+{
+    /* If the field pub_key in d is NULL, the corresponding input
+     * parameters MUST be non-NULL.  The priv_key field may
+     * be left NULL.
+     */
+    if (d->pub_key == NULL && pub_key == NULL)
+        return 0;
+
+    if (pub_key != NULL) {
+        BN_free(d->pub_key);
+        d->pub_key = pub_key;
+    }
+    if (priv_key != NULL) {
+        BN_free(d->priv_key);
+        d->priv_key = priv_key;
+    }
+
+    return 1;
+}
+
diff --git a/netpgp/netpgp/packet-parse.h b/netpgp/netpgp/packet-parse.h
new file mode 100644
index 0000000000000000000000000000000000000000..0406ff031453fc87d103334b74f49b3e0ced86a8
--- /dev/null
+++ b/netpgp/netpgp/packet-parse.h
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ * Parser for OpenPGP packets - headers.
+ */
+
+#ifndef PACKET_PARSE_H_
+#define PACKET_PARSE_H_
+
+#include "types.h"
+#include "packet.h"
+
+/** pgp_region_t */
+typedef struct pgp_region_t {
+	struct pgp_region_t	*parent;
+	unsigned		 length;
+	unsigned		 readc;		/* length read */
+	unsigned		 last_read;
+		/* length of last read, only valid in deepest child */
+	unsigned		 indeterminate:1;
+} pgp_region_t;
+
+void pgp_init_subregion(pgp_region_t *, pgp_region_t *);
+
+/** pgp_cb_ret_t */
+typedef enum {
+	PGP_RELEASE_MEMORY,
+	PGP_KEEP_MEMORY,
+	PGP_FINISHED
+} pgp_cb_ret_t;
+
+typedef struct pgp_cbdata_t	 pgp_cbdata_t;
+
+typedef pgp_cb_ret_t pgp_cbfunc_t(const pgp_packet_t *,
+					pgp_cbdata_t *);
+
+pgp_cb_ret_t
+get_passphrase_cb(const pgp_packet_t *, pgp_cbdata_t *);
+
+typedef struct pgp_stream_t	pgp_stream_t;
+typedef struct pgp_reader_t		pgp_reader_t;
+typedef struct pgp_cryptinfo_t	pgp_cryptinfo_t;
+
+/*
+   A reader MUST read at least one byte if it can, and should read up
+   to the number asked for. Whether it reads more for efficiency is
+   its own decision, but if it is a stacked reader it should never
+   read more than the length of the region it operates in (which it
+   would have to be given when it is stacked).
+
+   If a read is short because of EOF, then it should return the short
+   read (obviously this will be zero on the second attempt, if not the
+   first). Because a reader is not obliged to do a full read, only a
+   zero return can be taken as an indication of EOF.
+
+   If there is an error, then the callback should be notified, the
+   error stacked, and -1 should be returned.
+
+   Note that although length is a size_t, a reader will never be asked
+   to read more than INT_MAX in one go.
+
+ */
+typedef int pgp_reader_func_t(pgp_stream_t *, void *, size_t, pgp_error_t **,
+				pgp_reader_t *, pgp_cbdata_t *);
+
+typedef void pgp_reader_destroyer_t(pgp_reader_t *);
+
+void pgp_stream_delete(pgp_stream_t *);
+pgp_error_t *pgp_stream_get_errors(pgp_stream_t *);
+pgp_crypt_t *pgp_get_decrypt(pgp_stream_t *);
+
+void pgp_set_callback(pgp_stream_t *, pgp_cbfunc_t *, void *);
+void pgp_callback_push(pgp_stream_t *, pgp_cbfunc_t *, void *);
+void *pgp_callback_arg(pgp_cbdata_t *);
+void *pgp_callback_errors(pgp_cbdata_t *);
+void pgp_reader_set(pgp_stream_t *, pgp_reader_func_t *,
+			pgp_reader_destroyer_t *, void *);
+void pgp_reader_push(pgp_stream_t *, pgp_reader_func_t *,
+			pgp_reader_destroyer_t *, void *);
+void pgp_reader_pop(pgp_stream_t *);
+
+void *pgp_reader_get_arg(pgp_reader_t *);
+
+pgp_cb_ret_t pgp_callback(const pgp_packet_t *,
+					pgp_cbdata_t *);
+pgp_cb_ret_t pgp_stacked_callback(const pgp_packet_t *,
+					pgp_cbdata_t *);
+pgp_reader_t *pgp_readinfo(pgp_stream_t *);
+
+int pgp_parse(pgp_stream_t *, const int);
+
+/** Used to specify whether subpackets should be returned raw, parsed
+* or ignored.  */
+typedef enum {
+	PGP_PARSE_RAW,		/* Callback Raw */
+	PGP_PARSE_PARSED,	/* Callback Parsed */
+	PGP_PARSE_IGNORE	/* Don't callback */
+} pgp_parse_type_t;
+
+void pgp_parse_options(pgp_stream_t *, pgp_content_enum,
+			pgp_parse_type_t);
+
+unsigned pgp_limited_read(pgp_stream_t *, uint8_t *, size_t, pgp_region_t *,
+			pgp_error_t **, pgp_reader_t *,
+			pgp_cbdata_t *);
+unsigned pgp_stacked_limited_read(pgp_stream_t *, uint8_t *, unsigned,
+			pgp_region_t *, pgp_error_t **,
+			pgp_reader_t *, pgp_cbdata_t *);
+void pgp_parse_hash_init(pgp_stream_t *, pgp_hash_alg_t,
+			const uint8_t *);
+void pgp_parse_hash_data(pgp_stream_t *, const void *, size_t);
+void pgp_parse_hash_finish(pgp_stream_t *);
+pgp_hash_t *pgp_parse_hash_find(pgp_stream_t *, const uint8_t *);
+
+pgp_reader_func_t    pgp_stacked_read;
+
+int pgp_decompress(pgp_region_t *, pgp_stream_t *,
+			pgp_compression_type_t);
+unsigned pgp_writez(pgp_output_t *, const uint8_t *,
+			const unsigned);
+
+void 
+copy_sig_info(pgp_sig_info_t *dst, const pgp_sig_info_t *src);
+
+#endif /* PACKET_PARSE_H_ */
diff --git a/netpgp/netpgp/packet-show.h b/netpgp/netpgp/packet-show.h
new file mode 100644
index 0000000000000000000000000000000000000000..021727075da5b68a047550d29e2bc068e19daa1c
--- /dev/null
+++ b/netpgp/netpgp/packet-show.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef PACKET_SHOW_H_
+#define PACKET_SHOW_H_
+
+#include "packet.h"
+
+/** pgp_list_t
+ */
+typedef struct {
+	unsigned    size;	/* num of array slots allocated */
+	unsigned    used;	/* num of array slots currently used */
+	char          **strings;
+} pgp_list_t;
+
+/** pgp_text_t
+ */
+typedef struct {
+	pgp_list_t	known;
+	pgp_list_t   	unknown;
+} pgp_text_t;
+
+/** pgp_bit_map_t
+ */
+typedef struct {
+	uint8_t		mask;
+	const char     *string;
+} pgp_bit_map_t;
+
+void pgp_text_init(pgp_text_t *);
+void pgp_text_free(pgp_text_t *);
+
+const char *pgp_show_packet_tag(pgp_content_enum);
+const char *pgp_show_ss_type(pgp_content_enum);
+
+const char *pgp_show_sig_type(pgp_sig_type_t);
+const char *pgp_show_pka(pgp_pubkey_alg_t);
+
+const char *pgp_show_ss_zpref(uint8_t);
+
+const char *pgp_show_hash_alg(uint8_t);
+const char *pgp_show_symm_alg(uint8_t);
+
+pgp_text_t *pgp_showall_ss_skapref(const pgp_data_t *);
+const char *pgp_show_ss_skapref(uint8_t);
+
+const char *pgp_show_ss_rr_code(pgp_ss_rr_code_t);
+
+pgp_text_t *pgp_showall_ss_features(pgp_data_t);
+
+const char *pgp_show_ss_key_flag(uint8_t, pgp_bit_map_t *);
+
+const char *pgp_show_keyserv_pref(uint8_t, pgp_bit_map_t *);
+
+pgp_text_t *pgp_showall_notation(pgp_ss_notation_t);
+
+#endif /* PACKET_SHOW_H_ */
diff --git a/netpgp/netpgp/packet.h b/netpgp/netpgp/packet.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8d0171921e53abf7d3ddec1f161857275280ffa
--- /dev/null
+++ b/netpgp/netpgp/packet.h
@@ -0,0 +1,1017 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ * packet related headers.
+ */
+
+#ifndef PACKET_H_
+#define PACKET_H_
+
+#include <time.h>
+
+#ifdef HAVE_OPENSSL_BN_H
+#include <openssl/bn.h>
+#endif
+
+#include <openssl/ossl_typ.h>
+
+#include "types.h"
+#include "errors.h"
+
+/* structure to keep track of printing state variables */
+typedef struct pgp_printstate_t {
+	unsigned	unarmoured;
+	unsigned	skipping;
+	int		indent;
+} pgp_printstate_t;
+
+/** General-use structure for variable-length data
+ */
+
+typedef struct {
+	size_t           len;
+	uint8_t		*contents;
+	uint8_t		 mmapped;	/* contents need an munmap(2) */
+} pgp_data_t;
+
+/************************************/
+/* Packet Tags - RFC4880, 4.2 */
+/************************************/
+
+/** Packet Tag - Bit 7 Mask (this bit is always set).
+ * The first byte of a packet is the "Packet Tag".  It always
+ * has bit 7 set.  This is the mask for it.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_ALWAYS_SET		0x80
+
+/** Packet Tag - New Format Flag.
+ * Bit 6 of the Packet Tag is the packet format indicator.
+ * If it is set, the new format is used, if cleared the
+ * old format is used.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_NEW_FORMAT		0x40
+
+
+/** Old Packet Format: Mask for content tag.
+ * In the old packet format bits 5 to 2 (including)
+ * are the content tag.  This is the mask to apply
+ * to the packet tag.  Note that you need to
+ * shift by #PGP_PTAG_OF_CONTENT_TAG_SHIFT bits.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_OF_CONTENT_TAG_MASK	0x3c
+/** Old Packet Format: Offset for the content tag.
+ * As described at #PGP_PTAG_OF_CONTENT_TAG_MASK the
+ * content tag needs to be shifted after being masked
+ * out from the Packet Tag.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_OF_CONTENT_TAG_SHIFT	2
+/** Old Packet Format: Mask for length type.
+ * Bits 1 and 0 of the packet tag are the length type
+ * in the old packet format.
+ *
+ * See #pgp_ptag_of_lt_t for the meaning of the values.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_OF_LENGTH_TYPE_MASK	0x03
+
+
+/** Old Packet Format Lengths.
+ * Defines the meanings of the 2 bits for length type in the
+ * old packet format.
+ *
+ * \see RFC4880 4.2.1
+ */
+typedef enum {
+	PGP_PTAG_OLD_LEN_1 = 0x00,	/* Packet has a 1 byte length -
+					 * header is 2 bytes long. */
+	PGP_PTAG_OLD_LEN_2 = 0x01,	/* Packet has a 2 byte length -
+					 * header is 3 bytes long. */
+	PGP_PTAG_OLD_LEN_4 = 0x02,	/* Packet has a 4 byte
+						 * length - header is 5 bytes
+						 * long. */
+	PGP_PTAG_OLD_LEN_INDETERMINATE = 0x03	/* Packet has a
+						 * indeterminate length. */
+} pgp_ptag_of_lt_t;
+
+
+/** New Packet Format: Mask for content tag.
+ * In the new packet format the 6 rightmost bits
+ * are the content tag.  This is the mask to apply
+ * to the packet tag.  Note that you need to
+ * shift by #PGP_PTAG_NF_CONTENT_TAG_SHIFT bits.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_NF_CONTENT_TAG_MASK	0x3f
+/** New Packet Format: Offset for the content tag.
+ * As described at #PGP_PTAG_NF_CONTENT_TAG_MASK the
+ * content tag needs to be shifted after being masked
+ * out from the Packet Tag.
+ *
+ * \see RFC4880 4.2
+ */
+#define PGP_PTAG_NF_CONTENT_TAG_SHIFT	0
+
+/* PTag Content Tags */
+/***************************/
+
+/** Package Tags (aka Content Tags) and signature subpacket types.
+ * This enumerates all rfc-defined packet tag values and the
+ * signature subpacket type values that we understand.
+ *
+ * \see RFC4880 4.3
+ * \see RFC4880 5.2.3.1
+ */
+typedef enum {
+	PGP_PTAG_CT_RESERVED = 0,	/* Reserved - a packet tag must
+					 * not have this value */
+	PGP_PTAG_CT_PK_SESSION_KEY = 1,	/* Public-Key Encrypted Session
+					 * Key Packet */
+	PGP_PTAG_CT_SIGNATURE = 2,	/* Signature Packet */
+	PGP_PTAG_CT_SK_SESSION_KEY = 3,	/* Symmetric-Key Encrypted Session
+					 * Key Packet */
+	PGP_PTAG_CT_1_PASS_SIG = 4,	/* One-Pass Signature
+						 * Packet */
+	PGP_PTAG_CT_SECRET_KEY = 5,	/* Secret Key Packet */
+	PGP_PTAG_CT_PUBLIC_KEY = 6,	/* Public Key Packet */
+	PGP_PTAG_CT_SECRET_SUBKEY = 7,	/* Secret Subkey Packet */
+	PGP_PTAG_CT_COMPRESSED = 8,	/* Compressed Data Packet */
+	PGP_PTAG_CT_SE_DATA = 9,/* Symmetrically Encrypted Data Packet */
+	PGP_PTAG_CT_MARKER = 10,/* Marker Packet */
+	PGP_PTAG_CT_LITDATA = 11,	/* Literal Data Packet */
+	PGP_PTAG_CT_TRUST = 12,	/* Trust Packet */
+	PGP_PTAG_CT_USER_ID = 13,	/* User ID Packet */
+	PGP_PTAG_CT_PUBLIC_SUBKEY = 14,	/* Public Subkey Packet */
+	PGP_PTAG_CT_RESERVED2 = 15,	/* reserved */
+	PGP_PTAG_CT_RESERVED3 = 16,	/* reserved */
+	PGP_PTAG_CT_USER_ATTR = 17,	/* User Attribute Packet */
+	PGP_PTAG_CT_SE_IP_DATA = 18,	/* Sym. Encrypted and Integrity
+					 * Protected Data Packet */
+	PGP_PTAG_CT_MDC = 19,	/* Modification Detection Code Packet */
+
+	PGP_PARSER_PTAG = 0x100,/* Internal Use: The packet is the "Packet
+				 * Tag" itself - used when callback sends
+				 * back the PTag. */
+	PGP_PTAG_RAW_SS = 0x101,/* Internal Use: content is raw sig subtag */
+	PGP_PTAG_SS_ALL = 0x102,/* Internal Use: select all subtags */
+	PGP_PARSER_PACKET_END = 0x103,
+
+	/* signature subpackets (0x200-2ff) (type+0x200) */
+	/* only those we can parse are listed here */
+	PGP_PTAG_SIG_SUBPKT_BASE = 0x200,	/* Base for signature
+							 * subpacket types - All
+							 * signature type values
+							 * are relative to this
+							 * value. */
+	PGP_PTAG_SS_CREATION_TIME = 0x200 + 2,	/* signature creation time */
+	PGP_PTAG_SS_EXPIRATION_TIME = 0x200 + 3,	/* signature
+							 * expiration time */
+
+	PGP_PTAG_SS_EXPORT_CERT = 0x200 + 4,	/* exportable certification */
+	PGP_PTAG_SS_TRUST = 0x200 + 5,	/* trust signature */
+	PGP_PTAG_SS_REGEXP = 0x200 + 6,	/* regular expression */
+	PGP_PTAG_SS_REVOCABLE = 0x200 + 7,	/* revocable */
+	PGP_PTAG_SS_KEY_EXPIRY = 0x200 + 9,	/* key expiration
+							 * time */
+	PGP_PTAG_SS_RESERVED = 0x200 + 10,	/* reserved */
+	PGP_PTAG_SS_PREFERRED_SKA = 0x200 + 11,	/* preferred symmetric
+						 * algs */
+	PGP_PTAG_SS_REVOCATION_KEY = 0x200 + 12,	/* revocation key */
+	PGP_PTAG_SS_ISSUER_KEY_ID = 0x200 + 16,	/* issuer key ID */
+	PGP_PTAG_SS_NOTATION_DATA = 0x200 + 20,	/* notation data */
+	PGP_PTAG_SS_PREFERRED_HASH = 0x200 + 21,	/* preferred hash
+							 * algs */
+	PGP_PTAG_SS_PREF_COMPRESS = 0x200 + 22,	/* preferred
+							 * compression
+							 * algorithms */
+	PGP_PTAG_SS_KEYSERV_PREFS = 0x200 + 23,	/* key server
+							 * preferences */
+	PGP_PTAG_SS_PREF_KEYSERV = 0x200 + 24,	/* Preferred Key
+							 * Server */
+	PGP_PTAG_SS_PRIMARY_USER_ID = 0x200 + 25,	/* primary User ID */
+	PGP_PTAG_SS_POLICY_URI = 0x200 + 26,	/* Policy URI */
+	PGP_PTAG_SS_KEY_FLAGS = 0x200 + 27,	/* key flags */
+	PGP_PTAG_SS_SIGNERS_USER_ID = 0x200 + 28,	/* Signer's User ID */
+	PGP_PTAG_SS_REVOCATION_REASON = 0x200 + 29,	/* reason for
+							 * revocation */
+	PGP_PTAG_SS_FEATURES = 0x200 + 30,	/* features */
+	PGP_PTAG_SS_SIGNATURE_TARGET = 0x200 + 31,	/* signature target */
+	PGP_PTAG_SS_EMBEDDED_SIGNATURE = 0x200 + 32,	/* embedded signature */
+
+	PGP_PTAG_SS_USERDEFINED00 = 0x200 + 100,	/* internal or
+							 * user-defined */
+	PGP_PTAG_SS_USERDEFINED01 = 0x200 + 101,
+	PGP_PTAG_SS_USERDEFINED02 = 0x200 + 102,
+	PGP_PTAG_SS_USERDEFINED03 = 0x200 + 103,
+	PGP_PTAG_SS_USERDEFINED04 = 0x200 + 104,
+	PGP_PTAG_SS_USERDEFINED05 = 0x200 + 105,
+	PGP_PTAG_SS_USERDEFINED06 = 0x200 + 106,
+	PGP_PTAG_SS_USERDEFINED07 = 0x200 + 107,
+	PGP_PTAG_SS_USERDEFINED08 = 0x200 + 108,
+	PGP_PTAG_SS_USERDEFINED09 = 0x200 + 109,
+	PGP_PTAG_SS_USERDEFINED10 = 0x200 + 110,
+
+	/* pseudo content types */
+	PGP_PTAG_CT_LITDATA_HEADER = 0x300,
+	PGP_PTAG_CT_LITDATA_BODY = 0x300 + 1,
+	PGP_PTAG_CT_SIGNATURE_HEADER = 0x300 + 2,
+	PGP_PTAG_CT_SIGNATURE_FOOTER = 0x300 + 3,
+	PGP_PTAG_CT_ARMOUR_HEADER = 0x300 + 4,
+	PGP_PTAG_CT_ARMOUR_TRAILER = 0x300 + 5,
+	PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER = 0x300 + 6,
+	PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY = 0x300 + 7,
+	PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER = 0x300 + 8,
+	PGP_PTAG_CT_UNARMOURED_TEXT = 0x300 + 9,
+	PGP_PTAG_CT_ENCRYPTED_SECRET_KEY = 0x300 + 10,	/* In this case the
+							 * algorithm specific
+							 * fields will not be
+							 * initialised */
+	PGP_PTAG_CT_SE_DATA_HEADER = 0x300 + 11,
+	PGP_PTAG_CT_SE_DATA_BODY = 0x300 + 12,
+	PGP_PTAG_CT_SE_IP_DATA_HEADER = 0x300 + 13,
+	PGP_PTAG_CT_SE_IP_DATA_BODY = 0x300 + 14,
+	PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY = 0x300 + 15,
+
+	/* commands to the callback */
+	PGP_GET_PASSPHRASE = 0x400,
+	PGP_GET_SECKEY = 0x400 + 1,
+
+	/* Errors */
+	PGP_PARSER_ERROR = 0x500,	/* Internal Use: Parser Error */
+	PGP_PARSER_ERRCODE = 0x500 + 1	/* Internal Use: Parser Error
+					 * with errcode returned */
+} pgp_content_enum;
+
+enum {
+	PGP_REVOCATION_NO_REASON	= 0,
+	PGP_REVOCATION_SUPERSEDED	= 1,
+	PGP_REVOCATION_COMPROMISED	= 2,
+	PGP_REVOCATION_RETIRED		= 3,
+	PGP_REVOCATION_NO_LONGER_VALID	= 0x20
+};
+
+/** Structure to hold one error code */
+typedef struct {
+	pgp_errcode_t   errcode;
+} pgp_parser_errcode_t;
+
+/** Structure to hold one packet tag.
+ * \see RFC4880 4.2
+ */
+typedef struct {
+	unsigned        new_format;	/* Whether this packet tag is new
+					 * (1) or old format (0) */
+	unsigned        type;	/* content_tag value - See
+					 * #pgp_content_enum for meanings */
+	pgp_ptag_of_lt_t length_type;	/* Length type (#pgp_ptag_of_lt_t)
+					 * - only if this packet tag is old
+					 * format.  Set to 0 if new format. */
+	unsigned        length;	/* The length of the packet.  This value
+				 * is set when we read and compute the length
+				 * information, not at the same moment we
+				 * create the packet tag structure. Only
+	 * defined if #readc is set. *//* XXX: Ben, is this correct? */
+	unsigned        position;	/* The position (within the
+					 * current reader) of the packet */
+	unsigned	size;	/* number of bits */
+} pgp_ptag_t;
+
+/** Public Key Algorithm Numbers.
+ * OpenPGP assigns a unique Algorithm Number to each algorithm that is part of OpenPGP.
+ *
+ * This lists algorithm numbers for public key algorithms.
+ *
+ * \see RFC4880 9.1
+ */
+typedef enum {
+	PGP_PKA_NOTHING	= 0,	/* No PKA */
+	PGP_PKA_RSA = 1,	/* RSA (Encrypt or Sign) */
+	PGP_PKA_RSA_ENCRYPT_ONLY = 2,	/* RSA Encrypt-Only (deprecated -
+					 * \see RFC4880 13.5) */
+	PGP_PKA_RSA_SIGN_ONLY = 3,	/* RSA Sign-Only (deprecated -
+					 * \see RFC4880 13.5) */
+	PGP_PKA_ELGAMAL = 16,	/* Elgamal (Encrypt-Only) */
+	PGP_PKA_DSA = 17,	/* DSA (Digital Signature Algorithm) */
+	PGP_PKA_RESERVED_ELLIPTIC_CURVE = 18,	/* Reserved for Elliptic
+						 * Curve */
+	PGP_PKA_RESERVED_ECDSA = 19,	/* Reserved for ECDSA */
+	PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN = 20,	/* Deprecated. */
+	PGP_PKA_RESERVED_DH = 21,	/* Reserved for Diffie-Hellman
+					 * (X9.42, as defined for
+					 * IETF-S/MIME) */
+	PGP_PKA_PRIVATE00 = 100,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE01 = 101,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE02 = 102,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE03 = 103,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE04 = 104,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE05 = 105,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE06 = 106,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE07 = 107,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE08 = 108,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE09 = 109,/* Private/Experimental Algorithm */
+	PGP_PKA_PRIVATE10 = 110	/* Private/Experimental Algorithm */
+} pgp_pubkey_alg_t;
+
+/** Structure to hold one DSA public key params.
+ *
+ * \see RFC4880 5.5.2
+ */
+typedef struct {
+	BIGNUM         *p;	/* DSA prime p */
+	BIGNUM         *q;	/* DSA group order q */
+	BIGNUM         *g;	/* DSA group generator g */
+	BIGNUM         *y;	/* DSA public key value y (= g^x mod p
+				 * with x being the secret) */
+} pgp_dsa_pubkey_t;
+
+/** Structure to hold an RSA public key.
+ *
+ * \see RFC4880 5.5.2
+ */
+typedef struct {
+	BIGNUM         *n;	/* RSA public modulus n */
+	BIGNUM         *e;	/* RSA public encryption exponent e */
+} pgp_rsa_pubkey_t;
+
+/** Structure to hold an ElGamal public key params.
+ *
+ * \see RFC4880 5.5.2
+ */
+typedef struct {
+	BIGNUM         *p;	/* ElGamal prime p */
+	BIGNUM         *g;	/* ElGamal group generator g */
+	BIGNUM         *y;	/* ElGamal public key value y (= g^x mod p
+				 * with x being the secret) */
+} pgp_elgamal_pubkey_t;
+
+/** Version.
+ * OpenPGP has two different protocol versions: version 3 and version 4.
+ *
+ * \see RFC4880 5.2
+ */
+typedef enum {
+	PGP_V2 = 2,		/* Version 2 (essentially the same as v3) */
+	PGP_V3 = 3,		/* Version 3 */
+	PGP_V4 = 4		/* Version 4 */
+} pgp_version_t;
+
+/** Structure to hold a pgp public key */
+typedef struct {
+	pgp_version_t		version;/* version of the key (v3, v4...) */
+	time_t			birthtime;
+	time_t			duration;
+		/* validity period of the key in days since
+		* creation.  A value of 0 has a special meaning
+		* indicating this key does not expire.  Only used with
+		* v3 keys.  */
+	unsigned		days_valid;	/* v4 duration */
+	pgp_pubkey_alg_t	alg;	/* Public Key Algorithm type */
+	union {
+		pgp_dsa_pubkey_t dsa;	/* A DSA public key */
+		pgp_rsa_pubkey_t rsa;	/* An RSA public key */
+		pgp_elgamal_pubkey_t elgamal;	/* An ElGamal public key */
+	}			key;	/* Public Key Parameters */
+} pgp_pubkey_t;
+
+/** Structure to hold data for one RSA secret key
+ */
+typedef struct {
+	BIGNUM         *d;
+	BIGNUM         *p;
+	BIGNUM         *q;
+	BIGNUM         *u;
+} pgp_rsa_seckey_t;
+
+/** pgp_dsa_seckey_t */
+typedef struct {
+	BIGNUM         *x;
+} pgp_dsa_seckey_t;
+
+/** pgp_elgamal_seckey_t */
+typedef struct {
+	BIGNUM         *x;
+} pgp_elgamal_seckey_t;
+
+/** s2k_usage_t
+ */
+typedef enum {
+	PGP_S2KU_NONE = 0,
+	PGP_S2KU_ENCRYPTED_AND_HASHED = 254,
+	PGP_S2KU_ENCRYPTED = 255
+} pgp_s2k_usage_t;
+
+/** s2k_specifier_t
+ */
+typedef enum {
+	PGP_S2KS_SIMPLE = 0,
+	PGP_S2KS_SALTED = 1,
+	PGP_S2KS_ITERATED_AND_SALTED = 3
+} pgp_s2k_specifier_t;
+
+/** Symmetric Key Algorithm Numbers.
+ * OpenPGP assigns a unique Algorithm Number to each algorithm that is
+ * part of OpenPGP.
+ *
+ * This lists algorithm numbers for symmetric key algorithms.
+ *
+ * \see RFC4880 9.2
+ */
+typedef enum {
+	PGP_SA_PLAINTEXT = 0,	/* Plaintext or unencrypted data */
+	PGP_SA_IDEA = 1,	/* IDEA */
+	PGP_SA_TRIPLEDES = 2,	/* TripleDES */
+	PGP_SA_CAST5 = 3,	/* CAST5 */
+	PGP_SA_BLOWFISH = 4,	/* Blowfish */
+	PGP_SA_AES_128 = 7,	/* AES with 128-bit key (AES) */
+	PGP_SA_AES_192 = 8,	/* AES with 192-bit key */
+	PGP_SA_AES_256 = 9,	/* AES with 256-bit key */
+	PGP_SA_TWOFISH = 10,	/* Twofish with 256-bit key (TWOFISH) */
+	PGP_SA_CAMELLIA_128 = 100,	/* Camellia with 128-bit key (CAMELLIA) */
+	PGP_SA_CAMELLIA_192 = 101,	/* Camellia with 192-bit key */
+	PGP_SA_CAMELLIA_256 = 102	/* Camellia with 256-bit key */
+} pgp_symm_alg_t;
+
+#define PGP_SA_DEFAULT_CIPHER	PGP_SA_CAST5
+
+/** Hashing Algorithm Numbers.
+ * OpenPGP assigns a unique Algorithm Number to each algorithm that is
+ * part of OpenPGP.
+ *
+ * This lists algorithm numbers for hash algorithms.
+ *
+ * \see RFC4880 9.4
+ */
+typedef enum {
+	PGP_HASH_UNKNOWN = -1,	/* used to indicate errors */
+	PGP_HASH_MD5 = 1,	/* MD5 */
+	PGP_HASH_SHA1 = 2,	/* SHA-1 */
+	PGP_HASH_RIPEMD = 3,	/* RIPEMD160 */
+
+	PGP_HASH_SHA256 = 8,	/* SHA256 */
+	PGP_HASH_SHA384 = 9,	/* SHA384 */
+	PGP_HASH_SHA512 = 10,	/* SHA512 */
+	PGP_HASH_SHA224 = 11	/* SHA224 */
+} pgp_hash_alg_t;
+
+#define	PGP_DEFAULT_HASH_ALGORITHM	PGP_HASH_SHA256
+
+void   pgp_calc_mdc_hash(const uint8_t *,
+			const size_t,
+			const uint8_t *,
+			const unsigned,
+			uint8_t *);
+unsigned   pgp_is_hash_alg_supported(const pgp_hash_alg_t *);
+
+/* Maximum block size for symmetric crypto */
+#define PGP_MAX_BLOCK_SIZE	16
+
+/* Maximum key size for symmetric crypto */
+#define PGP_MAX_KEY_SIZE	32
+
+/* Salt size for hashing */
+#define PGP_SALT_SIZE		8
+
+/* Max hash size */
+#define PGP_MAX_HASH_SIZE	64
+
+/** pgp_seckey_t
+ */
+typedef struct pgp_seckey_t {
+	pgp_pubkey_t			pubkey;		/* public key */
+	pgp_s2k_usage_t		s2k_usage;
+	pgp_s2k_specifier_t		s2k_specifier;
+	pgp_symm_alg_t		alg;		/* symmetric alg */
+	pgp_hash_alg_t		hash_alg;	/* hash algorithm */
+	uint8_t				salt[PGP_SALT_SIZE];
+	unsigned			octetc;
+	uint8_t				iv[PGP_MAX_BLOCK_SIZE];
+	union {
+		pgp_rsa_seckey_t		rsa;
+		pgp_dsa_seckey_t		dsa;
+		pgp_elgamal_seckey_t		elgamal;
+	}				key;
+	unsigned			checksum;
+	uint8_t			       *checkhash;
+} pgp_seckey_t;
+
+/** Signature Type.
+ * OpenPGP defines different signature types that allow giving
+ * different meanings to signatures.  Signature types include 0x10 for
+ * generitc User ID certifications (used when Ben signs Weasel's key),
+ * Subkey binding signatures, document signatures, key revocations,
+ * etc.
+ *
+ * Different types are used in different places, and most make only
+ * sense in their intended location (for instance a subkey binding has
+ * no place on a UserID).
+ *
+ * \see RFC4880 5.2.1
+ */
+typedef enum {
+	PGP_SIG_BINARY = 0x00,	/* Signature of a binary document */
+	PGP_SIG_TEXT = 0x01,	/* Signature of a canonical text document */
+	PGP_SIG_STANDALONE = 0x02,	/* Standalone signature */
+
+	PGP_CERT_GENERIC = 0x10,/* Generic certification of a User ID and
+				 * Public Key packet */
+	PGP_CERT_PERSONA = 0x11,/* Persona certification of a User ID and
+				 * Public Key packet */
+	PGP_CERT_CASUAL = 0x12,	/* Casual certification of a User ID and
+				 * Public Key packet */
+	PGP_CERT_POSITIVE = 0x13,	/* Positive certification of a
+					 * User ID and Public Key packet */
+
+	PGP_SIG_SUBKEY = 0x18,	/* Subkey Binding Signature */
+	PGP_SIG_PRIMARY = 0x19,	/* Primary Key Binding Signature */
+	PGP_SIG_DIRECT = 0x1f,	/* Signature directly on a key */
+
+	PGP_SIG_REV_KEY = 0x20,	/* Key revocation signature */
+	PGP_SIG_REV_SUBKEY = 0x28,	/* Subkey revocation signature */
+	PGP_SIG_REV_CERT = 0x30,/* Certification revocation signature */
+
+	PGP_SIG_TIMESTAMP = 0x40,	/* Timestamp signature */
+
+	PGP_SIG_3RD_PARTY = 0x50/* Third-Party Confirmation signature */
+} pgp_sig_type_t;
+
+/** Struct to hold params of an RSA signature */
+typedef struct pgp_rsa_sig_t {
+	BIGNUM         *sig;	/* the signature value (m^d % n) */
+} pgp_rsa_sig_t;
+
+/** Struct to hold params of a DSA signature */
+typedef struct pgp_dsa_sig_t {
+	BIGNUM         *r;	/* DSA value r */
+	BIGNUM         *s;	/* DSA value s */
+} pgp_dsa_sig_t;
+
+/** pgp_elgamal_signature_t */
+typedef struct pgp_elgamal_sig_t {
+	BIGNUM         *r;
+	BIGNUM         *s;
+} pgp_elgamal_sig_t;
+
+#define PGP_KEY_ID_SIZE		8
+#define PGP_FINGERPRINT_SIZE	20
+
+/** Struct to hold a signature packet.
+ *
+ * \see RFC4880 5.2.2
+ * \see RFC4880 5.2.3
+ */
+typedef struct pgp_sig_info_t {
+	pgp_version_t   version;/* signature version number */
+	pgp_sig_type_t  type;	/* signature type value */
+	time_t          birthtime;	/* creation time of the signature */
+	time_t          duration;	/* number of seconds it's valid for */
+	time_t          key_expiry;	/* number of seconds key is valid for */
+	uint8_t         key_flags;
+	uint8_t		signer_id[PGP_KEY_ID_SIZE];	/* Eight-octet key ID
+							 * of signer */
+	pgp_pubkey_alg_t key_alg;	/* public key algorithm number */
+	pgp_hash_alg_t hash_alg;	/* hashing algorithm number */
+	union {
+		pgp_rsa_sig_t	rsa;	/* An RSA Signature */
+		pgp_dsa_sig_t	dsa;	/* A DSA Signature */
+		pgp_elgamal_sig_t	elgamal;	/* deprecated */
+		pgp_data_t	unknown;	/* private or experimental */
+	}			sig;	/* signature params */
+	size_t          v4_hashlen;
+	uint8_t		*v4_hashed;
+	unsigned	 birthtime_set:1;
+	unsigned	 signer_id_set:1;
+	unsigned	 duration_set:1;
+	unsigned	 key_expiry_set:1;
+	unsigned	 key_flags_set:1;
+	unsigned	 primary_userid:1;
+} pgp_sig_info_t;
+
+typedef enum {
+	PGP_KEYFLAG_CERT_KEYS = 0x01,
+	PGP_KEYFLAG_SIGN_DATA = 0x02,
+	PGP_KEYFLAG_ENC_COMM = 0x04,
+	PGP_KEYFLAG_ENC_STORAGE = 0x08,
+	PGP_KEYFLAG_SPLIT = 0x10,
+	PGP_KEYFLAG_AUTH = 0x20,
+	PGP_KEYFLAG_GROUP = 0x80
+} pgp_key_flags_t;
+
+/** Struct used when parsing a signature */
+typedef struct pgp_sig_t {
+	pgp_sig_info_t info;	/* The signature information */
+	/* The following fields are only used while parsing the signature */
+	uint8_t		 hash2[2];	/* high 2 bytes of hashed value */
+	size_t		 v4_hashstart;	/* only valid if accumulate is set */
+	pgp_hash_t     *hash;	/* the hash filled in for the data so far */
+} pgp_sig_t;
+
+/** The raw bytes of a signature subpacket */
+
+typedef struct pgp_ss_raw_t {
+	pgp_content_enum	 tag;
+	size_t          	 length;
+	uint8_t			*raw;
+} pgp_ss_raw_t;
+
+/** Signature Subpacket : Trust Level */
+
+typedef struct pgp_ss_trust_t {
+	uint8_t			 level;		/* Trust Level */
+	uint8_t			 amount;	/* Amount */
+} pgp_ss_trust_t;
+
+/** Signature Subpacket : Notation Data */
+typedef struct pgp_ss_notation_t {
+	pgp_data_t		flags;
+	pgp_data_t		name;
+	pgp_data_t		value;
+} pgp_ss_notation_t;
+
+/** Signature Subpacket : Signature Target */
+typedef struct pgp_ss_sig_target_t {
+	pgp_pubkey_alg_t	pka_alg;
+	pgp_hash_alg_t		hash_alg;
+	pgp_data_t		hash;
+} pgp_ss_sig_target_t;
+
+/** pgp_subpacket_t */
+typedef struct pgp_subpacket_t {
+	size_t          	 length;
+	uint8_t			*raw;
+} pgp_subpacket_t;
+
+/** Types of Compression */
+typedef enum {
+	PGP_C_NONE = 0,
+	PGP_C_ZIP = 1,
+	PGP_C_ZLIB = 2,
+	PGP_C_BZIP2 = 3
+} pgp_compression_type_t;
+
+/** pgp_one_pass_sig_t */
+typedef struct {
+	uint8_t			version;
+	pgp_sig_type_t		sig_type;
+	pgp_hash_alg_t		hash_alg;
+	pgp_pubkey_alg_t	key_alg;
+	uint8_t			keyid[PGP_KEY_ID_SIZE];
+	unsigned		nested;
+} pgp_one_pass_sig_t;
+
+/** Signature Subpacket : Revocation Key */
+typedef struct {
+	uint8_t   		class;
+	uint8_t   		algid;
+	uint8_t   		fingerprint[PGP_FINGERPRINT_SIZE];
+} pgp_ss_revocation_key_t;
+
+/** Signature Subpacket : Revocation Reason */
+typedef struct {
+	uint8_t   		 code;
+	char			*reason;
+} pgp_ss_revocation_t;
+
+/** litdata_type_t */
+typedef enum {
+	PGP_LDT_BINARY = 'b',
+	PGP_LDT_TEXT = 't',
+	PGP_LDT_UTF8 = 'u',
+	PGP_LDT_LOCAL = 'l',
+	PGP_LDT_LOCAL2 = '1'
+} pgp_litdata_enum;
+
+/** pgp_litdata_header_t */
+typedef struct {
+	pgp_litdata_enum	format;
+	char			filename[256];
+	time_t			mtime;
+} pgp_litdata_header_t;
+
+/** pgp_litdata_body_t */
+typedef struct {
+	unsigned         length;
+	uint8_t		*data;
+	void		*mem;		/* pgp_memory_t pointer */
+} pgp_litdata_body_t;
+
+/** pgp_header_var_t */
+typedef struct {
+	char           *key;
+	char           *value;
+} pgp_header_var_t;
+
+/** pgp_headers_t */
+typedef struct {
+	pgp_header_var_t	*headers;
+	unsigned	         headerc;
+} pgp_headers_t;
+
+/** pgp_armour_header_t */
+typedef struct {
+	const char	*type;
+	pgp_headers_t	 headers;
+} pgp_armour_header_t;
+
+/** pgp_fixed_body_t */
+typedef struct pgp_fixed_body_t {
+	unsigned        length;
+	uint8_t		data[8192];	/* \todo fix hard-coded value? */
+} pgp_fixed_body_t;
+
+/** pgp_dyn_body_t */
+typedef struct pgp_dyn_body_t {
+	unsigned         length;
+	uint8_t		*data;
+} pgp_dyn_body_t;
+
+enum {
+	PGP_SE_IP_DATA_VERSION = 1,
+	PGP_PKSK_V3 = 3
+};
+
+/** pgp_pk_sesskey_params_rsa_t */
+typedef struct {
+	BIGNUM         *encrypted_m;
+	BIGNUM         *m;
+} pgp_pk_sesskey_params_rsa_t;
+
+/** pgp_pk_sesskey_params_elgamal_t */
+typedef struct {
+	BIGNUM         *g_to_k;
+	BIGNUM         *encrypted_m;
+} pgp_pk_sesskey_params_elgamal_t;
+
+/** pgp_pk_sesskey_params_t */
+typedef union {
+	pgp_pk_sesskey_params_rsa_t rsa;
+	pgp_pk_sesskey_params_elgamal_t elgamal;
+} pgp_pk_sesskey_params_t;
+
+/** pgp_pk_sesskey_t */
+typedef uint8_t key_id_t[PGP_KEY_ID_SIZE];
+typedef struct {
+	unsigned			version;
+	key_id_t				key_id;
+	pgp_pubkey_alg_t		alg;
+	pgp_pk_sesskey_params_t	params;
+	pgp_symm_alg_t		symm_alg;
+	uint8_t				key[PGP_MAX_KEY_SIZE];
+	uint16_t			checksum;
+} pgp_pk_sesskey_t;
+
+/** pgp_seckey_passphrase_t */
+typedef struct {
+	const pgp_seckey_t *seckey;
+	char          **passphrase;	/* point somewhere that gets filled
+					 * in to work around constness of
+					 * content */
+} pgp_seckey_passphrase_t;
+
+/** pgp_get_seckey_t */
+typedef struct {
+	const pgp_seckey_t **seckey;
+	const pgp_pk_sesskey_t *pk_sesskey;
+} pgp_get_seckey_t;
+
+/** pgp_parser_union_content_t */
+typedef union {
+	const char 			*error;
+	pgp_parser_errcode_t		errcode;
+	pgp_ptag_t			ptag;
+	pgp_pubkey_t			pubkey;
+	pgp_data_t			trust;
+	uint8_t				*userid;
+	pgp_data_t			userattr;
+	pgp_sig_t			sig;
+	pgp_ss_raw_t			ss_raw;
+	pgp_ss_trust_t		ss_trust;
+	unsigned			ss_revocable;
+	time_t				ss_time;
+	uint8_t				ss_issuer[PGP_KEY_ID_SIZE];
+	pgp_ss_notation_t		ss_notation;
+	pgp_subpacket_t		packet;
+	pgp_compression_type_t	compressed;
+	pgp_one_pass_sig_t		one_pass_sig;
+	pgp_data_t			ss_skapref;
+	pgp_data_t			ss_hashpref;
+	pgp_data_t			ss_zpref;
+	pgp_data_t			ss_key_flags;
+	pgp_data_t			ss_key_server_prefs;
+	unsigned			ss_primary_userid;
+	char				*ss_regexp;
+	char				*ss_policy;
+	char				*ss_keyserv;
+	pgp_ss_revocation_key_t	ss_revocation_key;
+	pgp_data_t			ss_userdef;
+	pgp_data_t			ss_unknown;
+	pgp_litdata_header_t		litdata_header;
+	pgp_litdata_body_t		litdata_body;
+	pgp_dyn_body_t		mdc;
+	pgp_data_t			ss_features;
+	pgp_ss_sig_target_t		ss_sig_target;
+	pgp_data_t			ss_embedded_sig;
+	pgp_ss_revocation_t		ss_revocation;
+	pgp_seckey_t			seckey;
+	uint8_t				*ss_signer;
+	pgp_armour_header_t		armour_header;
+	const char 			*armour_trailer;
+	pgp_headers_t			cleartext_head;
+	pgp_fixed_body_t		cleartext_body;
+	struct pgp_hash_t		*cleartext_trailer;
+	pgp_dyn_body_t		unarmoured_text;
+	pgp_pk_sesskey_t		pk_sesskey;
+	pgp_seckey_passphrase_t	skey_passphrase;
+	unsigned			se_ip_data_header;
+	pgp_dyn_body_t		se_ip_data_body;
+	pgp_fixed_body_t		se_data_body;
+	pgp_get_seckey_t		get_seckey;
+} pgp_contents_t;
+
+/** pgp_packet_t */
+struct pgp_packet_t {
+	pgp_content_enum	tag;		/* type of contents */
+	uint8_t			critical;	/* for sig subpackets */
+	pgp_contents_t	u;		/* union for contents */
+};
+
+/** pgp_fingerprint_t */
+typedef struct {
+	uint8_t			fingerprint[PGP_FINGERPRINT_SIZE];
+	unsigned        	length;
+	pgp_hash_alg_t	hashtype;
+} pgp_fingerprint_t;
+
+int pgp_keyid(uint8_t *, const size_t, const pgp_pubkey_t *, pgp_hash_alg_t);
+int pgp_fingerprint(pgp_fingerprint_t *, const pgp_pubkey_t *, pgp_hash_alg_t);
+
+void pgp_finish(void);
+void pgp_pubkey_free(pgp_pubkey_t *);
+int pgp_pubkey_dup(pgp_pubkey_t *,pgp_pubkey_t *);
+void pgp_userid_free(uint8_t **);
+void pgp_data_free(pgp_data_t *);
+void pgp_sig_free(pgp_sig_t *);
+void pgp_ss_notation_free(pgp_ss_notation_t *);
+void pgp_ss_revocation_free(pgp_ss_revocation_t *);
+void pgp_ss_sig_target_free(pgp_ss_sig_target_t *);
+
+void pgp_subpacket_free(pgp_subpacket_t *);
+void pgp_parser_content_free(pgp_packet_t *);
+void pgp_seckey_free(pgp_seckey_t *);
+int pgp_seckey_dup(pgp_seckey_t *,pgp_seckey_t *);
+void pgp_pk_sesskey_free(pgp_pk_sesskey_t *);
+
+#define DYNARRAY(type, arr)	\
+	unsigned arr##c; unsigned arr##vsize; type *arr##s
+
+#define EXPAND_ARRAY(str, arr) do {					\
+	if (str->arr##c == str->arr##vsize) {				\
+		void	*__newarr;					\
+		char	*__newarrc;					\
+		unsigned	__newsize;				\
+		__newsize = (str->arr##vsize * 2) + 10; 		\
+		if ((__newarrc = __newarr = realloc(str->arr##s,	\
+			__newsize * sizeof(*str->arr##s))) == NULL) {	\
+			(void) fprintf(stderr, "EXPAND_ARRAY - bad realloc\n"); \
+		} else {						\
+			(void) memset(&__newarrc[str->arr##vsize * sizeof(*str->arr##s)], \
+				0x0, (__newsize - str->arr##vsize) * sizeof(*str->arr##s)); \
+			str->arr##s = __newarr;				\
+			str->arr##vsize = __newsize;			\
+		}							\
+	}								\
+} while(/*CONSTCOND*/0)
+
+#define FREE_ARRAY(str, arr) do {					\
+	if (str->arr##s) {				\
+		free(str->arr##s);				\
+		str->arr##s = NULL;				\
+		str->arr##vsize = 0;			\
+		str->arr##c = 0;				\
+	}								\
+} while(/*CONSTCOND*/0)
+
+#define INIT_ARRAY(str, arr) do {					\
+	str->arr##s = NULL;				\
+	str->arr##vsize = 0;			\
+	str->arr##c = 0;				\
+} while(/*CONSTCOND*/0)
+
+/** pgp_keydata_key_t
+ */
+typedef union {
+	pgp_pubkey_t pubkey;
+	pgp_seckey_t seckey;
+} pgp_keydata_key_t;
+
+/** userid signature subpackets */
+typedef struct pgp_uidsig_t {
+	uint32_t		uid;		/* index in userid array in key */
+	pgp_sig_info_t		siginfo;
+	uint8_t			trustlevel;	/* level of trust */
+	uint8_t			trustamount;	/* amount of trust */
+	pgp_subpacket_t	packet;
+} pgp_uidsig_t;
+
+/** subkey signature subpackets */
+typedef struct pgp_subkeysig_t {
+	uint32_t		subkey;		/* index of subkey in array */
+	pgp_sig_info_t		siginfo;
+	pgp_subpacket_t	packet;
+} pgp_subkeysig_t;
+
+typedef struct pgp_subkey_t {
+	pgp_keydata_key_t	key;		/* pubkey/seckey data */
+    uint8_t id[PGP_KEY_ID_SIZE];
+} pgp_subkey_t;
+
+typedef struct pgp_directsig_t {
+	pgp_sig_info_t siginfo;
+	pgp_subpacket_t  packet;
+} pgp_directsig_t;
+
+/* describes a user's key */
+struct pgp_key_t {
+	pgp_content_enum	type;		/* type of key */
+	pgp_keydata_key_t	key;		/* pubkey/seckey data */
+
+	DYNARRAY(pgp_directsig_t, directsig);		/* direct signatures */
+
+	DYNARRAY(uint8_t *, uid);		/* array of user ids */
+	DYNARRAY(pgp_uidsig_t, uidsig);	/* array of signature for user ids */
+
+    /* TODO user attributes */
+
+	DYNARRAY(pgp_subkey_t,	subkey);	/* array of subkeys */
+	DYNARRAY(pgp_subkeysig_t, subkeysig);	/* array of sigs for subkeys */
+	
+	uint8_t			pubkeyid[PGP_KEY_ID_SIZE];
+	pgp_fingerprint_t	pubkeyfpr;	/* pgp signature fingerprint */
+};
+
+typedef enum {
+	PGP_VALID,
+    PGP_WEAK,
+    PGP_TOOSHORT,
+	PGP_INVALID,
+	PGP_EXPIRED,
+	PGP_REVOKED
+} pgp_key_rating_t;
+
+#define MDC_PKT_TAG	0xd3
+#endif /* PACKET_H_ */
diff --git a/netpgp/netpgp/readerwriter.h b/netpgp/netpgp/readerwriter.h
new file mode 100644
index 0000000000000000000000000000000000000000..51029f5f9c753006b7f0ca76cf4c0d63a4c406d7
--- /dev/null
+++ b/netpgp/netpgp/readerwriter.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+#ifndef READERWRITER_H_
+#define READERWRITER_H_
+
+#include "create.h"
+
+#include "memory.h"
+
+/* if this is defined, we'll use mmap in preference to file ops */
+#define USE_MMAP_FOR_FILES      1
+
+void pgp_reader_set_fd(pgp_stream_t *, int);
+void pgp_reader_set_mmap(pgp_stream_t *, int);
+void pgp_reader_set_memory(pgp_stream_t *, const void *, size_t);
+
+/* Do a sum mod 65536 of all bytes read (as needed for secret keys) */
+void pgp_reader_push_sum16(pgp_stream_t *);
+uint16_t pgp_reader_pop_sum16(pgp_stream_t *);
+
+void pgp_reader_push_se_ip_data(pgp_stream_t *, pgp_crypt_t *,
+				pgp_region_t *);
+void pgp_reader_pop_se_ip_data(pgp_stream_t *);
+
+/* */
+unsigned pgp_write_mdc(pgp_output_t *, const uint8_t *);
+unsigned pgp_write_se_ip_pktset(pgp_output_t *, const uint8_t *,
+		       const unsigned,
+		       pgp_crypt_t *);
+void pgp_push_enc_crypt(pgp_output_t *, pgp_crypt_t *);
+int pgp_push_enc_se_ip(pgp_output_t *, const pgp_keyring_t *, const char *, unsigned);
+
+/* Secret Key checksum */
+void pgp_push_checksum_writer(pgp_output_t *, pgp_seckey_t *);
+unsigned pgp_pop_skey_checksum_writer(pgp_output_t *);
+
+
+/* memory writing */
+void pgp_setup_memory_write(pgp_output_t **, pgp_memory_t **, size_t);
+void pgp_teardown_memory_write(pgp_output_t *, pgp_memory_t *);
+
+/* memory reading */
+void pgp_setup_memory_read(pgp_io_t *,
+				pgp_stream_t **,
+				pgp_memory_t *,
+				void *,
+				pgp_cb_ret_t callback(const pgp_packet_t *,
+					pgp_cbdata_t *),
+				unsigned);
+void pgp_teardown_memory_read(pgp_stream_t *, pgp_memory_t *);
+
+/* file writing */
+int pgp_setup_file_write(pgp_output_t **, const char *, unsigned);
+void pgp_teardown_file_write(pgp_output_t *, int);
+
+/* file appending */
+int pgp_setup_file_append(pgp_output_t **, const char *);
+void pgp_teardown_file_append(pgp_output_t *, int);
+
+/* file reading */
+int pgp_setup_file_read(pgp_io_t *,
+			pgp_stream_t **,
+			const char *,
+			void *,
+			pgp_cb_ret_t callback(const pgp_packet_t *,
+		    			pgp_cbdata_t *),
+			unsigned);
+void pgp_teardown_file_read(pgp_stream_t *, int);
+
+unsigned pgp_reader_set_accumulate(pgp_stream_t *, unsigned);
+
+/* useful callbacks */
+pgp_cb_ret_t pgp_litdata_cb(const pgp_packet_t *, pgp_cbdata_t *);
+pgp_cb_ret_t pgp_pk_sesskey_cb(const pgp_packet_t *, pgp_cbdata_t *);
+pgp_cb_ret_t pgp_get_seckey_cb(const pgp_packet_t *, pgp_cbdata_t *);
+
+int pgp_getpassphrase(void *, char *, size_t);
+
+#endif /* READERWRITER_H_ */
diff --git a/netpgp/netpgp/signature.h b/netpgp/netpgp/signature.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb8bc4d046605e97ad12acc52a91a1bca11e7453
--- /dev/null
+++ b/netpgp/netpgp/signature.h
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef SIGNATURE_H_
+#define SIGNATURE_H_
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+
+#include "packet.h"
+#include "create.h"
+#include "memory.h"
+
+typedef struct pgp_create_sig_t	 pgp_create_sig_t;
+
+pgp_create_sig_t *pgp_create_sig_new(void);
+void pgp_create_sig_delete(pgp_create_sig_t *);
+
+unsigned pgp_check_useridcert_sig(const pgp_pubkey_t *,
+			  const uint8_t *,
+			  const pgp_sig_t *,
+			  const pgp_pubkey_t *);
+unsigned pgp_check_userattrcert_sig(const pgp_pubkey_t *,
+			  const pgp_data_t *,
+			  const pgp_sig_t *,
+			  const pgp_pubkey_t *);
+unsigned pgp_check_subkey_sig(const pgp_pubkey_t *,
+			   const pgp_pubkey_t *,
+			   const pgp_sig_t *,
+			   const pgp_pubkey_t *);
+unsigned pgp_check_direct_sig(const pgp_pubkey_t *,
+			   const pgp_sig_t *,
+			   const pgp_pubkey_t *);
+unsigned pgp_check_hash_sig(pgp_hash_t *,
+			 const pgp_sig_t *,
+			 const pgp_pubkey_t *);
+void pgp_sig_start_key_sig(pgp_create_sig_t *,
+				  const pgp_pubkey_t *,
+				  const pgp_pubkey_t * subkey, // parameter added by Delta Chat to allow subkey binding signatures, EDIT BY MR (bp)
+				  const uint8_t *,
+				  pgp_sig_type_t);
+void pgp_start_sig(pgp_create_sig_t *,
+			const pgp_seckey_t *,
+			const pgp_hash_alg_t,
+			const pgp_sig_type_t);
+
+void pgp_sig_add_data(pgp_create_sig_t *, const void *, size_t);
+pgp_hash_t *pgp_sig_get_hash(pgp_create_sig_t *);
+unsigned   pgp_end_hashed_subpkts(pgp_create_sig_t *);
+unsigned pgp_write_sig(pgp_output_t *, pgp_create_sig_t *,
+			const pgp_pubkey_t *, const pgp_seckey_t *);
+unsigned pgp_add_issuer_keyid(pgp_create_sig_t *,
+			const uint8_t *);
+void pgp_add_primary_userid(pgp_create_sig_t *, unsigned);
+
+unsigned 
+pgp_add_creation_time(pgp_create_sig_t *sig, time_t when);
+unsigned 
+pgp_add_sig_expiration_time(pgp_create_sig_t *sig, time_t duration);
+unsigned 
+pgp_add_key_expiration_time(pgp_create_sig_t *sig, time_t duration);
+unsigned
+pgp_add_key_flags(pgp_create_sig_t *sig, uint8_t flags);
+unsigned
+pgp_add_key_prefs(pgp_create_sig_t *sig);
+unsigned
+pgp_add_key_features(pgp_create_sig_t *sig);
+
+/* Standard Interface */
+unsigned   pgp_sign_file(pgp_io_t *,
+			const char *,
+			const char *,
+			const pgp_seckey_t *,
+			const char *,
+			const time_t,
+			const time_t,
+			const unsigned,
+			const unsigned,
+			const unsigned);
+
+int pgp_sign_detached(pgp_io_t *,
+			const char *,
+			char *,
+			const pgp_seckey_t *,
+			const char *,
+			const time_t,
+			const time_t,
+			const unsigned,
+			const unsigned);
+
+/* armoured stuff */
+unsigned pgp_crc24(unsigned, uint8_t);
+
+void pgp_reader_push_dearmour(pgp_stream_t *);
+
+void pgp_reader_pop_dearmour(pgp_stream_t *);
+unsigned pgp_writer_push_clearsigned(pgp_output_t *, pgp_create_sig_t *);
+void pgp_writer_push_armor_msg(pgp_output_t *);
+
+typedef enum {
+	PGP_PGP_MESSAGE = 1,
+	PGP_PGP_PUBLIC_KEY_BLOCK,
+	PGP_PGP_PRIVATE_KEY_BLOCK,
+	PGP_PGP_MULTIPART_MESSAGE_PART_X_OF_Y,
+	PGP_PGP_MULTIPART_MESSAGE_PART_X,
+	PGP_PGP_SIGNATURE
+} pgp_armor_type_t;
+
+#define CRC24_INIT 0xb704ceL
+
+unsigned pgp_writer_use_armored_sig(pgp_output_t *);
+
+void pgp_writer_push_armoured(pgp_output_t *, pgp_armor_type_t);
+
+pgp_memory_t   *pgp_sign_buf(pgp_io_t *,
+				const void *,
+				const size_t,
+				const pgp_seckey_t *,
+				const time_t,
+				const time_t,
+				const char *,
+				const unsigned,
+				const unsigned);
+
+/** \ingroup Core_Create
+ * needed for signature creation
+ */
+struct pgp_create_sig_t {
+	pgp_hash_t		 hash;
+	pgp_sig_t		 sig;
+	pgp_memory_t		*mem;
+	pgp_output_t		*output;	/* how to do the writing */
+	unsigned		 hashoff;	/* hashed count offset */
+	unsigned		 hashlen;
+	unsigned 		 unhashoff;
+};
+
+void 
+pgp_sig_start_key_rev(pgp_create_sig_t *sig,
+				  const pgp_pubkey_t *key,
+				  pgp_sig_type_t type);
+
+unsigned 
+pgp_add_revocation_reason(
+        pgp_create_sig_t *sig,
+        uint8_t code, const char *reason);
+
+#endif /* SIGNATURE_H_ */
diff --git a/netpgp/netpgp/types.h b/netpgp/netpgp/types.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc03f7b0735a07923397d23e8ad3c553b863eefa
--- /dev/null
+++ b/netpgp/netpgp/types.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#ifndef TYPES_H_
+#define TYPES_H_
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+typedef struct pgp_io_t {
+	void	*outs;	/* output file stream */
+	void	*errs;	/* file stream to put error messages */
+	void	*res;	/* file stream to put results */
+} pgp_io_t;
+
+/** pgp_map_t
+ */
+typedef struct {
+	int             type;
+	const char     *string;
+} pgp_map_t;
+
+/** pgp_errcode_name_map_t */
+typedef pgp_map_t		pgp_errcode_name_map_t;
+
+typedef struct pgp_crypt_t	pgp_crypt_t;
+
+/** pgp_hash_t */
+typedef struct pgp_hash_t	pgp_hash_t;
+
+/** Revocation Reason type */
+typedef uint8_t		pgp_ss_rr_code_t;
+
+/** pgp_packet_t */
+typedef struct pgp_packet_t	pgp_packet_t;
+
+/** Writer flags */
+typedef enum {
+	PGP_WF_DUMMY
+} pgp_writer_flags_t;
+
+/**
+ * \ingroup Create
+ * Contains the required information about how to write
+ */
+typedef struct pgp_output_t	pgp_output_t;
+
+#endif /* TYPES_H_ */
diff --git a/netpgp/netpgp/validate.h b/netpgp/netpgp/validate.h
new file mode 100644
index 0000000000000000000000000000000000000000..44455a33b033b528def746c636aca81916f0ade8
--- /dev/null
+++ b/netpgp/netpgp/validate.h
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#ifndef VALIDATE_H_
+#define VALIDATE_H_	1
+
+/** Struct used with the validate_key_cb callback */
+typedef struct validate_key_cb_t{
+	pgp_content_enum	type;		/* type of key */
+	pgp_keydata_key_t	key;		/* pubkey/seckey data */
+	pgp_keydata_key_t	subkey;
+	uint8_t			     pubkeyid[PGP_KEY_ID_SIZE];
+	enum {
+        LS_UNKNOWN = 0,
+		LS_ATTRIBUTE,
+		LS_ID,
+        LS_SUBKEY,
+        LS_PRIMARY,
+	}               	 last_seen;
+
+	uint8_t			*userid;
+	pgp_data_t		 userattr;
+	uint8_t			 hash[PGP_MAX_HASH_SIZE];
+	const pgp_keyring_t	*keyring;
+	pgp_validation_t	*result;
+	pgp_cb_ret_t(*getpassphrase) (const pgp_packet_t *,
+						pgp_cbdata_t *);
+
+    unsigned not_commited; /* tells on_valid it is first commit of that key */
+	pgp_sig_info_t valid_sig_info; /* store last valid sig info */
+    unsigned sig_is_valid; /* condition to call on_valid at packet end */ 
+    pgp_cb_ret_t(*on_valid) ( /* callback for action on valid sig */
+                    struct validate_key_cb_t *, /* this struct */
+                    const pgp_subpacket_t *); /* sig packet */
+    void *on_valid_args; /* pointer to argument for on_valid callback */
+
+} validate_key_cb_t;
+
+/** Struct use with the validate_data_cb callback */
+typedef struct {
+	enum {
+		LITDATA,
+		SIGNED_CLEARTEXT
+	} type;
+	union {
+		pgp_litdata_body_t	 litdata_body;
+		pgp_fixed_body_t	 cleartext_body;
+	} data;
+	uint8_t			 	 hash[PGP_MAX_HASH_SIZE];
+	pgp_memory_t			*mem;
+	const pgp_keyring_t		*keyring;
+	pgp_validation_t		*result;
+	char				*detachname;
+} validate_data_cb_t;
+
+pgp_cb_ret_t pgp_validate_key_cb(const pgp_packet_t *, pgp_cbdata_t *);
+
+unsigned check_binary_sig(const uint8_t *,
+		const unsigned,
+		const pgp_sig_t *,
+		const pgp_pubkey_t *);
+
+unsigned   pgp_validate_file(pgp_io_t *,
+			pgp_validation_t *,
+			const char *,
+			const char *,
+			const int,
+			const pgp_keyring_t *);
+
+unsigned   pgp_validate_mem(pgp_io_t *,
+			pgp_validation_t *,
+			pgp_memory_t *,
+			pgp_memory_t **,
+			const int,
+			const pgp_keyring_t *);
+
+unsigned   pgp_validate_mem_detached(pgp_io_t *,
+			pgp_validation_t *,
+			pgp_memory_t *,
+			pgp_memory_t **,
+			const int,
+			const pgp_keyring_t *,
+			pgp_memory_t *);
+
+pgp_cb_ret_t validate_data_cb(const pgp_packet_t *, pgp_cbdata_t *);
+void pgp_free_sig_info(pgp_sig_info_t *);
+
+unsigned 
+pgp_filter_keys_fileread(pgp_io_t *io,
+            pgp_keyring_t *destpubring,
+            pgp_keyring_t *destsecring,
+            pgp_keyring_t *certring,
+			const unsigned armour,
+			const char *filename);
+
+unsigned 
+pgp_filter_keys_from_mem(pgp_io_t *io,
+            pgp_keyring_t *destpubring,
+            pgp_keyring_t *destsecring,
+            pgp_keyring_t *certring,
+            const unsigned armour,
+            pgp_memory_t *mem);
+#endif /* !VALIDATE_H_ */
diff --git a/netpgp/netpgp/version.h b/netpgp/netpgp/version.h
new file mode 100644
index 0000000000000000000000000000000000000000..74bf76580ba357e6cf001311ff72c0ce5b596d68
--- /dev/null
+++ b/netpgp/netpgp/version.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#ifndef VERSION_H_
+#define VERSION_H_	1
+
+#ifndef NETPGP_AUTOCONF_VERSION
+#define NETPGP_AUTOCONF_VERSION	PACKAGE_VERSION
+#endif
+
+#ifndef NETPGP_MAINTAINER
+#define NETPGP_MAINTAINER	PACKAGE_BUGREPORT
+#endif
+
+/* development versions have .99 suffix */
+#define NETPGP_BASE_VERSION	"3.99.99"
+
+#define NETPGP_VERSION_CAT(a, b)	"NetPGP for Delta Chat " a "/[" b "]"
+#define NETPGP_VERSION_STRING \
+	NETPGP_VERSION_CAT(NETPGP_BASE_VERSION, NETPGP_AUTOCONF_VERSION)
+
+#endif /* !VERSION_H_ */
diff --git a/netpgp/netpgp/writer.h b/netpgp/netpgp/writer.h
new file mode 100644
index 0000000000000000000000000000000000000000..632eeeb67aab242339486f2e9943aa0a5039fd6c
--- /dev/null
+++ b/netpgp/netpgp/writer.h
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+
+#ifndef WRITER_H_
+#define WRITER_H_
+
+#include "types.h"
+#include "packet.h"
+#include "crypto.h"
+#include "errors.h"
+#include "keyring.h"
+
+/**
+ * \ingroup Writer
+ * the writer function prototype
+ */
+
+typedef struct pgp_writer_t	pgp_writer_t;
+typedef unsigned pgp_writer_func_t(const uint8_t *,
+	     unsigned,
+	     pgp_error_t **,
+	     pgp_writer_t *);
+typedef unsigned 
+pgp_writer_finaliser_t(pgp_error_t **, pgp_writer_t *);
+typedef void    pgp_writer_destroyer_t(pgp_writer_t *);
+
+/** Writer settings */
+struct pgp_writer_t {
+	pgp_writer_func_t	 *writer;	/* the writer itself */
+	pgp_writer_finaliser_t *finaliser;	/* the writer's finaliser */
+	pgp_writer_destroyer_t *destroyer;	/* the writer's destroyer */
+	void			 *arg;	/* writer-specific argument */
+	pgp_writer_t	 	 *next;	/* next writer in the stack */
+	pgp_io_t		 *io;	/* IO for errors and output */
+};
+
+
+void *pgp_writer_get_arg(pgp_writer_t *);
+
+void pgp_writer_set(pgp_output_t *,
+	       pgp_writer_func_t *,
+	       pgp_writer_finaliser_t *,
+	       pgp_writer_destroyer_t *,
+	       void *);
+void pgp_writer_push(pgp_output_t *,
+		pgp_writer_func_t *,
+		pgp_writer_finaliser_t *,
+		pgp_writer_destroyer_t *,
+		void *);
+void pgp_writer_pop(pgp_output_t *);
+unsigned pgp_writer_passthrough(const uint8_t *,
+		       unsigned,
+		       pgp_error_t **,
+		       pgp_writer_t *);
+
+void pgp_writer_set_fd(pgp_output_t *, int);
+unsigned pgp_writer_close(pgp_output_t *);
+
+unsigned pgp_write(pgp_output_t *, const void *, unsigned);
+unsigned pgp_write_length(pgp_output_t *, unsigned);
+unsigned pgp_write_ptag(pgp_output_t *, pgp_content_enum);
+unsigned pgp_write_scalar(pgp_output_t *, unsigned, unsigned);
+unsigned pgp_write_mpi(pgp_output_t *, const BIGNUM *);
+
+void pgp_writer_info_delete(pgp_writer_t *);
+unsigned pgp_writer_info_finalise(pgp_error_t **, pgp_writer_t *);
+
+void pgp_push_stream_enc_se_ip(pgp_output_t *, pgp_key_t *, const char *);
+
+void pgp_push_sum16_writer(pgp_output_t *output);
+
+uint16_t pgp_pop_sum16_writer(pgp_output_t *output);
+
+#endif /* WRITER_H_ */
diff --git a/netpgp/openssl_crypto.c b/netpgp/openssl_crypto.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b8499ee01a8ccb426fa519f40f8cb849dd72b58
--- /dev/null
+++ b/netpgp/openssl_crypto.c
@@ -0,0 +1,1070 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#ifdef HAVE_OPENSSL_DSA_H
+#include <openssl/dsa.h>
+#endif
+
+#ifdef HAVE_OPENSSL_RSA_H
+#include <openssl/rsa.h>
+#endif
+
+#ifdef HAVE_OPENSSL_ERR_H
+#include <openssl/err.h>
+#endif
+
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "netpgp/crypto.h"
+#include "netpgp/keyring.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/netpgpdigest.h"
+#include "netpgp/packet.h"
+#include "netpgp/openssl11stub.h"
+
+
+static void
+test_seckey(const pgp_seckey_t *seckey)
+{
+	RSA *test = RSA_new();
+
+    RSA_set0_key(test,
+     BN_dup(seckey->pubkey.key.rsa.n),
+     BN_dup(seckey->pubkey.key.rsa.e),
+     BN_dup(seckey->key.rsa.d));
+
+    RSA_set0_factors(test,
+     BN_dup(seckey->key.rsa.p),
+     BN_dup(seckey->key.rsa.q));
+
+	if (RSA_check_key(test) != 1) {
+		(void) fprintf(stderr,
+			"test_seckey: RSA_check_key failed\n");
+	}
+	RSA_free(test);
+}
+
+static int
+md5_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "md5_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(MD5_CTX))) == NULL) {
+		(void) fprintf(stderr, "md5_init: bad alloc\n");
+		return 0;
+	}
+	MD5_Init(hash->data);
+	return 1;
+}
+
+static void
+md5_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	MD5_Update(hash->data, data, length);
+}
+
+static unsigned
+md5_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	MD5_Final(out, hash->data);
+	free(hash->data);
+	hash->data = NULL;
+	return 16;
+}
+
+static const pgp_hash_t md5 = {
+	PGP_HASH_MD5,
+	MD5_DIGEST_LENGTH,
+	"MD5",
+	md5_init,
+	md5_add,
+	md5_finish,
+	NULL
+};
+
+/**
+   \ingroup Core_Crypto
+   \brief Initialise to MD5
+   \param hash Hash to initialise
+*/
+void
+pgp_hash_md5(pgp_hash_t *hash)
+{
+	*hash = md5;
+}
+
+static int
+sha1_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "sha1_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(SHA_CTX))) == NULL) {
+		(void) fprintf(stderr, "sha1_init: bad alloc\n");
+		return 0;
+	}
+	SHA1_Init(hash->data);
+	return 1;
+}
+
+static void
+sha1_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha1_add", data, length);
+	}
+	SHA1_Update(hash->data, data, length);
+}
+
+static unsigned
+sha1_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	SHA1_Final(out, hash->data);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha1_finish", out, PGP_SHA1_HASH_SIZE);
+	}
+	free(hash->data);
+	hash->data = NULL;
+	return PGP_SHA1_HASH_SIZE;
+}
+
+static const pgp_hash_t sha1 = {
+	PGP_HASH_SHA1,
+	PGP_SHA1_HASH_SIZE,
+	"SHA1",
+	sha1_init,
+	sha1_add,
+	sha1_finish,
+	NULL
+};
+
+/**
+   \ingroup Core_Crypto
+   \brief Initialise to SHA1
+   \param hash Hash to initialise
+*/
+void
+pgp_hash_sha1(pgp_hash_t *hash)
+{
+	*hash = sha1;
+}
+
+static int
+sha256_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "sha256_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(SHA256_CTX))) == NULL) {
+		(void) fprintf(stderr, "sha256_init: bad alloc\n");
+		return 0;
+	}
+	SHA256_Init(hash->data);
+	return 1;
+}
+
+static void
+sha256_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha256_add", data, length);
+	}
+	SHA256_Update(hash->data, data, length);
+}
+
+static unsigned
+sha256_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	SHA256_Final(out, hash->data);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha1_finish", out, SHA256_DIGEST_LENGTH);
+	}
+	free(hash->data);
+	hash->data = NULL;
+	return SHA256_DIGEST_LENGTH;
+}
+
+static const pgp_hash_t sha256 = {
+	PGP_HASH_SHA256,
+	SHA256_DIGEST_LENGTH,
+	"SHA256",
+	sha256_init,
+	sha256_add,
+	sha256_finish,
+	NULL
+};
+
+void
+pgp_hash_sha256(pgp_hash_t *hash)
+{
+	*hash = sha256;
+}
+
+/*
+ * SHA384
+ */
+static int
+sha384_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "sha384_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(SHA512_CTX))) == NULL) {
+		(void) fprintf(stderr, "sha384_init: bad alloc\n");
+		return 0;
+	}
+	SHA384_Init(hash->data);
+	return 1;
+}
+
+static void
+sha384_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha384_add", data, length);
+	}
+	SHA384_Update(hash->data, data, length);
+}
+
+static unsigned
+sha384_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	SHA384_Final(out, hash->data);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha384_finish", out, SHA384_DIGEST_LENGTH);
+	}
+	free(hash->data);
+	hash->data = NULL;
+	return SHA384_DIGEST_LENGTH;
+}
+
+static const pgp_hash_t sha384 = {
+	PGP_HASH_SHA384,
+	SHA384_DIGEST_LENGTH,
+	"SHA384",
+	sha384_init,
+	sha384_add,
+	sha384_finish,
+	NULL
+};
+
+void
+pgp_hash_sha384(pgp_hash_t *hash)
+{
+	*hash = sha384;
+}
+
+/*
+ * SHA512
+ */
+static int
+sha512_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "sha512_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(SHA512_CTX))) == NULL) {
+		(void) fprintf(stderr, "sha512_init: bad alloc\n");
+		return 0;
+	}
+	SHA512_Init(hash->data);
+	return 1;
+}
+
+static void
+sha512_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha512_add", data, length);
+	}
+	SHA512_Update(hash->data, data, length);
+}
+
+static unsigned
+sha512_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	SHA512_Final(out, hash->data);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha512_finish", out, SHA512_DIGEST_LENGTH);
+	}
+	free(hash->data);
+	hash->data = NULL;
+	return SHA512_DIGEST_LENGTH;
+}
+
+static const pgp_hash_t sha512 = {
+	PGP_HASH_SHA512,
+	SHA512_DIGEST_LENGTH,
+	"SHA512",
+	sha512_init,
+	sha512_add,
+	sha512_finish,
+	NULL
+};
+
+void
+pgp_hash_sha512(pgp_hash_t *hash)
+{
+	*hash = sha512;
+}
+
+/*
+ * SHA224
+ */
+
+static int
+sha224_init(pgp_hash_t *hash)
+{
+	if (hash->data) {
+		(void) fprintf(stderr, "sha224_init: hash data non-null\n");
+	}
+	if ((hash->data = calloc(1, sizeof(SHA256_CTX))) == NULL) {
+		(void) fprintf(stderr, "sha256_init: bad alloc\n");
+		return 0;
+	}
+	SHA224_Init(hash->data);
+	return 1;
+}
+
+static void
+sha224_add(pgp_hash_t *hash, const uint8_t *data, unsigned length)
+{
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha224_add", data, length);
+	}
+	SHA224_Update(hash->data, data, length);
+}
+
+static unsigned
+sha224_finish(pgp_hash_t *hash, uint8_t *out)
+{
+	SHA224_Final(out, hash->data);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sha224_finish", out, SHA224_DIGEST_LENGTH);
+	}
+	free(hash->data);
+	hash->data = NULL;
+	return SHA224_DIGEST_LENGTH;
+}
+
+static const pgp_hash_t sha224 = {
+	PGP_HASH_SHA224,
+	SHA224_DIGEST_LENGTH,
+	"SHA224",
+	sha224_init,
+	sha224_add,
+	sha224_finish,
+	NULL
+};
+
+void
+pgp_hash_sha224(pgp_hash_t *hash)
+{
+	*hash = sha224;
+}
+
+unsigned
+pgp_dsa_verify(const uint8_t *hash, size_t hash_length,
+	       const pgp_dsa_sig_t *sig,
+	       const pgp_dsa_pubkey_t *dsa)
+{
+	unsigned	qlen;
+	DSA_SIG        *osig;
+	DSA            *odsa;
+	int             ret;
+
+	osig = DSA_SIG_new();
+    DSA_SIG_set0(osig,
+	    BN_dup(sig->r),
+	    BN_dup(sig->s));
+
+	odsa = DSA_new();
+    DSA_set0_pqg(odsa,
+        BN_dup(dsa->p),
+        BN_dup(dsa->q),
+        BN_dup(dsa->g));
+
+    DSA_set0_key(odsa,
+        BN_dup(dsa->y),
+        NULL);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "input hash", hash, hash_length);
+		(void) fprintf(stderr, "Q=%d\n", BN_num_bytes(dsa->q));
+	}
+	if ((qlen = (unsigned)BN_num_bytes(dsa->q)) < hash_length) {
+		hash_length = qlen;
+	}
+	ret = DSA_do_verify(hash, (int)hash_length, osig, odsa);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "ret=%d\n", ret);
+	}
+	if (ret < 0) {
+		(void) fprintf(stderr, "pgp_dsa_verify: DSA verification\n");
+		return 0;
+	}
+
+	DSA_free(odsa);
+
+	DSA_SIG_free(osig);
+
+	return (unsigned)ret;
+}
+
+/**
+   \ingroup Core_Crypto
+   \brief Recovers message digest from the signature
+   \param out Where to write decrypted data to
+   \param in Encrypted data
+   \param length Length of encrypted data
+   \param pubkey RSA public key
+   \return size of recovered message digest
+*/
+int
+pgp_rsa_public_decrypt(uint8_t *out,
+			const uint8_t *in,
+			size_t length,
+			const pgp_rsa_pubkey_t *pubkey)
+{
+	RSA            *orsa;
+	int             n;
+
+	orsa = RSA_new();
+    RSA_set0_key(orsa,
+     BN_dup(pubkey->n),
+     BN_dup(pubkey->e),
+     NULL);
+
+
+	n = RSA_public_decrypt((int)length, in, out, orsa, RSA_NO_PADDING);
+
+	RSA_free(orsa);
+
+	return n;
+}
+
+/**
+   \ingroup Core_Crypto
+   \brief Signs data with RSA
+   \param out Where to write signature
+   \param in Data to sign
+   \param length Length of data
+   \param seckey RSA secret key
+   \param pubkey RSA public key
+   \return number of bytes decrypted
+*/
+int
+pgp_rsa_private_encrypt(uint8_t *out,
+			const uint8_t *in,
+			size_t length,
+			const pgp_rsa_seckey_t *seckey,
+			const pgp_rsa_pubkey_t *pubkey)
+{
+	RSA            *orsa;
+	int             n;
+
+	orsa = RSA_new();
+
+    RSA_set0_key(orsa,
+     BN_dup(pubkey->n),
+     BN_dup(pubkey->e),
+     BN_dup(seckey->d));
+
+	/* p and q are round the other way in openssl */
+    RSA_set0_factors(orsa,
+     /* q */ BN_dup(seckey->p),
+     /* p */ BN_dup(seckey->q));
+
+
+	/* debug */
+	if (RSA_check_key(orsa) != 1) {
+		(void) fprintf(stderr, "RSA_check_key is not set\n");
+		return 0;
+	}
+	/* end debug */
+
+	n = RSA_private_encrypt((int)length, in, out, orsa, RSA_NO_PADDING);
+
+	RSA_free(orsa);
+
+	return n;
+}
+
+/**
+\ingroup Core_Crypto
+\brief Decrypts RSA-encrypted data
+\param out Where to write the plaintext
+\param in Encrypted data
+\param length Length of encrypted data
+\param seckey RSA secret key
+\param pubkey RSA public key
+\return size of recovered plaintext
+*/
+int
+pgp_rsa_private_decrypt(uint8_t *out,
+			const uint8_t *in,
+			size_t length,
+			const pgp_rsa_seckey_t *seckey,
+			const pgp_rsa_pubkey_t *pubkey)
+{
+	RSA            *keypair;
+	int             n;
+	char            errbuf[1024];
+
+	keypair = RSA_new();
+    RSA_set0_key(keypair,
+     BN_dup(pubkey->n),
+     BN_dup(pubkey->e),
+     BN_dup(seckey->d));
+
+    RSA_set0_factors(keypair,
+     BN_dup(seckey->p),
+     BN_dup(seckey->q));
+
+	/* debug */
+	if (RSA_check_key(keypair) != 1) {
+		(void) fprintf(stderr, "RSA_check_key is not set\n");
+		return 0;
+	}
+	/* end debug */
+
+	n = RSA_private_decrypt((int)length, in, out, keypair, RSA_NO_PADDING);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		printf("pgp_rsa_private_decrypt: n=%d\n",n);
+	}
+
+	errbuf[0] = '\0';
+	if (n == -1) {
+		unsigned long   err = ERR_get_error();
+
+		ERR_error_string(err, &errbuf[0]);
+		(void) fprintf(stderr, "openssl error : %s\n", errbuf);
+	}
+	RSA_free(keypair);
+
+	return n;
+}
+
+/**
+   \ingroup Core_Crypto
+   \brief RSA-encrypts data
+   \param out Where to write the encrypted data
+   \param in Plaintext
+   \param length Size of plaintext
+   \param pubkey RSA Public Key
+*/
+int
+pgp_rsa_public_encrypt(uint8_t *out,
+			const uint8_t *in,
+			size_t length,
+			const pgp_rsa_pubkey_t *pubkey)
+{
+	RSA            *orsa;
+	int             n;
+
+	/* printf("pgp_rsa_public_encrypt: length=%ld\n", length); */
+
+	orsa = RSA_new();
+    RSA_set0_key(orsa,
+     BN_dup(pubkey->n),
+     BN_dup(pubkey->e),
+     NULL);
+
+	/* printf("len: %ld\n", length); */
+	/* pgp_print_bn("n: ", orsa->n); */
+	/* pgp_print_bn("e: ", orsa->e); */
+	n = RSA_public_encrypt((int)length, in, out, orsa, RSA_NO_PADDING);
+
+	if (n == -1) {
+		BIO            *fd_out;
+
+		fd_out = BIO_new_fd(fileno(stderr), BIO_NOCLOSE);
+		ERR_print_errors(fd_out);
+	}
+	RSA_free(orsa);
+
+	return n;
+}
+
+/**
+   \ingroup Core_Crypto
+   \brief Finalise openssl
+   \note Would usually call pgp_finish() instead
+   \sa pgp_finish()
+*/
+void
+pgp_crypto_finish(void)
+{
+    // No cleanup since OpenSSL 1.1.0
+	// CRYPTO_cleanup_all_ex_data();
+}
+
+/**
+   \ingroup Core_Hashes
+   \brief Get Hash name
+   \param hash Hash struct
+   \return Hash name
+*/
+const char     *
+pgp_text_from_hash(pgp_hash_t *hash)
+{
+	return hash->name;
+}
+
+/**
+ \ingroup HighLevel_KeyGenerate
+ \brief Generates an RSA keypair
+ \param numbits Modulus size
+ \param e Public Exponent
+ \param keydata Pointer to keydata struct to hold new key
+ \return 1 if key generated successfully; otherwise 0
+ \note It is the caller's responsibility to call pgp_keydata_free(keydata)
+*/
+unsigned
+pgp_rsa_generate_keypair(pgp_key_t *keydata,
+			const int numbits,
+			const unsigned long e__,
+			const char *hashalg,
+			const char *cipher,
+            const uint8_t *passphrase,
+            const size_t pplen)
+{
+	pgp_seckey_t *seckey;
+	RSA            *rsa;
+	BN_CTX         *ctx;
+	pgp_output_t *output;
+	pgp_memory_t   *mem;
+    int res;
+    const BIGNUM *_n = NULL;
+    const BIGNUM *_e = NULL;
+    const BIGNUM *_d = NULL;
+    const BIGNUM *_p = NULL;
+    const BIGNUM *_q = NULL;
+
+	ctx = BN_CTX_new();
+	pgp_keydata_init(keydata, PGP_PTAG_CT_SECRET_KEY);
+	seckey = pgp_get_writable_seckey(keydata);
+
+	/* generate the key pair */
+
+    BIGNUM *exp = BN_new();
+	BN_set_word(exp, e__);
+    /*
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    exp = BN_bin2bn((const unsigned char *)&e, sizeof(e), NULL);
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    exp = BN_lebin2bn((const unsigned char *)&e, sizeof(e), NULL);
+#else
+#error Unsupported endian
+#endif
+    if (!exp)
+        return 0;
+	*/
+
+	rsa = RSA_new();
+    res = RSA_generate_key_ex(rsa, numbits, exp, NULL);
+
+    BN_free(exp);
+
+    if (!res){
+        RSA_free(rsa);
+		return 0;
+    };
+
+	/* populate pgp key from ssl key */
+
+	seckey->pubkey.version = PGP_V4;
+	seckey->pubkey.birthtime = time(NULL);
+	seckey->pubkey.days_valid = 0;
+	seckey->pubkey.alg = PGP_PKA_RSA;
+
+    RSA_get0_key(rsa, &_n, &_e, &_d);
+
+	seckey->pubkey.key.rsa.n = BN_dup(_n);
+	seckey->pubkey.key.rsa.e = BN_dup(_e);
+
+	/* seckey->s2k_usage = PGP_S2KU_ENCRYPTED_AND_HASHED; */
+	seckey->s2k_usage = PGP_S2KU_NONE;
+	/* seckey->s2k_specifier = PGP_S2KS_SALTED;*/
+	/* seckey->s2k_specifier=PGP_S2KS_SIMPLE; */
+	if ((seckey->hash_alg = pgp_str_to_hash_alg(hashalg)) == PGP_HASH_UNKNOWN) {
+		seckey->hash_alg = PGP_HASH_SHA1;
+	}
+	seckey->alg = pgp_str_to_cipher(cipher);
+	seckey->octetc = 0;
+	seckey->checksum = 0;
+
+    RSA_get0_factors(rsa, &_p, &_q);
+	seckey->key.rsa.d = BN_dup(_d);
+	seckey->key.rsa.p = BN_dup(_p);
+	seckey->key.rsa.q = BN_dup(_q);
+	seckey->key.rsa.u = BN_mod_inverse(NULL, _p, _q, ctx);
+	if (seckey->key.rsa.u == NULL) {
+        RSA_free(rsa);
+		(void) fprintf(stderr, "seckey->key.rsa.u is NULL\n");
+		return 0;
+	}
+	BN_CTX_free(ctx);
+
+	RSA_free(rsa);
+
+	pgp_keyid(keydata->pubkeyid, PGP_KEY_ID_SIZE, &keydata->key.seckey.pubkey, seckey->hash_alg);
+	pgp_fingerprint(&keydata->pubkeyfpr, &keydata->key.seckey.pubkey, seckey->hash_alg);
+
+	/* Generate checksum */
+
+	output = NULL;
+	mem = NULL;
+
+	pgp_setup_memory_write(&output, &mem, 128);
+
+	pgp_push_checksum_writer(output, seckey);
+
+	switch (seckey->pubkey.alg) {
+	case PGP_PKA_DSA:
+		return pgp_write_mpi(output, seckey->key.dsa.x);
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!pgp_write_mpi(output, seckey->key.rsa.d) ||
+		    !pgp_write_mpi(output, seckey->key.rsa.p) ||
+		    !pgp_write_mpi(output, seckey->key.rsa.q) ||
+		    !pgp_write_mpi(output, seckey->key.rsa.u)) {
+			return 0;
+		}
+		break;
+	case PGP_PKA_ELGAMAL:
+		return pgp_write_mpi(output, seckey->key.elgamal.x);
+
+	default:
+		(void) fprintf(stderr, "Bad seckey->pubkey.alg\n");
+		return 0;
+	}
+
+	/* close rather than pop, since its the only one on the stack */
+	pgp_writer_close(output);
+	pgp_teardown_memory_write(output, mem);
+
+	/* should now have checksum in seckey struct */
+
+	/* test */
+	if (pgp_get_debug_level(__FILE__)) {
+		test_seckey(seckey);
+	}
+
+	return 1;
+}
+
+/**
+ \ingroup HighLevel_KeyGenerate
+ \brief Creates a self-signed RSA keypair
+ \param numbits Modulus size
+ \param e Public Exponent
+ \param userid User ID
+ \return The new keypair or NULL
+
+ \note It is the caller's responsibility to call pgp_keydata_free(keydata)
+ \sa pgp_rsa_generate_keypair()
+ \sa pgp_keydata_free()
+*/
+#if 0 //////
+pgp_key_t  *
+pgp_rsa_new_selfsign_key(const int numbits,
+				const unsigned long e,
+				const uint8_t *userid,
+				const char *hashalg,
+				const char *cipher)
+{
+	pgp_key_t  *keydata;
+
+	keydata = pgp_keydata_new();
+	if (!pgp_rsa_generate_keypair(keydata, numbits, e, hashalg, cipher,
+                                  (const uint8_t *) "", (const size_t) 0) ||
+	    !pgp_add_selfsigned_userid(keydata, NULL, userid, 0 /*never expire*/)) {
+		pgp_keydata_free(keydata);
+		return NULL;
+	}
+	return keydata;
+}
+#endif //////
+
+pgp_dsa_sig_t        *
+pgp_dsa_sign(uint8_t *hashbuf,
+		unsigned hashsize,
+		const pgp_dsa_seckey_t *secdsa,
+		const pgp_dsa_pubkey_t *pubdsa)
+{
+	DSA_SIG        *dsasig;
+	DSA            *odsa;
+    pgp_dsa_sig_t  *pgpdsasig;
+    const BIGNUM *pr = NULL;
+    const BIGNUM *ps = NULL;
+
+	odsa = DSA_new();
+    DSA_set0_pqg(odsa,
+        BN_dup(pubdsa->p),
+        BN_dup(pubdsa->q),
+        BN_dup(pubdsa->g));
+
+    DSA_set0_key(odsa,
+        BN_dup(pubdsa->y),
+        BN_dup(secdsa->x));
+
+	dsasig = DSA_do_sign(hashbuf, (int)hashsize, odsa);
+
+	DSA_free(odsa);
+
+
+    DSA_SIG_get0(dsasig, &pr, &ps);
+
+    pgpdsasig = calloc(1,sizeof(pgp_dsa_sig_t));
+    if(pgpdsasig != NULL){
+        pgpdsasig->r = BN_dup(pr);
+        pgpdsasig->s = BN_dup(ps);
+    }
+
+	DSA_SIG_free(dsasig);
+	return pgpdsasig;
+}
+
+
+/*
+ * Decide the number of bits in the random componont k
+ *
+ * It should be in the same range as p for signing (which
+ * is deprecated), but can be much smaller for encrypting.
+ *
+ * Until I research it further, I just mimic gpg behaviour.
+ * It has a special mapping table, for values <= 5120,
+ * above that it uses 'arbitrary high number'.	Following
+ * algorihm hovers 10-70 bits above gpg values.  And for
+ * larger p, it uses gpg's algorihm.
+ *
+ * The point is - if k gets large, encryption will be
+ * really slow.  It does not matter for decryption.
+ */
+static int
+decide_k_bits(int p_bits)
+{
+	return (p_bits <= 5120) ? p_bits / 10 + 160 : (p_bits / 8 + 200) * 3 / 2;
+}
+
+int
+pgp_elgamal_public_encrypt(uint8_t *g_to_k, uint8_t *encm,
+			const uint8_t *in,
+			size_t size,
+			const pgp_elgamal_pubkey_t *pubkey)
+{
+	int	ret = 0;
+	int	k_bits;
+	BIGNUM	   *m;
+	BIGNUM	   *p;
+	BIGNUM	   *g;
+	BIGNUM	   *y;
+	BIGNUM	   *k;
+	BIGNUM	   *yk;
+	BIGNUM	   *c1;
+	BIGNUM	   *c2;
+	BN_CTX	   *tmp;
+
+	m = BN_bin2bn(in, (int)size, NULL);
+	p = pubkey->p;
+	g = pubkey->g;
+	y = pubkey->y;
+	k = BN_new();
+	yk = BN_new();
+	c1 = BN_new();
+	c2 = BN_new();
+	tmp = BN_CTX_new();
+	if (!m || !p || !g || !y || !k || !yk || !c1 || !c2 || !tmp) {
+		goto done;
+	}
+	/*
+	 * generate k
+	 */
+	k_bits = decide_k_bits(BN_num_bits(p));
+	if (!BN_rand(k, k_bits, 0, 0)) {
+		goto done;
+	}
+	/*
+	 * c1 = g^k c2 = m * y^k
+	 */
+	if (!BN_mod_exp(c1, g, k, p, tmp)) {
+		goto done;
+	}
+	if (!BN_mod_exp(yk, y, k, p, tmp)) {
+		goto done;
+	}
+	if (!BN_mod_mul(c2, m, yk, p, tmp)) {
+		goto done;
+	}
+	/* result */
+	BN_bn2bin(c1, g_to_k);
+	ret = BN_num_bytes(c1);	/* c1 = g^k */
+	BN_bn2bin(c2, encm);
+	ret += BN_num_bytes(c2); /* c2 = m * y^k */
+done:
+	if (tmp) {
+		BN_CTX_free(tmp);
+	}
+	if (c2) {
+		BN_clear_free(c2);
+	}
+	if (c1) {
+		BN_clear_free(c1);
+	}
+	if (yk) {
+		BN_clear_free(yk);
+	}
+	if (k) {
+		BN_clear_free(k);
+	}
+	if (g) {
+		BN_clear_free(g);
+	}
+	return ret;
+}
+
+int
+pgp_elgamal_private_decrypt(uint8_t *out,
+				const uint8_t *g_to_k,
+				const uint8_t *in,
+				size_t length,
+				const pgp_elgamal_seckey_t *seckey,
+				const pgp_elgamal_pubkey_t *pubkey)
+{
+	BIGNUM	*bndiv;
+	BIGNUM	*c1x;
+	BN_CTX	*tmp;
+	BIGNUM	*c1;
+	BIGNUM	*c2;
+	BIGNUM	*p;
+	BIGNUM	*x;
+	BIGNUM	*m;
+	int	 ret;
+
+	ret = 0;
+	/* c1 and c2 are in g_to_k and in, respectively*/
+	c1 = BN_bin2bn(g_to_k, (int)length, NULL);
+	c2 = BN_bin2bn(in, (int)length, NULL);
+	/* other bits */
+	p = pubkey->p;
+	x = seckey->x;
+	c1x = BN_new();
+	bndiv = BN_new();
+	m = BN_new();
+	tmp = BN_CTX_new();
+	if (!c1 || !c2 || !p || !x || !c1x || !bndiv || !m || !tmp) {
+		goto done;
+	}
+	/*
+	 * m = c2 / (c1^x)
+	 */
+	if (!BN_mod_exp(c1x, c1, x, p, tmp)) {
+		goto done;
+	}
+	if (!BN_mod_inverse(bndiv, c1x, p, tmp)) {
+		goto done;
+	}
+	if (!BN_mod_mul(m, c2, bndiv, p, tmp)) {
+		goto done;
+	}
+	/* result */
+	ret = BN_bn2bin(m, out);
+done:
+	if (tmp) {
+		BN_CTX_free(tmp);
+	}
+	if (m) {
+		BN_clear_free(m);
+	}
+	if (bndiv) {
+		BN_clear_free(bndiv);
+	}
+	if (c1x) {
+		BN_clear_free(c1x);
+	}
+	if (x) {
+		BN_clear_free(x);
+	}
+	if (p) {
+		BN_clear_free(p);
+	}
+	if (c1) {
+		BN_clear_free(c1);
+	}
+	if (c2) {
+		BN_clear_free(c2);
+	}
+	return ret;
+}
diff --git a/netpgp/packet-parse.c b/netpgp/packet-parse.c
new file mode 100644
index 0000000000000000000000000000000000000000..dbf24506c66d0d1d14b01bfa58ec767b5a2ec73a
--- /dev/null
+++ b/netpgp/packet-parse.c
@@ -0,0 +1,3686 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ * \brief Parser for OpenPGP packets
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_OPENSSL_CAST_H
+#include <openssl/cast.h>
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "netpgp/packet.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/keyring.h"
+#include "netpgp/errors.h"
+#include "netpgp/packet-show.h"
+#include "netpgp/create.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/crypto.h"
+#include "netpgp/netpgpdigest.h"
+
+#define ERRP(cbinfo, cont, err)	do {					\
+	cont.u.error = err;						\
+	CALLBACK(PGP_PARSER_ERROR, cbinfo, &cont);			\
+	return 0;							\
+	/*NOTREACHED*/							\
+} while(/*CONSTCOND*/0)
+
+/**
+ * limread_data reads the specified amount of the subregion's data
+ * into a data_t structure
+ *
+ * \param data	Empty structure which will be filled with data
+ * \param len	Number of octets to read
+ * \param subregion
+ * \param stream	How to parse
+ *
+ * \return 1 on success, 0 on failure
+ */
+static int
+limread_data(pgp_data_t *data, unsigned len,
+		  pgp_region_t *subregion, pgp_stream_t *stream)
+{
+	data->len = len;
+
+	if (subregion->length - subregion->readc < len) {
+		(void) fprintf(stderr, "limread_data: bad length\n");
+		return 0;
+	}
+
+	data->contents = calloc(1, data->len);
+	if (!data->contents) {
+		return 0;
+	}
+
+	return pgp_limited_read(stream, data->contents, data->len, subregion,
+			&stream->errors, &stream->readinfo, &stream->cbinfo);
+}
+
+/**
+ * read_data reads the remainder of the subregion's data
+ * into a data_t structure
+ *
+ * \param data
+ * \param subregion
+ * \param stream
+ *
+ * \return 1 on success, 0 on failure
+ */
+static int
+read_data(pgp_data_t *data, pgp_region_t *region, pgp_stream_t *stream)
+{
+	int	cc;
+
+	cc = region->length - region->readc;
+	return (cc >= 0) ? limread_data(data, (unsigned)cc, region, stream) : 0;
+}
+
+/**
+ * Reads the remainder of the subregion as a string.
+ * It is the user's responsibility to free the memory allocated here.
+ */
+
+static int
+read_unsig_str(uint8_t **str, pgp_region_t *subregion,
+		     pgp_stream_t *stream)
+{
+	size_t	len;
+
+	len = subregion->length - subregion->readc;
+	if ((*str = calloc(1, len + 1)) == NULL) {
+		return 0;
+	}
+	if (len &&
+	    !pgp_limited_read(stream, *str, len, subregion, &stream->errors,
+				     &stream->readinfo, &stream->cbinfo)) {
+		return 0;
+	}
+	(*str)[len] = '\0';
+	return 1;
+}
+
+static int
+read_string(char **str, pgp_region_t *subregion, pgp_stream_t *stream)
+{
+	return read_unsig_str((uint8_t **) str, subregion, stream);
+}
+
+void
+pgp_init_subregion(pgp_region_t *subregion, pgp_region_t *region)
+{
+	(void) memset(subregion, 0x0, sizeof(*subregion));
+	subregion->parent = region;
+}
+
+/*
+ * XXX: replace pgp_ptag_t with something more appropriate for limiting reads
+ */
+
+/* data from partial blocks is queued up in virtual block in stream */
+static int
+read_partial_data(pgp_stream_t *stream,
+                  pgp_reader_t *readinfo,
+                  void *dest, size_t length)
+{
+    unsigned	n;
+
+    if (pgp_get_debug_level(__FILE__)) {
+        (void) fprintf(stderr, "fd_reader: coalesced data, off %d\n",
+                       readinfo->virtualoff);
+    }
+    n = MIN(readinfo->virtualc - readinfo->virtualoff, (unsigned)length);
+    (void) memcpy(dest, &readinfo->virtualpkt[readinfo->virtualoff], n);
+    readinfo->virtualoff += n;
+    if (readinfo->virtualoff == readinfo->virtualc) {
+        free(readinfo->virtualpkt);
+        readinfo->virtualpkt = NULL;
+        readinfo->virtualc = readinfo->virtualoff = 0;
+    }
+    return (int)n;
+}
+
+
+/**
+ * low-level function to read data from reader function
+ *
+ * Use this function, rather than calling the reader directly.
+ *
+ * If the accumulate flag is set in *stream, the function
+ * adds the read data to the accumulated data, and updates
+ * the accumulated length. This is useful if, for example,
+ * the application wants access to the raw data as well as the
+ * parsed data.
+ *
+ * This function will also try to read the entire amount asked for, but not
+ * if it is over INT_MAX. Obviously many callers will know that they
+ * never ask for that much and so can avoid the extra complexity of
+ * dealing with return codes and filled-in lengths.
+ *
+ * \param *dest
+ * \param *plength
+ * \param flags
+ * \param *stream
+ *
+ * \return PGP_R_OK
+ * \return PGP_R_PARTIAL_READ
+ * \return PGP_R_EOF
+ * \return PGP_R_EARLY_EOF
+ *
+ * \sa #pgp_reader_ret_t for details of return codes
+ */
+
+static int
+sub_base_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+	      pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	size_t          n;
+
+	/* reading more than this would look like an error */
+	if (length > INT_MAX)
+		length = INT_MAX;
+
+	for (n = 0; n < length;) {
+		int	r;
+
+        if (!readinfo->coalescing && readinfo->virtualc && readinfo->virtualoff < readinfo->virtualc) {
+            r = read_partial_data(stream, readinfo, (char*) dest + n, length - n);
+        }else{
+            r = readinfo->reader(stream, (char *) dest + n,
+                                 length - n, errors,
+                                 readinfo, cbinfo);
+        }
+		if (r > (int)(length - n)) {
+			(void) fprintf(stderr, "sub_base_read: bad read\n");
+			return 0;
+		}
+		if (r < 0) {
+			return r;
+		}
+		if (r == 0) {
+			break;
+		}
+		n += (unsigned)r;
+	}
+
+	if (n == 0) {
+		return 0;
+	}
+	if (readinfo->accumulate) {
+		if (readinfo->asize < readinfo->alength) {
+			(void) fprintf(stderr, "sub_base_read: bad size\n");
+			return 0;
+		}
+		if (readinfo->alength + n > readinfo->asize) {
+			uint8_t	*temp;
+
+			readinfo->asize = (readinfo->asize * 2) + (unsigned)n;
+			temp = realloc(readinfo->accumulated, readinfo->asize);
+			if (temp == NULL) {
+				(void) fprintf(stderr,
+					"sub_base_read: bad alloc\n");
+				return 0;
+			}
+			readinfo->accumulated = temp;
+		}
+		if (readinfo->asize < readinfo->alength + n) {
+			(void) fprintf(stderr, "sub_base_read: bad realloc\n");
+			return 0;
+		}
+		(void) memcpy(readinfo->accumulated + readinfo->alength, dest,
+				n);
+	}
+	/* we track length anyway, because it is used for packet offsets */
+	readinfo->alength += (unsigned)n;
+	/* and also the position */
+	readinfo->position += (unsigned)n;
+
+	return (int)n;
+}
+
+int
+pgp_stacked_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+		 pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	return sub_base_read(stream, dest, length, errors, readinfo->next, cbinfo);
+}
+
+/* This will do a full read so long as length < MAX_INT */
+static int
+base_read(uint8_t *dest, size_t length, pgp_stream_t *stream)
+{
+	return sub_base_read(stream, dest, length, &stream->errors, &stream->readinfo,
+			     &stream->cbinfo);
+}
+
+/*
+ * Read a full size_t's worth. If the return is < than length, then
+ * *last_read tells you why - < 0 for an error, == 0 for EOF
+ */
+
+static size_t
+full_read(pgp_stream_t *stream, uint8_t *dest,
+		size_t length,
+		int *last_read,
+		pgp_error_t **errors,
+		pgp_reader_t *readinfo,
+		pgp_cbdata_t *cbinfo)
+{
+	size_t          t;
+	int             r = 0;	/* preset in case some loon calls with length
+				 * == 0 */
+
+	for (t = 0; t < length;) {
+		r = sub_base_read(stream, dest + t, length - t, errors, readinfo,
+				cbinfo);
+		if (r <= 0) {
+			*last_read = r;
+			return t;
+		}
+		t += (size_t)r;
+	}
+
+	*last_read = r;
+
+	return t;
+}
+
+
+
+/** Read a scalar value of selected length from reader.
+ *
+ * Read an unsigned scalar value from reader in Big Endian representation.
+ *
+ * This function does not know or care about packet boundaries. It
+ * also assumes that an EOF is an error.
+ *
+ * \param *result	The scalar value is stored here
+ * \param *reader	Our reader
+ * \param length	How many bytes to read
+ * \return		1 on success, 0 on failure
+ */
+static unsigned
+_read_scalar(unsigned *result, unsigned length,
+	     pgp_stream_t *stream)
+{
+	unsigned        t = 0;
+
+	if (length > sizeof(*result)) {
+		(void) fprintf(stderr, "_read_scalar: bad length\n");
+		return 0;
+	}
+
+	while (length--) {
+		uint8_t	c;
+		int	r;
+
+		r = base_read(&c, 1, stream);
+		if (r != 1)
+			return 0;
+		t = (t << 8) + c;
+	}
+
+	*result = t;
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Read bytes from a region within the packet.
+ *
+ * Read length bytes into the buffer pointed to by *dest.
+ * Make sure we do not read over the packet boundary.
+ * Updates the Packet Tag's pgp_ptag_t::readc.
+ *
+ * If length would make us read over the packet boundary, or if
+ * reading fails, we call the callback with an error.
+ *
+ * Note that if the region is indeterminate, this can return a short
+ * read - check region->last_read for the length. EOF is indicated by
+ * a success return and region->last_read == 0 in this case (for a
+ * region of known length, EOF is an error).
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param dest		The destination buffer
+ * \param length	How many bytes to read
+ * \param region	Pointer to packet region
+ * \param errors    Error stack
+ * \param readinfo		Reader info
+ * \param cbinfo	Callback info
+ * \return		1 on success, 0 on error
+ */
+unsigned
+pgp_limited_read(pgp_stream_t *stream, uint8_t *dest,
+			size_t length,
+			pgp_region_t *region,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo)
+{
+	size_t	r;
+	int	lr;
+
+	if (!region->indeterminate &&
+	    region->readc + length > region->length) {
+		PGP_ERROR_1(errors, PGP_E_P_NOT_ENOUGH_DATA, "%s",
+		    "Not enough data");
+		return 0;
+	}
+	r = full_read(stream, dest, length, &lr, errors, readinfo, cbinfo);
+	if (lr < 0) {
+		PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
+		return 0;
+	}
+	if (!region->indeterminate && r != length) {
+		PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
+		return 0;
+	}
+	region->last_read = (unsigned)r;
+	do {
+		region->readc += (unsigned)r;
+		if (region->parent && region->length > region->parent->length) {
+			(void) fprintf(stderr,
+				"ops_limited_read: bad length\n");
+			return 0;
+		}
+	} while ((region = region->parent) != NULL);
+	return 1;
+}
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Call pgp_limited_read on next in stack
+*/
+unsigned
+pgp_stacked_limited_read(pgp_stream_t *stream, uint8_t *dest, unsigned length,
+			 pgp_region_t *region,
+			 pgp_error_t **errors,
+			 pgp_reader_t *readinfo,
+			 pgp_cbdata_t *cbinfo)
+{
+	return pgp_limited_read(stream, dest, length, region, errors,
+				readinfo->next, cbinfo);
+}
+
+static unsigned
+limread(uint8_t *dest, unsigned length,
+	     pgp_region_t *region, pgp_stream_t *info)
+{
+	return pgp_limited_read(info, dest, length, region, &info->errors,
+				&info->readinfo, &info->cbinfo);
+}
+
+#if 0
+static unsigned
+exact_limread(uint8_t *dest, unsigned len,
+		   pgp_region_t *region,
+		   pgp_stream_t *stream)
+{
+	unsigned   ret;
+
+	stream->exact_read = 1;
+	ret = limread(dest, len, region, stream);
+	stream->exact_read = 0;
+	return ret;
+}
+#endif
+
+/** Skip over length bytes of this packet.
+ *
+ * Calls limread() to skip over some data.
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param length	How many bytes to skip
+ * \param *region	Pointer to packet region
+ * \param *stream	How to parse
+ * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
+ */
+static int
+limskip(unsigned length, pgp_region_t *region, pgp_stream_t *stream)
+{
+	uint8_t   buf[NETPGP_BUFSIZ];
+
+	while (length > 0) {
+		unsigned	n = length % NETPGP_BUFSIZ;
+
+		if (!limread(buf, n, region, stream)) {
+			return 0;
+		}
+		length -= n;
+	}
+	return 1;
+}
+
+/** Read a scalar.
+ *
+ * Read a big-endian scalar of length bytes, respecting packet
+ * boundaries (by calling limread() to read the raw data).
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param *dest		The scalar value is stored here
+ * \param length	How many bytes make up this scalar (at most 4)
+ * \param *region	Pointer to current packet region
+ * \param *stream	How to parse
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
+ *
+ * \see RFC4880 3.1
+ */
+static int
+limread_scalar(unsigned *dest,
+			unsigned len,
+			pgp_region_t *region,
+			pgp_stream_t *stream)
+{
+	uint8_t		c[4] = "";
+	unsigned        t;
+	unsigned        n;
+
+	if (len > 4) {
+		(void) fprintf(stderr, "limread_scalar: bad length\n");
+		return 0;
+	}
+	/*LINTED*/
+	if (/*CONSTCOND*/sizeof(*dest) < 4) {
+		(void) fprintf(stderr, "limread_scalar: bad dest\n");
+		return 0;
+	}
+	if (!limread(c, len, region, stream)) {
+		return 0;
+	}
+	for (t = 0, n = 0; n < len; ++n) {
+		t = (t << 8) + c[n];
+	}
+	*dest = t;
+	return 1;
+}
+
+/** Read a scalar.
+ *
+ * Read a big-endian scalar of length bytes, respecting packet
+ * boundaries (by calling limread() to read the raw data).
+ *
+ * The value read is stored in a size_t, which is a different size
+ * from an unsigned on some platforms.
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param *dest		The scalar value is stored here
+ * \param length	How many bytes make up this scalar (at most 4)
+ * \param *region	Pointer to current packet region
+ * \param *stream	How to parse
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
+ *
+ * \see RFC4880 3.1
+ */
+static int
+limread_size_t(size_t *dest,
+				unsigned length,
+				pgp_region_t *region,
+				pgp_stream_t *stream)
+{
+	unsigned        tmp;
+
+	/*
+	 * Note that because the scalar is at most 4 bytes, we don't care if
+	 * size_t is bigger than usigned
+	 */
+	if (!limread_scalar(&tmp, length, region, stream))
+		return 0;
+
+	*dest = tmp;
+	return 1;
+}
+
+/** Read a timestamp.
+ *
+ * Timestamps in OpenPGP are unix time, i.e. seconds since The Epoch (1.1.1970).  They are stored in an unsigned scalar
+ * of 4 bytes.
+ *
+ * This function reads the timestamp using limread_scalar().
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param *dest		The timestamp is stored here
+ * \param *ptag		Pointer to current packet's Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		see limread_scalar()
+ *
+ * \see RFC4880 3.5
+ */
+static int
+limited_read_time(time_t *dest, pgp_region_t *region,
+		  pgp_stream_t *stream)
+{
+	uint8_t	c;
+	time_t	mytime = 0;
+	int	i;
+
+	/*
+         * Cannot assume that time_t is 4 octets long -
+	 * SunOS 5.10 and NetBSD both have 64-bit time_ts.
+         */
+	if (/* CONSTCOND */sizeof(time_t) == 4) {
+		return limread_scalar((unsigned *)(void *)dest, 4, region, stream);
+	}
+	for (i = 0; i < 4; i++) {
+		if (!limread(&c, 1, region, stream)) {
+			return 0;
+		}
+		mytime = (mytime << 8) + c;
+	}
+	*dest = mytime;
+	return 1;
+}
+
+/**
+ * \ingroup Core_MPI
+ * Read a multiprecision integer.
+ *
+ * Large numbers (multiprecision integers, MPI) are stored in OpenPGP in two parts.  First there is a 2 byte scalar
+ * indicating the length of the following MPI in Bits.  Then follow the bits that make up the actual number, most
+ * significant bits first (Big Endian).  The most significant bit in the MPI is supposed to be 1 (unless the MPI is
+ * encrypted - then it may be different as the bit count refers to the plain text but the bits are encrypted).
+ *
+ * Unused bits (i.e. those filling up the most significant byte from the left to the first bits that counts) are
+ * supposed to be cleared - I guess. XXX - does anything actually say so?
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param **pgn		return the integer there - the BIGNUM is created by BN_bin2bn() and probably needs to be freed
+ * 				by the caller XXX right ben?
+ * \param *ptag		Pointer to current packet's Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
+ * 				 see comment below - the callback is called with a PGP_PARSER_ERROR in case of an error)
+ *
+ * \see RFC4880 3.2
+ */
+static int
+limread_mpi(BIGNUM **pbn, pgp_region_t *region, pgp_stream_t *stream)
+{
+	uint8_t   buf[NETPGP_BUFSIZ] = "";
+					/* an MPI has a 2 byte length part.
+					 * Length is given in bits, so the
+					 * largest we should ever need for
+					 * the buffer is NETPGP_BUFSIZ bytes. */
+	unsigned        length;
+	unsigned        nonzero;
+	unsigned	ret;
+
+	//stream->reading_mpi_len = 1;
+	ret = (unsigned)limread_scalar(&length, 2, region, stream);
+
+	//stream->reading_mpi_len = 0;
+	if (!ret)
+		return 0;
+
+	nonzero = length & 7;	/* there should be this many zero bits in the
+				 * MS byte */
+	if (!nonzero)
+		nonzero = 8;
+	length = (length + 7) / 8;
+
+	if (length == 0) {
+		/* if we try to read a length of 0, then fail */
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "limread_mpi: 0 length\n");
+		}
+		return 0;
+	}
+	if (length > NETPGP_BUFSIZ) {
+		(void) fprintf(stderr, "limread_mpi: bad length\n");
+		return 0;
+	}
+	if (!limread(buf, length, region, stream)) {
+		return 0;
+	}
+	if (((unsigned)buf[0] >> nonzero) != 0 ||
+	    !((unsigned)buf[0] & (1U << (nonzero - 1U)))) {
+		PGP_ERROR_1(&stream->errors, PGP_E_P_MPI_FORMAT_ERROR,
+		    "%s", "MPI Format error");
+		/* XXX: Ben, one part of
+		 * this constraint does
+		 * not apply to
+		 * encrypted MPIs the
+		 * draft says. -- peter */
+		return 0;
+	}
+	*pbn = BN_bin2bn(buf, (int)length, NULL);
+	return 1;
+}
+
+static unsigned read_new_length(unsigned *, pgp_stream_t *);
+
+/* allocate space, read, and stash data away in a virtual pkt */
+static void
+streamread(pgp_stream_t *stream, unsigned c)
+{
+	int	cc;
+    pgp_reader_t *readinfo = &stream->readinfo;
+
+	readinfo->virtualpkt = realloc(readinfo->virtualpkt, readinfo->virtualc + c);
+	cc = readinfo->reader(stream, &readinfo->virtualpkt[readinfo->virtualc],
+		c, &stream->errors, readinfo, &stream->cbinfo);
+	readinfo->virtualc += cc;
+}
+
+/* coalesce all the partial blocks together */
+static int
+coalesce_blocks(pgp_stream_t *stream, unsigned length)
+{
+	unsigned	c;
+    unsigned    r;
+
+    pgp_reader_t *readinfo = &stream->readinfo;
+	readinfo->coalescing = 1;
+
+	/* already read a partial block length - prime the array */
+	streamread(stream, length);
+	while ((r = read_new_length(&c, stream)) && readinfo->partial_read) {
+		/* length we read is partial - add to end of array */
+		streamread(stream, c);
+	}
+	/* not partial - add the last extent to the end of the array */
+	if(r > 0) streamread(stream, c);
+	readinfo->coalescing = 0;
+	return 1;
+}
+
+/** Read some data with a New-Format length from reader.
+ *
+ * \sa Internet-Draft RFC4880.txt Section 4.2.2
+ *
+ * \param *length	Where the decoded length will be put
+ * \param *stream	How to parse
+ * \return		1 if OK, else 0
+ *
+ */
+
+static unsigned
+read_new_length(unsigned *length, pgp_stream_t *stream)
+{
+	uint8_t   c;
+    pgp_reader_t *readinfo = &stream->readinfo;
+
+	readinfo->partial_read = 0;
+	if (base_read(&c, 1, stream) != 1) {
+		return 0;
+	}
+	if (c < 192) {
+		/* 1. One-octet packet */
+		*length = c;
+		return 1;
+	}
+	if (c < 224) {
+		/* 2. Two-octet packet */
+		unsigned        t = (c - 192) << 8;
+
+		if (base_read(&c, 1, stream) != 1) {
+			return 0;
+		}
+		*length = t + c + 192;
+		return 1;
+	}
+	if (c < 255) {
+		/* 3. Partial Body Length */
+		readinfo->partial_read = 1;
+		*length = 1 << (c & 0x1f);
+		if (!readinfo->coalescing) {
+			/* we have been called from coalesce_blocks -
+			 * just return with the partial length */
+			coalesce_blocks(stream, *length);
+			*length = readinfo->virtualc;
+		}
+		return 1;
+	}
+	/* 4. Five-Octet packet */
+	return _read_scalar(length, 4, stream);
+}
+
+/** Read the length information for a new format Packet Tag.
+ *
+ * New style Packet Tags encode the length in one to five octets.  This function reads the right amount of bytes and
+ * decodes it to the proper length information.
+ *
+ * This function makes sure to respect packet boundaries.
+ *
+ * \param *length	return the length here
+ * \param *ptag		Pointer to current packet's Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
+ * 				 see comment below)
+ *
+ * \see RFC4880 4.2.2
+ * \see pgp_ptag_t
+ */
+static int
+limited_read_new_length(unsigned *length, pgp_region_t *region,
+			pgp_stream_t *stream)
+{
+	uint8_t   c = 0x0;
+    pgp_reader_t *readinfo = &stream->readinfo;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	if (c < 192) {
+		*length = c;
+		return 1;
+	}
+	if (c < 224) {
+		unsigned        t = (c - 192) << 8;
+
+		if (!limread(&c, 1, region, stream)) {
+			return 0;
+		}
+		*length = t + c + 192;
+		return 1;
+	}
+	if (c < 255) {
+		readinfo->partial_read = 1;
+		*length = 1 << (c & 0x1f);
+		if (!readinfo->coalescing) {
+			/* we have been called from coalesce_blocks -
+			 * just return with the partial length */
+			coalesce_blocks(stream, *length);
+			*length = readinfo->virtualc;
+		}
+		return 1;
+	}
+	return limread_scalar(length, 4, region, stream);
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+void
+pgp_data_free(pgp_data_t *data)
+{
+	free(data->contents);
+	data->contents = NULL;
+	data->len = 0;
+}
+
+void
+pgp_data_dup(pgp_data_t *dst, const pgp_data_t *src)
+{
+	dst->contents = calloc(1, src->len);
+    memcpy(dst->contents, src->contents, src->len);
+	dst->len = src->len;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+static void
+string_free(char **str)
+{
+	free(*str);
+	*str = NULL;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+/* ! Free packet memory, set pointer to NULL */
+void
+pgp_subpacket_free(pgp_subpacket_t *packet)
+{
+	free(packet->raw);
+	packet->raw = NULL;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+static void
+headers_free(pgp_headers_t *headers)
+{
+	unsigned        n;
+
+	for (n = 0; n < headers->headerc; ++n) {
+		free(headers->headers[n].key);
+		free(headers->headers[n].value);
+	}
+	free(headers->headers);
+	headers->headers = NULL;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+static void
+cleartext_trailer_free(struct pgp_hash_t **trailer)
+{
+	free(*trailer);
+	*trailer = NULL;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+static void
+cmd_get_passphrase_free(pgp_seckey_passphrase_t *skp)
+{
+	if (skp->passphrase && *skp->passphrase) {
+		free(*skp->passphrase);
+		*skp->passphrase = NULL;
+	}
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+static void
+free_BN(BIGNUM **pp)
+{
+	BN_free(*pp);
+	*pp = NULL;
+}
+
+static void
+dup_BN(BIGNUM **dst, const BIGNUM *src)
+{
+	*dst = BN_dup(src);
+}
+
+void
+copy_sig_info(pgp_sig_info_t *dst, const pgp_sig_info_t *src)
+{
+	(void) memcpy(dst, src, sizeof(*src));
+
+	if ((dst->v4_hashed = calloc(1, src->v4_hashlen)) == NULL) {
+		(void) fprintf(stderr, "copy_sig_info: bad alloc\n");
+	} else {
+		(void) memcpy(dst->v4_hashed, src->v4_hashed, src->v4_hashlen);
+	}
+
+	switch (src->key_alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		dup_BN(&dst->sig.rsa.sig, src->sig.rsa.sig);
+		break;
+
+	case PGP_PKA_DSA:
+		dup_BN(&dst->sig.dsa.r, src->sig.dsa.r);
+		dup_BN(&dst->sig.dsa.s, src->sig.dsa.s);
+		break;
+
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		dup_BN(&dst->sig.elgamal.r, src->sig.elgamal.r);
+		dup_BN(&dst->sig.elgamal.s, src->sig.elgamal.s);
+		break;
+
+	case PGP_PKA_PRIVATE00:
+	case PGP_PKA_PRIVATE01:
+	case PGP_PKA_PRIVATE02:
+	case PGP_PKA_PRIVATE03:
+	case PGP_PKA_PRIVATE04:
+	case PGP_PKA_PRIVATE05:
+	case PGP_PKA_PRIVATE06:
+	case PGP_PKA_PRIVATE07:
+	case PGP_PKA_PRIVATE08:
+	case PGP_PKA_PRIVATE09:
+	case PGP_PKA_PRIVATE10:
+		pgp_data_dup(&dst->sig.unknown, &src->sig.unknown);
+		break;
+
+	default:
+		(void) fprintf(stderr, "sig_dup: bad sig type\n");
+	}
+}
+/**
+ * \ingroup Core_Create
+ * \brief Free the memory used when parsing a signature
+ * \param sig
+ */
+void
+pgp_free_sig_info(pgp_sig_info_t *info)
+{
+    free(info->v4_hashed);
+
+	switch (info->key_alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		free_BN(&info->sig.rsa.sig);
+		break;
+
+	case PGP_PKA_DSA:
+		free_BN(&info->sig.dsa.r);
+		free_BN(&info->sig.dsa.s);
+		break;
+
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		free_BN(&info->sig.elgamal.r);
+		free_BN(&info->sig.elgamal.s);
+		break;
+
+	case PGP_PKA_PRIVATE00:
+	case PGP_PKA_PRIVATE01:
+	case PGP_PKA_PRIVATE02:
+	case PGP_PKA_PRIVATE03:
+	case PGP_PKA_PRIVATE04:
+	case PGP_PKA_PRIVATE05:
+	case PGP_PKA_PRIVATE06:
+	case PGP_PKA_PRIVATE07:
+	case PGP_PKA_PRIVATE08:
+	case PGP_PKA_PRIVATE09:
+	case PGP_PKA_PRIVATE10:
+		pgp_data_free(&info->sig.unknown);
+		break;
+
+	default:
+		(void) fprintf(stderr, "info-free: bad info-type\n");
+	}
+    memset(info, 0, sizeof(pgp_sig_info_t));
+}
+
+static void
+sig_free(pgp_sig_t *sig)
+{
+    pgp_free_sig_info(&sig->info);
+}
+
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+/* ! Free any memory allocated when parsing the packet content */
+void
+pgp_parser_content_free(pgp_packet_t *c)
+{
+	switch (c->tag) {
+	case PGP_PARSER_PTAG:
+	case PGP_PTAG_CT_COMPRESSED:
+	case PGP_PTAG_SS_CREATION_TIME:
+	case PGP_PTAG_SS_EXPIRATION_TIME:
+	case PGP_PTAG_SS_KEY_EXPIRY:
+	case PGP_PTAG_SS_TRUST:
+	case PGP_PTAG_SS_ISSUER_KEY_ID:
+	case PGP_PTAG_CT_1_PASS_SIG:
+	case PGP_PTAG_SS_PRIMARY_USER_ID:
+	case PGP_PTAG_SS_REVOCABLE:
+	case PGP_PTAG_SS_REVOCATION_KEY:
+	case PGP_PTAG_CT_LITDATA_HEADER:
+	case PGP_PTAG_CT_LITDATA_BODY:
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
+	case PGP_PTAG_CT_UNARMOURED_TEXT:
+	case PGP_PTAG_CT_ARMOUR_TRAILER:
+	case PGP_PTAG_CT_SIGNATURE_HEADER:
+	case PGP_PTAG_CT_SE_DATA_HEADER:
+	case PGP_PTAG_CT_SE_IP_DATA_HEADER:
+	case PGP_PTAG_CT_SE_IP_DATA_BODY:
+	case PGP_PTAG_CT_MDC:
+	case PGP_GET_SECKEY:
+		break;
+
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
+		headers_free(&c->u.cleartext_head);
+		break;
+
+	case PGP_PTAG_CT_ARMOUR_HEADER:
+		headers_free(&c->u.armour_header.headers);
+		break;
+
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
+		cleartext_trailer_free(&c->u.cleartext_trailer);
+		break;
+
+	case PGP_PTAG_CT_TRUST:
+		pgp_data_free(&c->u.trust);
+		break;
+
+	case PGP_PTAG_CT_SIGNATURE:
+	case PGP_PTAG_CT_SIGNATURE_FOOTER:
+		sig_free(&c->u.sig);
+		break;
+
+	case PGP_PTAG_CT_PUBLIC_KEY:
+	case PGP_PTAG_CT_PUBLIC_SUBKEY:
+		pgp_pubkey_free(&c->u.pubkey);
+		break;
+
+	case PGP_PTAG_CT_USER_ID:
+		pgp_userid_free(&c->u.userid);
+		break;
+
+	case PGP_PTAG_SS_SIGNERS_USER_ID:
+		pgp_userid_free(&c->u.ss_signer);
+		break;
+
+	case PGP_PTAG_CT_USER_ATTR:
+		pgp_data_free(&c->u.userattr);
+		break;
+
+	case PGP_PTAG_SS_PREFERRED_SKA:
+		pgp_data_free(&c->u.ss_skapref);
+		break;
+
+	case PGP_PTAG_SS_PREFERRED_HASH:
+		pgp_data_free(&c->u.ss_hashpref);
+		break;
+
+	case PGP_PTAG_SS_PREF_COMPRESS:
+		pgp_data_free(&c->u.ss_zpref);
+		break;
+
+	case PGP_PTAG_SS_KEY_FLAGS:
+		pgp_data_free(&c->u.ss_key_flags);
+		break;
+
+	case PGP_PTAG_SS_KEYSERV_PREFS:
+		pgp_data_free(&c->u.ss_key_server_prefs);
+		break;
+
+	case PGP_PTAG_SS_FEATURES:
+		pgp_data_free(&c->u.ss_features);
+		break;
+
+	case PGP_PTAG_SS_NOTATION_DATA:
+		pgp_data_free(&c->u.ss_notation.name);
+		pgp_data_free(&c->u.ss_notation.value);
+		break;
+
+	case PGP_PTAG_SS_REGEXP:
+		string_free(&c->u.ss_regexp);
+		break;
+
+	case PGP_PTAG_SS_POLICY_URI:
+		string_free(&c->u.ss_policy);
+		break;
+
+	case PGP_PTAG_SS_PREF_KEYSERV:
+		string_free(&c->u.ss_keyserv);
+		break;
+
+	case PGP_PTAG_SS_USERDEFINED00:
+	case PGP_PTAG_SS_USERDEFINED01:
+	case PGP_PTAG_SS_USERDEFINED02:
+	case PGP_PTAG_SS_USERDEFINED03:
+	case PGP_PTAG_SS_USERDEFINED04:
+	case PGP_PTAG_SS_USERDEFINED05:
+	case PGP_PTAG_SS_USERDEFINED06:
+	case PGP_PTAG_SS_USERDEFINED07:
+	case PGP_PTAG_SS_USERDEFINED08:
+	case PGP_PTAG_SS_USERDEFINED09:
+	case PGP_PTAG_SS_USERDEFINED10:
+		pgp_data_free(&c->u.ss_userdef);
+		break;
+
+	case PGP_PTAG_SS_RESERVED:
+		pgp_data_free(&c->u.ss_unknown);
+		break;
+
+	case PGP_PTAG_SS_REVOCATION_REASON:
+		string_free(&c->u.ss_revocation.reason);
+		break;
+
+	case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
+		pgp_data_free(&c->u.ss_embedded_sig);
+		break;
+
+	case PGP_PARSER_PACKET_END:
+		pgp_subpacket_free(&c->u.packet);
+		break;
+
+	case PGP_PARSER_ERROR:
+	case PGP_PARSER_ERRCODE:
+		break;
+
+	case PGP_PTAG_CT_SECRET_KEY:
+	case PGP_PTAG_CT_ENCRYPTED_SECRET_KEY:
+		pgp_seckey_free(&c->u.seckey);
+		break;
+
+	case PGP_PTAG_CT_PK_SESSION_KEY:
+	case PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
+		pgp_pk_sesskey_free(&c->u.pk_sesskey);
+		break;
+
+	case PGP_GET_PASSPHRASE:
+		cmd_get_passphrase_free(&c->u.skey_passphrase);
+		break;
+
+	default:
+		fprintf(stderr, "Can't free %d (0x%x)\n", c->tag, c->tag);
+	}
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+void
+pgp_pk_sesskey_free(pgp_pk_sesskey_t *sk)
+{
+	switch (sk->alg) {
+	case PGP_PKA_RSA:
+		free_BN(&sk->params.rsa.encrypted_m);
+		break;
+
+	case PGP_PKA_ELGAMAL:
+		free_BN(&sk->params.elgamal.g_to_k);
+		free_BN(&sk->params.elgamal.encrypted_m);
+		break;
+
+	default:
+		(void) fprintf(stderr, "pgp_pk_sesskey_free: bad alg\n");
+		break;
+	}
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+/* ! Free the memory used when parsing a public key */
+void
+pgp_pubkey_free(pgp_pubkey_t *p)
+{
+	switch (p->alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		free_BN(&p->key.rsa.n);
+		free_BN(&p->key.rsa.e);
+		break;
+
+	case PGP_PKA_DSA:
+		free_BN(&p->key.dsa.p);
+		free_BN(&p->key.dsa.q);
+		free_BN(&p->key.dsa.g);
+		free_BN(&p->key.dsa.y);
+		break;
+
+	case PGP_PKA_ELGAMAL:
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		free_BN(&p->key.elgamal.p);
+		free_BN(&p->key.elgamal.g);
+		free_BN(&p->key.elgamal.y);
+		break;
+
+	case PGP_PKA_NOTHING:
+		/* nothing to free */
+		break;
+
+	default:
+		(void) fprintf(stderr, "pgp_pubkey_free: bad alg\n");
+	}
+    memset(p, 0, sizeof(*p));
+}
+
+int
+pgp_pubkey_dup(pgp_pubkey_t *dst, pgp_pubkey_t *src)
+{
+    memcpy(dst, src, sizeof(*src));
+
+	switch (src->alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		dup_BN(&dst->key.rsa.n, src->key.rsa.n);
+		dup_BN(&dst->key.rsa.e, src->key.rsa.e);
+		break;
+
+	case PGP_PKA_DSA:
+		dup_BN(&dst->key.dsa.p, src->key.dsa.p);
+		dup_BN(&dst->key.dsa.q, src->key.dsa.q);
+		dup_BN(&dst->key.dsa.g, src->key.dsa.g);
+		dup_BN(&dst->key.dsa.y, src->key.dsa.y);
+		break;
+
+	case PGP_PKA_ELGAMAL:
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		dup_BN(&dst->key.elgamal.p, src->key.elgamal.p);
+		dup_BN(&dst->key.elgamal.g, src->key.elgamal.g);
+		dup_BN(&dst->key.elgamal.y, src->key.elgamal.y);
+		break;
+
+	case PGP_PKA_NOTHING:
+		/* nothing to dup */
+		break;
+
+	default:
+		(void) fprintf(stderr, "pgp_pubkey_dup: bad alg\n");
+        return 0;
+	}
+
+    /*TODO alloc error handling */
+    return 1;
+}
+/**
+   \ingroup Core_ReadPackets
+*/
+static int
+parse_pubkey_data(pgp_pubkey_t *key, pgp_region_t *region,
+		      pgp_stream_t *stream)
+{
+	uint8_t   c = 0x0;
+
+	if (region->readc != 0) {
+		/* We should not have read anything so far */
+		(void) fprintf(stderr, "parse_pubkey_data: bad length\n");
+		return 0;
+	}
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	key->version = (pgp_version_t)c;
+	switch (key->version) {
+	case PGP_V2:
+	case PGP_V3:
+	case PGP_V4:
+		break;
+	default:
+		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_PUBLIC_KEY_VRSN,
+			    "Bad public key version (0x%02x)", key->version);
+		return 0;
+	}
+	if (!limited_read_time(&key->birthtime, region, stream)) {
+		return 0;
+	}
+
+	key->days_valid = 0;
+	if ((key->version == 2 || key->version == 3) &&
+	    !limread_scalar(&key->days_valid, 2, region, stream)) {
+		return 0;
+	}
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	key->alg = c;
+
+	switch (key->alg) {
+	case PGP_PKA_DSA:
+		if (!limread_mpi(&key->key.dsa.p, region, stream) ||
+		    !limread_mpi(&key->key.dsa.q, region, stream) ||
+		    !limread_mpi(&key->key.dsa.g, region, stream) ||
+		    !limread_mpi(&key->key.dsa.y, region, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!limread_mpi(&key->key.rsa.n, region, stream) ||
+		    !limread_mpi(&key->key.rsa.e, region, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_ELGAMAL:
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		if (!limread_mpi(&key->key.elgamal.p, region, stream) ||
+		    !limread_mpi(&key->key.elgamal.g, region, stream) ||
+		    !limread_mpi(&key->key.elgamal.y, region, stream)) {
+			return 0;
+		}
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
+			"Unsupported Public Key algorithm (%s)",
+			pgp_show_pka(key->alg));
+		return 0;
+	}
+
+	return 1;
+}
+
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse a public key packet.
+ *
+ * This function parses an entire v3 (== v2) or v4 public key packet for RSA, ElGamal, and DSA keys.
+ *
+ * Once the key has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the current Packet Tag.  This function should consume the entire packet.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.5.2
+ */
+static int
+parse_pubkey(pgp_content_enum tag, pgp_region_t *region,
+		 pgp_stream_t *stream)
+{
+	pgp_packet_t pkt;
+
+	if (!parse_pubkey_data(&pkt.u.pubkey, region, stream)) {
+		(void) fprintf(stderr, "parse_pubkey: parse_pubkey_data failed\n");
+		return 0;
+	}
+
+	/* XXX: this test should be done for all packets, surely? */
+	if (region->readc != region->length) {
+		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
+			    "Unconsumed data (%d)", region->length - region->readc);
+		return 0;
+	}
+	CALLBACK(tag, &stream->cbinfo, &pkt);
+
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse one user attribute packet.
+ *
+ * User attribute packets contain one or more attribute subpackets.
+ * For now, handle the whole packet as raw data.
+ */
+
+static int
+parse_userattr(pgp_region_t *region, pgp_stream_t *stream)
+{
+
+	pgp_packet_t pkt;
+
+	/*
+	 * xxx- treat as raw data for now. Could break down further into
+	 * attribute sub-packets later - rachel
+	 */
+	if (region->readc != 0) {
+		/* We should not have read anything so far */
+		(void) fprintf(stderr, "parse_userattr: bad length\n");
+		return 0;
+	}
+	if (!read_data(&pkt.u.userattr, region, stream)) {
+		return 0;
+	}
+	CALLBACK(PGP_PTAG_CT_USER_ATTR, &stream->cbinfo, &pkt);
+	return 1;
+}
+
+/**
+\ingroup Core_Create
+\brief Free allocated memory
+*/
+/* ! Free the memory used when parsing this packet type */
+void
+pgp_userid_free(uint8_t **id)
+{
+	free(*id);
+	*id = NULL;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse a user id.
+ *
+ * This function parses an user id packet, which is basically just a char array the size of the packet.
+ *
+ * The char array is to be treated as an UTF-8 string.
+ *
+ * The userid gets null terminated by this function.  Freeing it is the responsibility of the caller.
+ *
+ * Once the userid has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire packet.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.11
+ */
+static int
+parse_userid(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t pkt;
+
+	 if (region->readc != 0) {
+		/* We should not have read anything so far */
+		(void) fprintf(stderr, "parse_userid: bad length\n");
+		return 0;
+	}
+
+	if ((pkt.u.userid = calloc(1, region->length + 1)) == NULL) {
+		(void) fprintf(stderr, "parse_userid: bad alloc\n");
+		return 0;
+	}
+
+	if (region->length &&
+	    !limread(pkt.u.userid, region->length, region,
+			stream)) {
+		return 0;
+	}
+	pkt.u.userid[region->length] = 0x0;
+	CALLBACK(PGP_PTAG_CT_USER_ID, &stream->cbinfo, &pkt);
+	return 1;
+}
+
+static pgp_hash_t     *
+parse_hash_find(pgp_stream_t *stream, const uint8_t *keyid)
+{
+	pgp_hashtype_t	*hp;
+	size_t			 n;
+
+	for (n = 0, hp = stream->hashes; n < stream->hashc; n++, hp++) {
+		if (memcmp(hp->keyid, keyid, PGP_KEY_ID_SIZE) == 0) {
+			return &hp->hash;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * \ingroup Core_Parse
+ * \brief Parse a version 3 signature.
+ *
+ * This function parses an version 3 signature packet, handling RSA and DSA signatures.
+ *
+ * Once the signature has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire packet.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.2.2
+ */
+static int
+parse_v3_sig(pgp_region_t *region,
+		   pgp_stream_t *stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	/* clear signature */
+	(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));
+
+	pkt.u.sig.info.version = PGP_V3;
+
+	/* hash info length */
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	if (c != 5) {
+		ERRP(&stream->cbinfo, pkt, "bad hash info length");
+	}
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.type = (pgp_sig_type_t)c;
+	/* XXX: check signature type */
+
+	if (!limited_read_time(&pkt.u.sig.info.birthtime, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.birthtime_set = 1;
+
+	if (!limread(pkt.u.sig.info.signer_id, PGP_KEY_ID_SIZE, region,
+			stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.signer_id_set = 1;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
+	/* XXX: check algorithm */
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
+	/* XXX: check algorithm */
+
+	if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
+		return 0;
+	}
+
+	switch (pkt.u.sig.info.key_alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_DSA:
+		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream) ||
+		    !limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
+				stream) ||
+		    !limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
+		    		stream)) {
+			return 0;
+		}
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
+			"Unsupported signature key algorithm (%s)",
+			pgp_show_pka(pkt.u.sig.info.key_alg));
+		return 0;
+	}
+
+	if (region->readc != region->length) {
+		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
+			"Unconsumed data (%d)",
+			region->length - region->readc);
+		return 0;
+	}
+	if (pkt.u.sig.info.signer_id_set) {
+		pkt.u.sig.hash = parse_hash_find(stream,
+				pkt.u.sig.info.signer_id);
+	}
+	CALLBACK(PGP_PTAG_CT_SIGNATURE, &stream->cbinfo, &pkt);
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse one signature sub-packet.
+ *
+ * Version 4 signatures can have an arbitrary amount of (hashed and
+ * unhashed) subpackets.  Subpackets are used to hold optional
+ * attributes of subpackets.
+ *
+ * This function parses one such signature subpacket.
+ *
+ * Once the subpacket has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire subpacket.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.2.3
+ */
+static int
+parse_one_sig_subpacket(pgp_sig_t *sig,
+			      pgp_region_t *region,
+			      pgp_stream_t *stream)
+{
+	pgp_region_t	subregion;
+	pgp_packet_t	pkt;
+	uint8_t		bools = 0x0;
+	uint8_t		c = 0x0;
+	unsigned	doread = 1;
+	unsigned        t8;
+	unsigned        t7;
+
+	pgp_init_subregion(&subregion, region);
+	if (!limited_read_new_length(&subregion.length, region, stream)) {
+		return 0;
+	}
+
+	if (subregion.length > region->length) {
+		ERRP(&stream->cbinfo, pkt, "Subpacket too long");
+	}
+
+	if (!limread(&c, 1, &subregion, stream)) {
+		return 0;
+	}
+
+	t8 = (c & 0x7f) / 8;
+	t7 = 1 << (c & 7);
+
+	pkt.critical = (unsigned)c >> 7;
+	pkt.tag = (pgp_content_enum)(PGP_PTAG_SIG_SUBPKT_BASE + (c & 0x7f));
+
+	/* Application wants it delivered raw */
+	if (stream->ss_raw[t8] & t7) {
+		pkt.u.ss_raw.tag = pkt.tag;
+		pkt.u.ss_raw.length = subregion.length - 1;
+		pkt.u.ss_raw.raw = calloc(1, pkt.u.ss_raw.length);
+		if (pkt.u.ss_raw.raw == NULL) {
+			(void) fprintf(stderr, "parse_one_sig_subpacket: bad alloc\n");
+			return 0;
+		}
+		if (!limread(pkt.u.ss_raw.raw, (unsigned)pkt.u.ss_raw.length,
+				&subregion, stream)) {
+			return 0;
+		}
+		CALLBACK(PGP_PTAG_RAW_SS, &stream->cbinfo, &pkt);
+		return 1;
+	}
+	switch (pkt.tag) {
+	case PGP_PTAG_SS_CREATION_TIME:
+	case PGP_PTAG_SS_EXPIRATION_TIME:
+	case PGP_PTAG_SS_KEY_EXPIRY:
+		if (!limited_read_time(&pkt.u.ss_time, &subregion, stream))
+			return 0;
+		if (pkt.tag == PGP_PTAG_SS_CREATION_TIME) {
+			sig->info.birthtime = pkt.u.ss_time;
+			sig->info.birthtime_set = 1;
+		}
+		if (pkt.tag == PGP_PTAG_SS_EXPIRATION_TIME) {
+			sig->info.duration = pkt.u.ss_time;
+			sig->info.duration_set = 1;
+		}
+		if (pkt.tag == PGP_PTAG_SS_KEY_EXPIRY) {
+			sig->info.key_expiry = pkt.u.ss_time;
+			sig->info.key_expiry_set = 1;
+		}
+		break;
+
+	case PGP_PTAG_SS_TRUST:
+		if (!limread(&pkt.u.ss_trust.level, 1, &subregion, stream) ||
+		    !limread(&pkt.u.ss_trust.amount, 1, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_REVOCABLE:
+		if (!limread(&bools, 1, &subregion, stream)) {
+			return 0;
+		}
+		pkt.u.ss_revocable = !!bools;
+		break;
+
+	case PGP_PTAG_SS_ISSUER_KEY_ID:
+		if (!limread(pkt.u.ss_issuer, PGP_KEY_ID_SIZE, &subregion, stream)) {
+			return 0;
+		}
+		(void) memcpy(sig->info.signer_id, pkt.u.ss_issuer, PGP_KEY_ID_SIZE);
+		sig->info.signer_id_set = 1;
+		break;
+
+	case PGP_PTAG_SS_PREFERRED_SKA:
+		if (!read_data(&pkt.u.ss_skapref, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_PREFERRED_HASH:
+		if (!read_data(&pkt.u.ss_hashpref, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_PREF_COMPRESS:
+		if (!read_data(&pkt.u.ss_zpref, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_PRIMARY_USER_ID:
+		if (!limread(&bools, 1, &subregion, stream)) {
+			return 0;
+		}
+		pkt.u.ss_primary_userid = !!bools;
+		sig->info.primary_userid = pkt.u.ss_primary_userid;
+		break;
+
+	case PGP_PTAG_SS_KEY_FLAGS:
+		if (!read_data(&pkt.u.ss_key_flags, &subregion, stream)) {
+			return 0;
+		}
+		if(pkt.u.ss_key_flags.len > 0){
+            /* Only one byte is defined in rfc4880 for now */
+            sig->info.key_flags = pkt.u.ss_key_flags.contents[0];
+            sig->info.key_flags_set = 1;
+        }
+		break;
+
+	case PGP_PTAG_SS_KEYSERV_PREFS:
+		if (!read_data(&pkt.u.ss_key_server_prefs, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_FEATURES:
+		if (!read_data(&pkt.u.ss_features, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_SIGNERS_USER_ID:
+		if (!read_unsig_str(&pkt.u.ss_signer, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
+		/* \todo should do something with this sig? */
+		if (!read_data(&pkt.u.ss_embedded_sig, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_NOTATION_DATA:
+		if (!limread_data(&pkt.u.ss_notation.flags, 4,
+				&subregion, stream)) {
+			return 0;
+		}
+		if (!limread_size_t(&pkt.u.ss_notation.name.len, 2,
+				&subregion, stream)) {
+			return 0;
+		}
+		if (!limread_size_t(&pkt.u.ss_notation.value.len, 2,
+				&subregion, stream)) {
+			return 0;
+		}
+		if (!limread_data(&pkt.u.ss_notation.name,
+				(unsigned)pkt.u.ss_notation.name.len,
+				&subregion, stream)) {
+			return 0;
+		}
+		if (!limread_data(&pkt.u.ss_notation.value,
+			   (unsigned)pkt.u.ss_notation.value.len,
+			   &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_POLICY_URI:
+		if (!read_string(&pkt.u.ss_policy, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_REGEXP:
+		if (!read_string(&pkt.u.ss_regexp, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_PREF_KEYSERV:
+		if (!read_string(&pkt.u.ss_keyserv, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_USERDEFINED00:
+	case PGP_PTAG_SS_USERDEFINED01:
+	case PGP_PTAG_SS_USERDEFINED02:
+	case PGP_PTAG_SS_USERDEFINED03:
+	case PGP_PTAG_SS_USERDEFINED04:
+	case PGP_PTAG_SS_USERDEFINED05:
+	case PGP_PTAG_SS_USERDEFINED06:
+	case PGP_PTAG_SS_USERDEFINED07:
+	case PGP_PTAG_SS_USERDEFINED08:
+	case PGP_PTAG_SS_USERDEFINED09:
+	case PGP_PTAG_SS_USERDEFINED10:
+		if (!read_data(&pkt.u.ss_userdef, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_RESERVED:
+		if (!read_data(&pkt.u.ss_unknown, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_REVOCATION_REASON:
+		/* first byte is the machine-readable code */
+		if (!limread(&pkt.u.ss_revocation.code, 1, &subregion, stream)) {
+			return 0;
+		}
+		/* the rest is a human-readable UTF-8 string */
+		if (!read_string(&pkt.u.ss_revocation.reason, &subregion,
+				stream)) {
+			return 0;
+		}
+		break;
+
+	case PGP_PTAG_SS_REVOCATION_KEY:
+		/* octet 0 = class. Bit 0x80 must be set */
+		if (!limread(&pkt.u.ss_revocation_key.class, 1,
+				&subregion, stream)) {
+			return 0;
+		}
+		if (!(pkt.u.ss_revocation_key.class & 0x80)) {
+			printf("Warning: PGP_PTAG_SS_REVOCATION_KEY class: "
+			       "Bit 0x80 should be set\n");
+			return 0;
+		}
+		/* octet 1 = algid */
+		if (!limread(&pkt.u.ss_revocation_key.algid, 1,
+				&subregion, stream)) {
+			return 0;
+		}
+		/* octets 2-21 = fingerprint */
+		if (!limread(&pkt.u.ss_revocation_key.fingerprint[0],
+				PGP_FINGERPRINT_SIZE, &subregion, stream)) {
+			return 0;
+		}
+		break;
+
+	default:
+		if (stream->ss_parsed[t8] & t7) {
+			PGP_ERROR_1(&stream->errors, PGP_E_PROTO_UNKNOWN_SS,
+				    "Unknown signature subpacket type (%d)",
+				    c & 0x7f);
+		}
+		doread = 0;
+		break;
+	}
+
+	/* Application doesn't want it delivered parsed */
+	if (!(stream->ss_parsed[t8] & t7)) {
+		if (pkt.critical) {
+			PGP_ERROR_1(&stream->errors,
+				PGP_E_PROTO_CRITICAL_SS_IGNORED,
+				"Critical signature subpacket ignored (%d)",
+				c & 0x7f);
+		}
+		if (!doread &&
+		    !limskip(subregion.length - 1, &subregion, stream)) {
+			return 0;
+		}
+		if (doread) {
+			pgp_parser_content_free(&pkt);
+		}
+		return 1;
+	}
+	if (doread && subregion.readc != subregion.length) {
+		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
+			    "Unconsumed data (%d)",
+			    subregion.length - subregion.readc);
+        pgp_parser_content_free(&pkt);
+		return 0;
+	}
+	CALLBACK(pkt.tag, &stream->cbinfo, &pkt);
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse several signature subpackets.
+ *
+ * Hashed and unhashed subpacket sets are preceded by an octet count that specifies the length of the complete set.
+ * This function parses this length and then calls parse_one_sig_subpacket() for each subpacket until the
+ * entire set is consumed.
+ *
+ * This function does not call the callback directly, parse_one_sig_subpacket() does for each subpacket.
+ *
+ * \param *ptag		Pointer to the Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.2.3
+ */
+static int
+parse_sig_subpkts(pgp_sig_t *sig,
+			   pgp_region_t *region,
+			   pgp_stream_t *stream)
+{
+	pgp_region_t	subregion;
+	pgp_packet_t	pkt;
+
+	pgp_init_subregion(&subregion, region);
+	if (!limread_scalar(&subregion.length, 2, region, stream)) {
+		return 0;
+	}
+
+	if (subregion.length > region->length) {
+		ERRP(&stream->cbinfo, pkt, "Subpacket set too long");
+	}
+
+	while (subregion.readc < subregion.length) {
+		if (!parse_one_sig_subpacket(sig, &subregion, stream)) {
+			return 0;
+		}
+	}
+
+	if (subregion.readc != subregion.length) {
+		if (!limskip(subregion.length - subregion.readc,
+				&subregion, stream)) {
+			ERRP(&stream->cbinfo, pkt,
+"parse_sig_subpkts: subpacket length read mismatch");
+		}
+		ERRP(&stream->cbinfo, pkt, "Subpacket length mismatch");
+	}
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse a version 4 signature.
+ *
+ * This function parses a version 4 signature including all its hashed and unhashed subpackets.
+ *
+ * Once the signature packet has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ *
+ * \see RFC4880 5.2.3
+ */
+static int
+parse_v4_sig(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "\nparse_v4_sig\n");
+	}
+	/* clear signature */
+	(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));
+
+	/*
+	 * We need to hash the packet data from version through the hashed
+	 * subpacket data
+	 */
+
+	pkt.u.sig.v4_hashstart = stream->readinfo.alength - 1;
+
+	/* Set version,type,algorithms */
+
+	pkt.u.sig.info.version = PGP_V4;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.type = (pgp_sig_type_t)c;
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "signature type=%d (%s)\n",
+			pkt.u.sig.info.type,
+			pgp_show_sig_type(pkt.u.sig.info.type));
+	}
+	/* XXX: check signature type */
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
+	/* XXX: check key algorithm */
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "key_alg=%d (%s)\n",
+			pkt.u.sig.info.key_alg,
+			pgp_show_pka(pkt.u.sig.info.key_alg));
+	}
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
+	/* XXX: check hash algorithm */
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "hash_alg=%d %s\n",
+			pkt.u.sig.info.hash_alg,
+		  pgp_show_hash_alg(pkt.u.sig.info.hash_alg));
+	}
+	CALLBACK(PGP_PTAG_CT_SIGNATURE_HEADER, &stream->cbinfo, &pkt);
+
+	if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
+		return 0;
+	}
+
+	pkt.u.sig.info.v4_hashlen = stream->readinfo.alength
+					- pkt.u.sig.v4_hashstart;
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "v4_hashlen=%zd\n", pkt.u.sig.info.v4_hashlen);
+	}
+
+	/* copy hashed subpackets */
+	if (pkt.u.sig.info.v4_hashed) {
+		free(pkt.u.sig.info.v4_hashed);
+	}
+	pkt.u.sig.info.v4_hashed = calloc(1, pkt.u.sig.info.v4_hashlen);
+	if (pkt.u.sig.info.v4_hashed == NULL) {
+		(void) fprintf(stderr, "parse_v4_sig: bad alloc\n");
+		return 0;
+	}
+
+	if (!stream->readinfo.accumulate) {
+		/* We must accumulate, else we can't check the signature */
+		fprintf(stderr, "*** ERROR: must set accumulate to 1\n");
+        goto error_unalloc_v4_hashed;
+	}
+	(void) memcpy(pkt.u.sig.info.v4_hashed,
+	       stream->readinfo.accumulated + pkt.u.sig.v4_hashstart,
+	       pkt.u.sig.info.v4_hashlen);
+
+	if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
+        goto error_unalloc_v4_hashed;
+	}
+
+	if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
+        goto error_unalloc_v4_hashed;
+	}
+
+	switch (pkt.u.sig.info.key_alg) {
+	case PGP_PKA_RSA:
+		if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
+            goto error_unalloc_v4_hashed;
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "parse_v4_sig: RSA: sig is\n");
+			BN_print_fp(stderr, pkt.u.sig.info.sig.rsa.sig);
+			(void) fprintf(stderr, "\n");
+		}
+		break;
+
+	case PGP_PKA_DSA:
+		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream)) {
+			/*
+			 * usually if this fails, it just means we've reached
+			 * the end of the keyring
+			 */
+			if (pgp_get_debug_level(__FILE__)) {
+				(void) fprintf(stderr,
+				"Error reading DSA r field in signature");
+			}
+            goto error_unalloc_v4_hashed;
+		}
+		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
+			ERRP(&stream->cbinfo, pkt,
+			"Error reading DSA s field in signature");
+		}
+		break;
+
+	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
+		if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
+				stream) ||
+		    !limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
+		    		stream)) {
+                goto error_unalloc_v4_hashed;
+		}
+		break;
+
+	case PGP_PKA_PRIVATE00:
+	case PGP_PKA_PRIVATE01:
+	case PGP_PKA_PRIVATE02:
+	case PGP_PKA_PRIVATE03:
+	case PGP_PKA_PRIVATE04:
+	case PGP_PKA_PRIVATE05:
+	case PGP_PKA_PRIVATE06:
+	case PGP_PKA_PRIVATE07:
+	case PGP_PKA_PRIVATE08:
+	case PGP_PKA_PRIVATE09:
+	case PGP_PKA_PRIVATE10:
+		if (!read_data(&pkt.u.sig.info.sig.unknown, region, stream)) {
+            goto error_unalloc_v4_hashed;
+		}
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors, PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
+			    "Bad v4 signature key algorithm (%s)",
+			    pgp_show_pka(pkt.u.sig.info.key_alg));
+            goto error_unalloc_v4_hashed;
+	}
+	if (region->readc != region->length) {
+		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
+			    "Unconsumed data (%d)",
+			    region->length - region->readc);
+        goto error_unalloc_v4_hashed;
+	}
+	CALLBACK(PGP_PTAG_CT_SIGNATURE_FOOTER, &stream->cbinfo, &pkt);
+	return 1;
+
+error_unalloc_v4_hashed:
+    free(pkt.u.sig.info.v4_hashed);
+    return 0;
+
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse a signature subpacket.
+ *
+ * This function calls the appropriate function to handle v3 or v4 signatures.
+ *
+ * Once the signature packet has been parsed successfully, it is passed to the callback.
+ *
+ * \param *ptag		Pointer to the Packet Tag.
+ * \param *reader	Our reader
+ * \param *cb		The callback
+ * \return		1 on success, 0 on error
+ */
+static int
+parse_sig(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	if (region->readc != 0) {
+		/* We should not have read anything so far */
+		(void) fprintf(stderr, "parse_sig: bad length\n");
+		return 0;
+	}
+
+	(void) memset(&pkt, 0x0, sizeof(pkt));
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	if (c == 2 || c == 3) {
+		return parse_v3_sig(region, stream);
+	}
+	if (c == 4) {
+		return parse_v4_sig(region, stream);
+	}
+	PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_SIGNATURE_VRSN,
+		    "Bad signature version (%d)", c);
+	return 0;
+}
+
+/**
+ \ingroup Core_ReadPackets
+ \brief Parse Compressed packet
+*/
+static int
+parse_compressed(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+
+	pkt.u.compressed = (pgp_compression_type_t)c;
+
+	CALLBACK(PGP_PTAG_CT_COMPRESSED, &stream->cbinfo, &pkt);
+
+	/*
+	 * The content of a compressed data packet is more OpenPGP packets
+	 * once decompressed, so recursively handle them
+	 */
+
+	return pgp_decompress(region, stream, pkt.u.compressed);
+}
+
+/* XXX: this could be improved by sharing all hashes that are the */
+/* same, then duping them just before checking the signature. */
+static void
+parse_hash_init(pgp_stream_t *stream, pgp_hash_alg_t type,
+		    const uint8_t *keyid)
+{
+	pgp_hashtype_t *hash;
+
+	hash = realloc(stream->hashes,
+			      (stream->hashc + 1) * sizeof(*stream->hashes));
+	if (hash == NULL) {
+		(void) fprintf(stderr, "parse_hash_init: bad alloc 0\n");
+		/* just continue and die here */
+		/* XXX - agc - no way to return failure */
+	} else {
+		stream->hashes = hash;
+	}
+	hash = &stream->hashes[stream->hashc++];
+
+	pgp_hash_any(&hash->hash, type);
+	if (!hash->hash.init(&hash->hash)) {
+		(void) fprintf(stderr, "parse_hash_init: bad alloc\n");
+		/* just continue and die here */
+		/* XXX - agc - no way to return failure */
+	}
+	(void) memcpy(hash->keyid, keyid, sizeof(hash->keyid));
+}
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Parse a One Pass Signature packet
+*/
+static int
+parse_one_pass(pgp_region_t * region, pgp_stream_t * stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	if (!limread(&pkt.u.one_pass_sig.version, 1, region, stream)) {
+		return 0;
+	}
+	if (pkt.u.one_pass_sig.version != 3) {
+		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_ONE_PASS_SIG_VRSN,
+			    "Bad one-pass signature version (%d)",
+			    pkt.u.one_pass_sig.version);
+		return 0;
+	}
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.one_pass_sig.sig_type = (pgp_sig_type_t)c;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.one_pass_sig.hash_alg = (pgp_hash_alg_t)c;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.one_pass_sig.key_alg = (pgp_pubkey_alg_t)c;
+
+	if (!limread(pkt.u.one_pass_sig.keyid,
+			  (unsigned)sizeof(pkt.u.one_pass_sig.keyid),
+			  region, stream)) {
+		return 0;
+	}
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.one_pass_sig.nested = !!c;
+	CALLBACK(PGP_PTAG_CT_1_PASS_SIG, &stream->cbinfo, &pkt);
+	/* XXX: we should, perhaps, let the app choose whether to hash or not */
+	parse_hash_init(stream, pkt.u.one_pass_sig.hash_alg,
+			    pkt.u.one_pass_sig.keyid);
+	return 1;
+}
+
+/**
+ \ingroup Core_ReadPackets
+ \brief Parse a Trust packet
+*/
+static int
+parse_trust(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t pkt;
+
+	if (!read_data(&pkt.u.trust, region, stream)) {
+		return 0;
+	}
+	CALLBACK(PGP_PTAG_CT_TRUST, &stream->cbinfo, &pkt);
+	return 1;
+}
+
+static void
+parse_hash_data(pgp_stream_t *stream, const void *data,
+		    size_t length)
+{
+	size_t          n;
+
+	for (n = 0; n < stream->hashc; ++n) {
+		stream->hashes[n].hash.add(&stream->hashes[n].hash, data, (unsigned)length);
+	}
+}
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Parse a Literal Data packet
+*/
+static int
+parse_litdata(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_memory_t	*mem;
+	pgp_packet_t	 pkt;
+	uint8_t		 c = 0x0;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.litdata_header.format = (pgp_litdata_enum)c;
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	if (!limread((uint8_t *)pkt.u.litdata_header.filename,
+			(unsigned)c, region, stream)) {
+		return 0;
+	}
+	pkt.u.litdata_header.filename[c] = '\0';
+	if (!limited_read_time(&pkt.u.litdata_header.mtime, region, stream)) {
+		return 0;
+	}
+	CALLBACK(PGP_PTAG_CT_LITDATA_HEADER, &stream->cbinfo, &pkt);
+	mem = pkt.u.litdata_body.mem = pgp_memory_new();
+	pgp_memory_init(pkt.u.litdata_body.mem,
+			(unsigned)((region->length * 101) / 100) + 12);
+	pkt.u.litdata_body.data = mem->buf;
+
+	while (region->readc < region->length) {
+		unsigned        readc = region->length - region->readc;
+
+		if (!limread(mem->buf, readc, region, stream)) {
+			return 0;
+		}
+		pkt.u.litdata_body.length = readc;
+		parse_hash_data(stream, pkt.u.litdata_body.data, region->length);
+		CALLBACK(PGP_PTAG_CT_LITDATA_BODY, &stream->cbinfo, &pkt);
+	}
+
+	/* XXX - get rid of mem here? */
+
+	return 1;
+}
+
+/**
+ * \ingroup Core_Create
+ *
+ * pgp_seckey_free() frees the memory associated with "key". Note that
+ * the key itself is not freed.
+ *
+ * \param key
+ */
+
+void
+pgp_seckey_free(pgp_seckey_t *key)
+{
+	switch (key->pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		free_BN(&key->key.rsa.d);
+		free_BN(&key->key.rsa.p);
+		free_BN(&key->key.rsa.q);
+		free_BN(&key->key.rsa.u);
+		break;
+
+	case PGP_PKA_DSA:
+		free_BN(&key->key.dsa.x);
+		break;
+
+	default:
+		(void) fprintf(stderr,
+			"pgp_seckey_free: Unknown algorithm: %d (%s)\n",
+			key->pubkey.alg,
+			pgp_show_pka(key->pubkey.alg));
+	}
+	free(key->checkhash);
+    pgp_pubkey_free(&key->pubkey);
+    memset(key, 0, sizeof(*key));
+}
+
+int
+pgp_seckey_dup(pgp_seckey_t *dst, pgp_seckey_t *src)
+{
+    memcpy(dst, src, sizeof(*src));
+
+	switch (src->pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		dup_BN(&dst->key.rsa.d, src->key.rsa.d);
+		dup_BN(&dst->key.rsa.p, src->key.rsa.p);
+		dup_BN(&dst->key.rsa.q, src->key.rsa.q);
+		dup_BN(&dst->key.rsa.u, src->key.rsa.u);
+		break;
+
+	case PGP_PKA_DSA:
+		dup_BN(&dst->key.dsa.x, src->key.dsa.x);
+		break;
+
+	default:
+		(void) fprintf(stderr,
+			"pgp_seckey_dup: Unknown algorithm: %d (%s)\n",
+			src->pubkey.alg,
+			pgp_show_pka(src->pubkey.alg));
+        return 0;
+	}
+
+    if(src->checkhash) {
+        if ((dst->checkhash = calloc(1, PGP_CHECKHASH_SIZE)) == NULL) {
+            (void) fprintf(stderr, "pgp_seckey_dup: bad alloc\n");
+            return 0;
+        }else{
+            memcpy(dst->checkhash, src->checkhash, PGP_CHECKHASH_SIZE);
+        }
+    }
+
+    pgp_pubkey_dup(&dst->pubkey, &src->pubkey);
+
+    /*TODO alloc error handling */
+    return 1;
+}
+
+static int
+consume_packet(pgp_region_t *region, pgp_stream_t *stream, unsigned warn)
+{
+	pgp_packet_t	pkt;
+	pgp_data_t	remainder;
+
+	if (region->indeterminate) {
+		ERRP(&stream->cbinfo, pkt,
+			"Can't consume indeterminate packets");
+	}
+
+	if (read_data(&remainder, region, stream)) {
+		/* now throw it away */
+		pgp_data_free(&remainder);
+		if (warn) {
+			PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_CONSUMED,
+			    "%s", "Warning: packet consumer");
+		}
+		return 1;
+	}
+	PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_NOT_CONSUMED,
+	    "%s", (warn) ? "Warning: Packet was not consumed" :
+	    "Packet was not consumed");
+	return warn;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse a secret key
+ */
+static int
+parse_seckey(pgp_content_enum tag, pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t		pkt;
+	pgp_region_t		encregion;
+	pgp_region_t	       *saved_region = NULL;
+	pgp_crypt_t		decrypt;
+	pgp_hash_t		checkhash;
+	unsigned		blocksize;
+	unsigned		crypted;
+	uint8_t			c = 0x0;
+	int			ret = 1;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "\n---------\nparse_seckey:\n");
+		fprintf(stderr,
+			"region length=%u, readc=%u, remainder=%u\n",
+			region->length, region->readc,
+			region->length - region->readc);
+	}
+	(void) memset(&pkt, 0x0, sizeof(pkt));
+	if (!parse_pubkey_data(&pkt.u.seckey.pubkey, region, stream)) {
+		return 0;
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "parse_seckey: public key parsed\n");
+		//pgp_print_pubkey(&pkt.u.seckey.pubkey);
+	}
+	//stream->reading_v3_secret = (pkt.u.seckey.pubkey.version != PGP_V4);
+    if (pkt.u.seckey.pubkey.version != PGP_V4){
+        (void) fprintf(stderr,
+                       "parse_seckey: Only V4 is supported\n");
+        return 0;
+    }
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.seckey.s2k_usage = (pgp_s2k_usage_t)c;
+
+	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
+	    pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
+		if (!limread(&c, 1, region, stream)) {
+			return 0;
+		}
+		pkt.u.seckey.alg = (pgp_symm_alg_t)c;
+		if (!limread(&c, 1, region, stream)) {
+			return 0;
+		}
+		pkt.u.seckey.s2k_specifier = (pgp_s2k_specifier_t)c;
+		switch (pkt.u.seckey.s2k_specifier) {
+		case PGP_S2KS_SIMPLE:
+		case PGP_S2KS_SALTED:
+		case PGP_S2KS_ITERATED_AND_SALTED:
+			break;
+		default:
+			(void) fprintf(stderr,
+				"parse_seckey: bad seckey\n");
+			return 0;
+		}
+		if (!limread(&c, 1, region, stream)) {
+			return 0;
+		}
+		pkt.u.seckey.hash_alg = (pgp_hash_alg_t)c;
+		if (pkt.u.seckey.s2k_specifier != PGP_S2KS_SIMPLE &&
+		    !limread(pkt.u.seckey.salt, 8, region, stream)) {
+			return 0;
+		}
+		if (pkt.u.seckey.s2k_specifier ==
+					PGP_S2KS_ITERATED_AND_SALTED) {
+			if (!limread(&c, 1, region, stream)) {
+				return 0;
+			}
+			pkt.u.seckey.octetc =
+				(16 + ((unsigned)c & 15)) <<
+						(((unsigned)c >> 4) + 6);
+		}
+	} else if (pkt.u.seckey.s2k_usage != PGP_S2KU_NONE) {
+		/* this is V3 style, looks just like a V4 simple hash */
+		pkt.u.seckey.alg = (pgp_symm_alg_t)c;
+		pkt.u.seckey.s2k_usage = PGP_S2KU_ENCRYPTED;
+		pkt.u.seckey.s2k_specifier = PGP_S2KS_SIMPLE;
+		pkt.u.seckey.hash_alg = PGP_HASH_MD5;
+	}
+	crypted = pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
+		pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED;
+
+	if (crypted) {
+		pgp_packet_t	seckey;
+		pgp_hash_t	hashes[(PGP_MAX_KEY_SIZE + PGP_MIN_HASH_SIZE - 1) / PGP_MIN_HASH_SIZE];
+		unsigned	passlen;
+		uint8_t   	key[PGP_MAX_KEY_SIZE + PGP_MAX_HASH_SIZE];
+		char           *passphrase;
+		int             hashsize;
+		int             keysize;
+		int             n;
+
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "crypted seckey\n");
+		}
+		blocksize = pgp_block_size(pkt.u.seckey.alg);
+		if (blocksize == 0 || blocksize > PGP_MAX_BLOCK_SIZE) {
+			(void) fprintf(stderr,
+				"parse_seckey: bad blocksize\n");
+			return 0;
+		}
+
+		if (!limread(pkt.u.seckey.iv, blocksize, region, stream)) {
+			return 0;
+		}
+		(void) memset(&seckey, 0x0, sizeof(seckey));
+		passphrase = NULL;
+		seckey.u.skey_passphrase.passphrase = &passphrase;
+		seckey.u.skey_passphrase.seckey = &pkt.u.seckey;
+		CALLBACK(PGP_GET_PASSPHRASE, &stream->cbinfo, &seckey);
+		if (!passphrase) {
+			if (pgp_get_debug_level(__FILE__)) {
+				/* \todo make into proper error */
+				(void) fprintf(stderr,
+				"parse_seckey: can't get passphrase\n");
+			}
+			if (!consume_packet(region, stream, 0)) {
+				return 0;
+			}
+
+			CALLBACK(PGP_PTAG_CT_ENCRYPTED_SECRET_KEY,
+				&stream->cbinfo, &pkt);
+
+			return 1;
+		}
+		keysize = pgp_key_size(pkt.u.seckey.alg);
+		if (keysize == 0 || keysize > PGP_MAX_KEY_SIZE) {
+			(void) fprintf(stderr,
+				"parse_seckey: bad keysize\n");
+			return 0;
+		}
+
+		/* Hardcoded SHA1 for just now */
+		pkt.u.seckey.hash_alg = PGP_HASH_SHA1;
+		hashsize = pgp_hash_size(pkt.u.seckey.hash_alg);
+		if (hashsize == 0 || hashsize > PGP_MAX_HASH_SIZE) {
+			(void) fprintf(stderr,
+				"parse_seckey: bad hashsize\n");
+			return 0;
+		}
+
+		for (n = 0; n * hashsize < keysize; ++n) {
+			int             i;
+
+			pgp_hash_any(&hashes[n],
+				pkt.u.seckey.hash_alg);
+			if (!hashes[n].init(&hashes[n])) {
+				(void) fprintf(stderr,
+					"parse_seckey: bad alloc\n");
+				return 0;
+			}
+			/* preload hashes with zeroes... */
+			for (i = 0; i < n; ++i) {
+				hashes[n].add(&hashes[n],
+					(const uint8_t *) "", 1);
+			}
+		}
+		passlen = (unsigned)strlen(passphrase);
+		for (n = 0; n * hashsize < keysize; ++n) {
+			unsigned        i;
+
+			switch (pkt.u.seckey.s2k_specifier) {
+			case PGP_S2KS_SALTED:
+				hashes[n].add(&hashes[n],
+					pkt.u.seckey.salt,
+					PGP_SALT_SIZE);
+				/* FALLTHROUGH */
+			case PGP_S2KS_SIMPLE:
+				hashes[n].add(&hashes[n],
+					(uint8_t *)passphrase, (unsigned)passlen);
+				break;
+
+			case PGP_S2KS_ITERATED_AND_SALTED:
+				for (i = 0; i < pkt.u.seckey.octetc;
+						i += passlen + PGP_SALT_SIZE) {
+					unsigned	j;
+
+					j = passlen + PGP_SALT_SIZE;
+					if (i + j > pkt.u.seckey.octetc && i != 0) {
+						j = pkt.u.seckey.octetc - i;
+					}
+					hashes[n].add(&hashes[n],
+						pkt.u.seckey.salt,
+						(unsigned)(j > PGP_SALT_SIZE) ?
+							PGP_SALT_SIZE : j);
+					if (j > PGP_SALT_SIZE) {
+						hashes[n].add(&hashes[n],
+						(uint8_t *) passphrase,
+						j - PGP_SALT_SIZE);
+					}
+				}
+				break;
+			default:
+				break;
+			}
+		}
+
+		for (n = 0; n * hashsize < keysize; ++n) {
+			int	r;
+
+			r = hashes[n].finish(&hashes[n], key + n * hashsize);
+			if (r != hashsize) {
+				(void) fprintf(stderr,
+					"parse_seckey: bad r\n");
+				return 0;
+			}
+		}
+
+		pgp_forget(passphrase, passlen);
+
+		pgp_crypt_any(&decrypt, pkt.u.seckey.alg);
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "input iv", pkt.u.seckey.iv, pgp_block_size(pkt.u.seckey.alg));
+			hexdump(stderr, "key", key, CAST_KEY_LENGTH);
+		}
+		decrypt.set_iv(&decrypt, pkt.u.seckey.iv);
+		decrypt.set_crypt_key(&decrypt, key);
+
+		/* now read encrypted data */
+
+		pgp_reader_push_decrypt(stream, &decrypt, region);
+
+		/*
+		 * Since all known encryption for PGP doesn't compress, we
+		 * can limit to the same length as the current region (for
+		 * now).
+		 */
+		pgp_init_subregion(&encregion, NULL);
+		encregion.length = region->length - region->readc;
+		if (pkt.u.seckey.pubkey.version != PGP_V4) {
+			encregion.length -= 2;
+		}
+		saved_region = region;
+		region = &encregion;
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "parse_seckey: end of crypted passphrase\n");
+	}
+
+    /* XXX - Hard-coded SHA1 here ?? Check */
+	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
+        pkt.u.seckey.checkhash = calloc(1, PGP_SHA1_HASH_SIZE);
+		if (pkt.u.seckey.checkhash == NULL) {
+			(void) fprintf(stderr, "parse_seckey: bad alloc\n");
+			return 0;
+		}
+		pgp_hash_sha1(&checkhash);
+		pgp_reader_push_hash(stream, &checkhash);
+	} else {
+		pgp_reader_push_sum16(stream);
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "parse_seckey: checkhash, reading MPIs\n");
+	}
+	switch (pkt.u.seckey.pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!limread_mpi(&pkt.u.seckey.key.rsa.d, region, stream) ||
+		    !limread_mpi(&pkt.u.seckey.key.rsa.p, region, stream) ||
+		    !limread_mpi(&pkt.u.seckey.key.rsa.q, region, stream) ||
+		    !limread_mpi(&pkt.u.seckey.key.rsa.u, region, stream)) {
+			ret = 0;
+		}
+		break;
+
+	case PGP_PKA_DSA:
+		if (!limread_mpi(&pkt.u.seckey.key.dsa.x, region, stream)) {
+			ret = 0;
+		}
+		break;
+
+	case PGP_PKA_ELGAMAL:
+		if (!limread_mpi(&pkt.u.seckey.key.elgamal.x, region, stream)) {
+			ret = 0;
+		}
+		break;
+
+	default:
+		PGP_ERROR_2(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
+			"Unsupported Public Key algorithm %d (%s)",
+			pkt.u.seckey.pubkey.alg,
+			pgp_show_pka(pkt.u.seckey.pubkey.alg));
+		ret = 0;
+	}
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "4 MPIs read\n");
+	}
+	//stream->reading_v3_secret = 0;
+
+	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
+		uint8_t   hash[PGP_CHECKHASH_SIZE];
+
+		pgp_reader_pop_hash(stream);
+		checkhash.finish(&checkhash, hash);
+
+		if (crypted &&
+		    pkt.u.seckey.pubkey.version != PGP_V4) {
+			pgp_reader_pop_decrypt(stream);
+			region = saved_region;
+		}
+		if (ret) {
+			if (!limread(pkt.u.seckey.checkhash,
+				PGP_CHECKHASH_SIZE, region, stream)) {
+				return 0;
+			}
+
+			if (memcmp(hash, pkt.u.seckey.checkhash,
+					PGP_CHECKHASH_SIZE) != 0) {
+				ERRP(&stream->cbinfo, pkt,
+					"Hash mismatch in secret key");
+			}
+		}
+	} else {
+		uint16_t  sum;
+
+		sum = pgp_reader_pop_sum16(stream);
+		if (crypted &&
+		    pkt.u.seckey.pubkey.version != PGP_V4) {
+			pgp_reader_pop_decrypt(stream);
+			region = saved_region;
+		}
+		if (ret) {
+			if (!limread_scalar(&pkt.u.seckey.checksum, 2,
+					region, stream))
+				return 0;
+
+			if (sum != pkt.u.seckey.checksum) {
+				ERRP(&stream->cbinfo, pkt,
+					"Checksum mismatch in secret key");
+			}
+		}
+	}
+
+	if (crypted && pkt.u.seckey.pubkey.version == PGP_V4) {
+		pgp_reader_pop_decrypt(stream);
+	}
+	if (region == NULL) {
+		(void) fprintf(stderr, "parse_seckey: NULL region\n");
+		return 0;
+	}
+	if (ret && region->readc != region->length) {
+		(void) fprintf(stderr, "parse_seckey: bad length\n");
+		return 0;
+	}
+	if (!ret) {
+		return 0;
+	}
+	CALLBACK(tag, &stream->cbinfo, &pkt);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "--- end of parse_seckey\n\n");
+	}
+	return 1;
+}
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Parse a Public Key Session Key packet
+*/
+static int
+parse_pk_sesskey(pgp_region_t *region,
+		     pgp_stream_t *stream)
+{
+	const pgp_seckey_t	*secret;
+	pgp_packet_t		 sesskey;
+	pgp_packet_t		 pkt;
+	uint8_t			*iv;
+	uint8_t		   	 c = 0x0;
+	uint8_t			 cs[2];
+	unsigned		 k;
+	BIGNUM			*g_to_k;
+	BIGNUM			*enc_m;
+	int			 n;
+	uint8_t		 	 unencoded_m_buf[1024];
+
+	if (!limread(&c, 1, region, stream)) {
+		(void) fprintf(stderr, "parse_pk_sesskey - can't read char in region\n");
+		return 0;
+	}
+	pkt.u.pk_sesskey.version = c;
+	if (pkt.u.pk_sesskey.version != 3) {
+		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_PKSK_VRSN,
+			"Bad public-key encrypted session key version (%d)",
+			    pkt.u.pk_sesskey.version);
+		return 0;
+	}
+	if (!limread(pkt.u.pk_sesskey.key_id,
+			  (unsigned)sizeof(pkt.u.pk_sesskey.key_id), region, stream)) {
+		return 0;
+	}
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sesskey: pubkey id", pkt.u.pk_sesskey.key_id, sizeof(pkt.u.pk_sesskey.key_id));
+	}
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.pk_sesskey.alg = (pgp_pubkey_alg_t)c;
+	switch (pkt.u.pk_sesskey.alg) {
+	case PGP_PKA_RSA:
+		if (!limread_mpi(&pkt.u.pk_sesskey.params.rsa.encrypted_m,
+				      region, stream)) {
+			return 0;
+		}
+		enc_m = pkt.u.pk_sesskey.params.rsa.encrypted_m;
+		g_to_k = NULL;
+		break;
+
+	case PGP_PKA_DSA:
+	case PGP_PKA_ELGAMAL:
+		if (!limread_mpi(&pkt.u.pk_sesskey.params.elgamal.g_to_k,
+				      region, stream) ||
+		    !limread_mpi(
+			&pkt.u.pk_sesskey.params.elgamal.encrypted_m,
+					 region, stream)) {
+			return 0;
+		}
+		g_to_k = pkt.u.pk_sesskey.params.elgamal.g_to_k;
+		enc_m = pkt.u.pk_sesskey.params.elgamal.encrypted_m;
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors,
+			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
+			"Unknown public key algorithm in session key (%s)",
+			pgp_show_pka(pkt.u.pk_sesskey.alg));
+		return 0;
+	}
+
+	(void) memset(&sesskey, 0x0, sizeof(sesskey));
+	secret = NULL;
+	sesskey.u.get_seckey.seckey = &secret;
+	sesskey.u.get_seckey.pk_sesskey = &pkt.u.pk_sesskey;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "getting secret key via callback\n");
+	}
+
+	CALLBACK(PGP_GET_SECKEY, &stream->cbinfo, &sesskey);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "got secret key via callback\n");
+	}
+	if (!secret) {
+		CALLBACK(PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, &stream->cbinfo,
+			&pkt);
+		return 1;
+	}
+	n = pgp_decrypt_decode_mpi(unencoded_m_buf,
+		(unsigned)sizeof(unencoded_m_buf), g_to_k, enc_m, secret);
+
+	if (n < 1) {
+		ERRP(&stream->cbinfo, pkt, "decrypted message too short");
+		return 0;
+	}
+
+	/* PKA */
+	pkt.u.pk_sesskey.symm_alg = (pgp_symm_alg_t)unencoded_m_buf[0];
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "symm alg %d\n", pkt.u.pk_sesskey.symm_alg);
+	}
+
+	if (!pgp_is_sa_supported(pkt.u.pk_sesskey.symm_alg)) {
+		/* ERR1P */
+		PGP_ERROR_1(&stream->errors, PGP_E_ALG_UNSUPPORTED_SYMMETRIC_ALG,
+			    "Symmetric algorithm %s not supported",
+			    pgp_show_symm_alg(
+				pkt.u.pk_sesskey.symm_alg));
+		return 0;
+	}
+	k = pgp_key_size(pkt.u.pk_sesskey.symm_alg);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "key size %d\n", k);
+	}
+
+	if ((unsigned) n != k + 3) {
+		PGP_ERROR_2(&stream->errors, PGP_E_PROTO_DECRYPTED_MSG_WRONG_LEN,
+		      "decrypted message wrong length (got %d expected %d)",
+			    n, k + 3);
+		return 0;
+	}
+	if (k > sizeof(pkt.u.pk_sesskey.key)) {
+		(void) fprintf(stderr, "parse_pk_sesskey: bad keylength\n");
+		return 0;
+	}
+
+	(void) memcpy(pkt.u.pk_sesskey.key, unencoded_m_buf + 1, k);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "recovered sesskey", pkt.u.pk_sesskey.key, k);
+	}
+	pkt.u.pk_sesskey.checksum = unencoded_m_buf[k + 1] +
+			(unencoded_m_buf[k + 2] << 8);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "session key checksum: %2x %2x\n",
+			unencoded_m_buf[k + 1], unencoded_m_buf[k + 2]);
+	}
+
+	/* Check checksum */
+	pgp_calc_sesskey_checksum(&pkt.u.pk_sesskey, &cs[0]);
+	if (unencoded_m_buf[k + 1] != cs[0] ||
+	    unencoded_m_buf[k + 2] != cs[1]) {
+		PGP_ERROR_4(&stream->errors, PGP_E_PROTO_BAD_SK_CHECKSUM,
+		"Session key checksum wrong: expected %2x %2x, got %2x %2x",
+		cs[0], cs[1], unencoded_m_buf[k + 1],
+		unencoded_m_buf[k + 2]);
+		return 0;
+	}
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "getting pk session key via callback\n");
+	}
+	/* all is well */
+	CALLBACK(PGP_PTAG_CT_PK_SESSION_KEY, &stream->cbinfo, &pkt);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "got pk session key via callback\n");
+	}
+
+	pgp_crypt_any(&stream->decrypt, pkt.u.pk_sesskey.symm_alg);
+	iv = calloc(1, stream->decrypt.blocksize);
+	if (iv == NULL) {
+		(void) fprintf(stderr, "parse_pk_sesskey: bad alloc\n");
+		return 0;
+	}
+	stream->decrypt.set_iv(&stream->decrypt, iv);
+	stream->decrypt.set_crypt_key(&stream->decrypt, pkt.u.pk_sesskey.key);
+	pgp_encrypt_init(&stream->decrypt);
+	free(iv);
+	return 1;
+}
+
+#if 0
+static int
+decrypt_se_data(pgp_content_enum tag, pgp_region_t *region,
+		    pgp_stream_t *stream)
+{
+	pgp_crypt_t	*decrypt;
+	const int	 printerrors = 1;
+	int		 r = 1;
+
+	decrypt = pgp_get_decrypt(stream);
+	if (decrypt) {
+		pgp_region_t	encregion;
+		unsigned	b = (unsigned)decrypt->blocksize;
+		uint8_t		buf[PGP_MAX_BLOCK_SIZE + 2] = "";
+
+		pgp_reader_push_decrypt(stream, decrypt, region);
+
+		pgp_init_subregion(&encregion, NULL);
+		encregion.length = b + 2;
+
+		if (!exact_limread(buf, b + 2, &encregion, stream)) {
+			return 0;
+		}
+		if (buf[b - 2] != buf[b] || buf[b - 1] != buf[b + 1]) {
+			pgp_reader_pop_decrypt(stream);
+			PGP_ERROR_4(&stream->errors,
+				PGP_E_PROTO_BAD_SYMMETRIC_DECRYPT,
+				"Bad symmetric decrypt (%02x%02x vs %02x%02x)",
+				buf[b - 2], buf[b - 1], buf[b], buf[b + 1]);
+			return 0;
+		}
+		if (tag == PGP_PTAG_CT_SE_DATA_BODY) {
+			decrypt->decrypt_resync(decrypt);
+			decrypt->block_encrypt(decrypt, decrypt->civ,
+					decrypt->civ);
+		}
+		r = pgp_parse(stream, !printerrors);
+
+		pgp_reader_pop_decrypt(stream);
+	} else {
+		pgp_packet_t pkt;
+
+		while (region->readc < region->length) {
+			unsigned        len;
+
+			len = region->length - region->readc;
+			if (len > sizeof(pkt.u.se_data_body.data))
+				len = sizeof(pkt.u.se_data_body.data);
+
+			if (!limread(pkt.u.se_data_body.data, len,
+					region, stream)) {
+				return 0;
+			}
+			pkt.u.se_data_body.length = len;
+			CALLBACK(tag, &stream->cbinfo, &pkt);
+		}
+	}
+
+	return r;
+}
+#endif
+
+static int
+decrypt_se_ip_data(pgp_content_enum tag, pgp_region_t *region,
+		       pgp_stream_t *stream)
+{
+	pgp_crypt_t	*decrypt;
+	const int	 printerrors = 1;
+	int		 r = 1;
+
+	decrypt = pgp_get_decrypt(stream);
+	if (decrypt) {
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "decrypt_se_ip_data: decrypt\n");
+		}
+		pgp_reader_push_decrypt(stream, decrypt, region);
+		pgp_reader_push_se_ip_data(stream, decrypt, region);
+
+		r = pgp_parse(stream, !printerrors);
+
+		pgp_reader_pop_se_ip_data(stream);
+		pgp_reader_pop_decrypt(stream);
+	} else {
+		pgp_packet_t pkt;
+
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "decrypt_se_ip_data: no decrypt\n");
+		}
+		while (region->readc < region->length) {
+			unsigned        len;
+
+			len = region->length - region->readc;
+			if (len > sizeof(pkt.u.se_data_body.data)) {
+				len = sizeof(pkt.u.se_data_body.data);
+			}
+
+			if (!limread(pkt.u.se_data_body.data,
+					len, region, stream)) {
+				return 0;
+			}
+
+			pkt.u.se_data_body.length = len;
+
+			CALLBACK(tag, &stream->cbinfo, &pkt);
+		}
+	}
+
+	return r;
+}
+
+#if 0
+/**
+   \ingroup Core_ReadPackets
+   \brief Read a Symmetrically Encrypted packet
+*/
+static int
+parse_se_data(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t pkt;
+
+	/* there's no info to go with this, so just announce it */
+	CALLBACK(PGP_PTAG_CT_SE_DATA_HEADER, &stream->cbinfo, &pkt);
+
+	/*
+	 * The content of an encrypted data packet is more OpenPGP packets
+	 * once decrypted, so recursively handle them
+	 */
+	return decrypt_se_data(PGP_PTAG_CT_SE_DATA_BODY, region, stream);
+}
+#endif
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Read a Symmetrically Encrypted Integrity Protected packet
+*/
+static int
+parse_se_ip_data(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t	pkt;
+	uint8_t		c = 0x0;
+
+	if (!limread(&c, 1, region, stream)) {
+		return 0;
+	}
+	pkt.u.se_ip_data_header = c;
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "parse_se_ip_data: data header %d\n", c);
+	}
+	if (pkt.u.se_ip_data_header != PGP_SE_IP_DATA_VERSION) {
+		(void) fprintf(stderr, "parse_se_ip_data: bad version\n");
+		return 0;
+	}
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "parse_se_ip_data: region %d,%d\n",
+			region->readc, region->length);
+		hexdump(stderr, "compressed region", stream->readinfo.virtualpkt, stream->readinfo.virtualc);
+	}
+	/*
+	 * The content of an encrypted data packet is more OpenPGP packets
+	 * once decrypted, so recursively handle them
+	 */
+	return decrypt_se_ip_data(PGP_PTAG_CT_SE_IP_DATA_BODY, region, stream);
+}
+
+/**
+   \ingroup Core_ReadPackets
+   \brief Read a MDC packet
+*/
+static int
+parse_mdc(pgp_region_t *region, pgp_stream_t *stream)
+{
+	pgp_packet_t pkt;
+
+	pkt.u.mdc.length = PGP_SHA1_HASH_SIZE;
+	if ((pkt.u.mdc.data = calloc(1, PGP_SHA1_HASH_SIZE)) == NULL) {
+		(void) fprintf(stderr, "parse_mdc: bad alloc\n");
+		return 0;
+	}
+	if (!limread(pkt.u.mdc.data, PGP_SHA1_HASH_SIZE, region, stream)) {
+		return 0;
+	}
+	CALLBACK(PGP_PTAG_CT_MDC, &stream->cbinfo, &pkt);
+	free(pkt.u.mdc.data);
+	return 1;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ * \brief Parse one packet.
+ *
+ * This function parses the packet tag.  It computes the value of the
+ * content tag and then calls the appropriate function to handle the
+ * content.
+ *
+ * \param *stream	How to parse
+ * \param *pktlen	On return, will contain number of bytes in packet
+ * \return 1 on success, 0 on error, -1 on EOF */
+static int
+parse_packet(pgp_stream_t *stream, uint32_t *pktlen)
+{
+	pgp_packet_t	pkt;
+	pgp_region_t	region;
+	uint8_t		ptag;
+	unsigned	indeterminate = 0;
+	int		ret;
+
+	pkt.u.ptag.position = stream->readinfo.position;
+
+	ret = base_read(&ptag, 1, stream);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr,
+			"parse_packet: base_read returned %d, ptag %d\n",
+			ret, ptag);
+	}
+
+	/* errors in the base read are effectively EOF. */
+	if (ret <= 0) {
+		return -1;
+	}
+
+	*pktlen = 0;
+
+	if (!(ptag & PGP_PTAG_ALWAYS_SET)) {
+		pkt.u.error = "Format error (ptag bit not set)";
+		CALLBACK(PGP_PARSER_ERROR, &stream->cbinfo, &pkt);
+		return 0;
+	}
+	pkt.u.ptag.new_format = !!(ptag & PGP_PTAG_NEW_FORMAT);
+	if (pkt.u.ptag.new_format) {
+		pkt.u.ptag.type = (ptag & PGP_PTAG_NF_CONTENT_TAG_MASK);
+		pkt.u.ptag.length_type = 0;
+		if (!read_new_length(&pkt.u.ptag.length, stream)) {
+			return 0;
+		}
+	} else {
+		unsigned   rb;
+
+		pkt.u.ptag.type = ((unsigned)ptag &
+				PGP_PTAG_OF_CONTENT_TAG_MASK)
+			>> PGP_PTAG_OF_CONTENT_TAG_SHIFT;
+		pkt.u.ptag.length_type = ptag & PGP_PTAG_OF_LENGTH_TYPE_MASK;
+		switch (pkt.u.ptag.length_type) {
+		case PGP_PTAG_OLD_LEN_1:
+			rb = _read_scalar(&pkt.u.ptag.length, 1, stream);
+			break;
+
+		case PGP_PTAG_OLD_LEN_2:
+			rb = _read_scalar(&pkt.u.ptag.length, 2, stream);
+			break;
+
+		case PGP_PTAG_OLD_LEN_4:
+			rb = _read_scalar(&pkt.u.ptag.length, 4, stream);
+			break;
+
+		case PGP_PTAG_OLD_LEN_INDETERMINATE:
+			pkt.u.ptag.length = 0;
+			indeterminate = 1;
+			rb = 1;
+			break;
+		}
+		if (!rb) {
+			return 0;
+		}
+	}
+
+	CALLBACK(PGP_PARSER_PTAG, &stream->cbinfo, &pkt);
+
+	pgp_init_subregion(&region, NULL);
+	region.length = pkt.u.ptag.length;
+	region.indeterminate = indeterminate;
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "parse_packet: type %u\n",
+			       pkt.u.ptag.type);
+	}
+	switch (pkt.u.ptag.type) {
+	case PGP_PTAG_CT_SIGNATURE:
+		ret = parse_sig(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_PUBLIC_KEY:
+	case PGP_PTAG_CT_PUBLIC_SUBKEY:
+		ret = parse_pubkey((pgp_content_enum)pkt.u.ptag.type, &region, stream);
+		break;
+
+	case PGP_PTAG_CT_TRUST:
+		ret = parse_trust(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_USER_ID:
+		ret = parse_userid(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_COMPRESSED:
+		ret = parse_compressed(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_1_PASS_SIG:
+		ret = parse_one_pass(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_LITDATA:
+		ret = parse_litdata(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_USER_ATTR:
+		ret = parse_userattr(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_SECRET_KEY:
+	case PGP_PTAG_CT_SECRET_SUBKEY:
+		ret = parse_seckey((pgp_content_enum)pkt.u.ptag.type, &region, stream);
+		break;
+
+	case PGP_PTAG_CT_PK_SESSION_KEY:
+		ret = parse_pk_sesskey(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_SE_DATA:
+        // SE_DATA CURRENTLY BROKEN
+        ret = 0;
+		//ret = parse_se_data(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_SE_IP_DATA:
+		ret = parse_se_ip_data(&region, stream);
+		break;
+
+	case PGP_PTAG_CT_MDC:
+		ret = parse_mdc(&region, stream);
+		break;
+
+	default:
+		PGP_ERROR_1(&stream->errors, PGP_E_P_UNKNOWN_TAG,
+			    "Unknown content tag 0x%x",
+			    pkt.u.ptag.type);
+		ret = 0;
+	}
+
+	/* Ensure that the entire packet has been consumed */
+
+	if (region.length != region.readc && !region.indeterminate) {
+		if (!consume_packet(&region, stream, 0)) {
+			ret = -1;
+		}
+	}
+
+	/* also consume it if there's been an error? */
+	/* \todo decide what to do about an error on an */
+	/* indeterminate packet */
+	if (ret == 0) {
+		if (!consume_packet(&region, stream, 0)) {
+			ret = -1;
+		}
+	}
+	/* set pktlen */
+
+	*pktlen = stream->readinfo.alength;
+
+	/* do callback on entire packet, if desired and there was no error */
+
+	if (ret > 0 && stream->readinfo.accumulate) {
+		pkt.u.packet.length = stream->readinfo.alength;
+		pkt.u.packet.raw = stream->readinfo.accumulated;
+		stream->readinfo.accumulated = NULL;
+		stream->readinfo.asize = 0;
+		CALLBACK(PGP_PARSER_PACKET_END, &stream->cbinfo, &pkt);
+	}
+	stream->readinfo.alength = 0;
+
+	return (ret < 0) ? -1 : (ret) ? 1 : 0;
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ *
+ * \brief Parse packets from an input stream until EOF or error.
+ *
+ * \details Setup the necessary parsing configuration in "stream"
+ * before calling pgp_parse().
+ *
+ * That information includes :
+ *
+ * - a "reader" function to be used to get the data to be parsed
+ *
+ * - a "callback" function to be called when this library has identified
+ * a parseable object within the data
+ *
+ * - whether the calling function wants the signature subpackets
+ * returned raw, parsed or not at all.
+ *
+ * After returning, stream->errors holds any errors encountered while parsing.
+ *
+ * \param stream	Parsing configuration
+ * \return		1 on success in all packets, 0 on error in any packet
+ *
+ * \sa CoreAPI Overview
+ *
+ * \sa pgp_print_errors()
+ *
+ */
+
+int
+pgp_parse(pgp_stream_t *stream, const int perrors)
+{
+	uint32_t   pktlen;
+	int             r;
+
+	do {
+		r = parse_packet(stream, &pktlen);
+	} while (r != -1);
+	if (perrors) {
+		pgp_print_errors(stream->errors);
+	}
+	return (stream->errors == NULL);
+}
+
+/**
+ * \ingroup Core_ReadPackets
+ *
+ * \brief Specifies whether one or more signature
+ * subpacket types should be returned parsed; or raw; or ignored.
+ *
+ * \param	stream	Pointer to previously allocated structure
+ * \param	tag	Packet tag. PGP_PTAG_SS_ALL for all SS tags; or one individual signature subpacket tag
+ * \param	type	Parse type
+ * \todo Make all packet types optional, not just subpackets */
+void
+pgp_parse_options(pgp_stream_t *stream,
+		  pgp_content_enum tag,
+		  pgp_parse_type_t type)
+{
+	unsigned	t7;
+	unsigned	t8;
+
+	if (tag == PGP_PTAG_SS_ALL) {
+		int             n;
+
+		for (n = 0; n < 256; ++n) {
+			pgp_parse_options(stream,
+				PGP_PTAG_SIG_SUBPKT_BASE + n,
+				type);
+		}
+		return;
+	}
+	if (tag < PGP_PTAG_SIG_SUBPKT_BASE ||
+	    tag > PGP_PTAG_SIG_SUBPKT_BASE + NTAGS - 1) {
+		(void) fprintf(stderr, "pgp_parse_options: bad tag\n");
+		return;
+	}
+	t8 = (tag - PGP_PTAG_SIG_SUBPKT_BASE) / 8;
+	t7 = 1 << ((tag - PGP_PTAG_SIG_SUBPKT_BASE) & 7);
+	switch (type) {
+	case PGP_PARSE_RAW:
+		stream->ss_raw[t8] |= t7;
+		stream->ss_parsed[t8] &= ~t7;
+		break;
+
+	case PGP_PARSE_PARSED:
+		stream->ss_raw[t8] &= ~t7;
+		stream->ss_parsed[t8] |= t7;
+		break;
+
+	case PGP_PARSE_IGNORE:
+		stream->ss_raw[t8] &= ~t7;
+		stream->ss_parsed[t8] &= ~t7;
+		break;
+	}
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Free pgp_stream_t struct and its contents
+*/
+void
+pgp_stream_delete(pgp_stream_t *stream)
+{
+	pgp_cbdata_t	*cbinfo;
+	pgp_cbdata_t	*next;
+    pgp_cryptinfo_t *cryptinfo = &stream->cbinfo.cryptinfo;
+
+	for (cbinfo = stream->cbinfo.next; cbinfo; cbinfo = next) {
+		next = cbinfo->next;
+		free(cbinfo);
+	}
+
+    FREE_ARRAY(cryptinfo, recipients_key_ids);
+
+	if (stream->readinfo.destroyer) {
+		stream->readinfo.destroyer(&stream->readinfo);
+	}
+	pgp_free_errors(stream->errors);
+	if (stream->readinfo.accumulated) {
+		free(stream->readinfo.accumulated);
+	}
+	free(stream);
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Returns the parse_info's reader_info
+\return Pointer to the reader_info inside the parse_info
+*/
+pgp_reader_t *
+pgp_readinfo(pgp_stream_t *stream)
+{
+	return &stream->readinfo;
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Sets the parse_info's callback
+This is used when adding the first callback in a stack of callbacks.
+\sa pgp_callback_push()
+*/
+
+void
+pgp_set_callback(pgp_stream_t *stream, pgp_cbfunc_t *cb, void *arg)
+{
+	stream->cbinfo.cbfunc = cb;
+	stream->cbinfo.arg = arg;
+	stream->cbinfo.errors = &stream->errors;
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Adds a further callback to a stack of callbacks
+\sa pgp_set_callback()
+*/
+void
+pgp_callback_push(pgp_stream_t *stream, pgp_cbfunc_t *cb, void *arg)
+{
+	pgp_cbdata_t	*cbinfo;
+
+	if ((cbinfo = calloc(1, sizeof(*cbinfo))) == NULL) {
+		(void) fprintf(stderr, "pgp_callback_push: bad alloc\n");
+		return;
+	}
+	(void) memcpy(cbinfo, &stream->cbinfo, sizeof(*cbinfo));
+	cbinfo->io = stream->io;
+	stream->cbinfo.next = cbinfo;
+	pgp_set_callback(stream, cb, arg);
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Returns callback's arg
+*/
+void *
+pgp_callback_arg(pgp_cbdata_t *cbinfo)
+{
+	return cbinfo->arg;
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Returns callback's errors
+*/
+void *
+pgp_callback_errors(pgp_cbdata_t *cbinfo)
+{
+	return cbinfo->errors;
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Calls the parse_cb_info's callback if present
+\return Return value from callback, if present; else PGP_FINISHED
+*/
+pgp_cb_ret_t
+pgp_callback(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	return (cbinfo->cbfunc) ? cbinfo->cbfunc(pkt, cbinfo) : PGP_FINISHED;
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Calls the next callback  in the stack
+\return Return value from callback
+*/
+pgp_cb_ret_t
+pgp_stacked_callback(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	return pgp_callback(pkt, cbinfo->next);
+}
+
+/**
+\ingroup Core_ReadPackets
+\brief Returns the parse_info's errors
+\return parse_info's errors
+*/
+pgp_error_t    *
+pgp_stream_get_errors(pgp_stream_t *stream)
+{
+	return stream->errors;
+}
+
+pgp_crypt_t    *
+pgp_get_decrypt(pgp_stream_t *stream)
+{
+	return (stream->decrypt.alg) ? &stream->decrypt : NULL;
+}
diff --git a/netpgp/packet-show.c b/netpgp/packet-show.c
new file mode 100644
index 0000000000000000000000000000000000000000..fdc4909ce7003e25106fde83bf3649c5ed65047f
--- /dev/null
+++ b/netpgp/packet-show.c
@@ -0,0 +1,491 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ *
+ * Creates printable text strings from packet contents
+ *
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "netpgp/packet-show.h"
+
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/netpgpdefs.h"
+
+
+/*
+ * Arrays of value->text maps
+ */
+
+static pgp_map_t packet_tag_map[] =
+{
+	{PGP_PTAG_CT_RESERVED, "Reserved"},
+	{PGP_PTAG_CT_PK_SESSION_KEY, "Public-Key Encrypted Session Key"},
+	{PGP_PTAG_CT_SIGNATURE, "Signature"},
+	{PGP_PTAG_CT_SK_SESSION_KEY, "Symmetric-Key Encrypted Session Key"},
+	{PGP_PTAG_CT_1_PASS_SIG, "One-Pass Signature"},
+	{PGP_PTAG_CT_SECRET_KEY, "Secret Key"},
+	{PGP_PTAG_CT_PUBLIC_KEY, "Public Key"},
+	{PGP_PTAG_CT_SECRET_SUBKEY, "Secret Subkey"},
+	{PGP_PTAG_CT_COMPRESSED, "Compressed Data"},
+	{PGP_PTAG_CT_SE_DATA, "Symmetrically Encrypted Data"},
+	{PGP_PTAG_CT_MARKER, "Marker"},
+	{PGP_PTAG_CT_LITDATA, "Literal Data"},
+	{PGP_PTAG_CT_TRUST, "Trust"},
+	{PGP_PTAG_CT_USER_ID, "User ID"},
+	{PGP_PTAG_CT_PUBLIC_SUBKEY, "Public Subkey"},
+	{PGP_PTAG_CT_RESERVED2, "reserved2"},
+	{PGP_PTAG_CT_RESERVED3, "reserved3"},
+	{PGP_PTAG_CT_USER_ATTR, "User Attribute"},
+	{PGP_PTAG_CT_SE_IP_DATA,
+		"Symmetric Encrypted and Integrity Protected Data"},
+	{PGP_PTAG_CT_MDC, "Modification Detection Code"},
+	{PGP_PARSER_PTAG, "PGP_PARSER_PTAG"},
+	{PGP_PTAG_RAW_SS, "PGP_PTAG_RAW_SS"},
+	{PGP_PTAG_SS_ALL, "PGP_PTAG_SS_ALL"},
+	{PGP_PARSER_PACKET_END, "PGP_PARSER_PACKET_END"},
+	{PGP_PTAG_SIG_SUBPKT_BASE, "PGP_PTAG_SIG_SUBPKT_BASE"},
+	{PGP_PTAG_SS_CREATION_TIME, "SS: Signature Creation Time"},
+	{PGP_PTAG_SS_EXPIRATION_TIME, "SS: Signature Expiration Time"},
+	{PGP_PTAG_SS_EXPORT_CERT, "SS: Exportable Certification"},
+	{PGP_PTAG_SS_TRUST, "SS: Trust Signature"},
+	{PGP_PTAG_SS_REGEXP, "SS: Regular Expression"},
+	{PGP_PTAG_SS_REVOCABLE, "SS: Revocable"},
+	{PGP_PTAG_SS_KEY_EXPIRY, "SS: Key Expiration Time"},
+	{PGP_PTAG_SS_RESERVED, "SS: Reserved"},
+	{PGP_PTAG_SS_PREFERRED_SKA, "SS: Preferred Secret Key Algorithm"},
+	{PGP_PTAG_SS_REVOCATION_KEY, "SS: Revocation Key"},
+	{PGP_PTAG_SS_ISSUER_KEY_ID, "SS: Issuer Key Id"},
+	{PGP_PTAG_SS_NOTATION_DATA, "SS: Notation Data"},
+	{PGP_PTAG_SS_PREFERRED_HASH, "SS: Preferred Hash Algorithm"},
+	{PGP_PTAG_SS_PREF_COMPRESS, "SS: Preferred Compression Algorithm"},
+	{PGP_PTAG_SS_KEYSERV_PREFS, "SS: Key Server Preferences"},
+	{PGP_PTAG_SS_PREF_KEYSERV, "SS: Preferred Key Server"},
+	{PGP_PTAG_SS_PRIMARY_USER_ID, "SS: Primary User ID"},
+	{PGP_PTAG_SS_POLICY_URI, "SS: Policy URI"},
+	{PGP_PTAG_SS_KEY_FLAGS, "SS: Key Flags"},
+	{PGP_PTAG_SS_SIGNERS_USER_ID, "SS: Signer's User ID"},
+	{PGP_PTAG_SS_REVOCATION_REASON, "SS: Reason for Revocation"},
+	{PGP_PTAG_SS_FEATURES, "SS: Features"},
+	{PGP_PTAG_SS_SIGNATURE_TARGET, "SS: Signature Target"},
+	{PGP_PTAG_SS_EMBEDDED_SIGNATURE, "SS: Embedded Signature"},
+
+	{PGP_PTAG_CT_LITDATA_HEADER, "CT: Literal Data Header"},
+	{PGP_PTAG_CT_LITDATA_BODY, "CT: Literal Data Body"},
+	{PGP_PTAG_CT_SIGNATURE_HEADER, "CT: Signature Header"},
+	{PGP_PTAG_CT_SIGNATURE_FOOTER, "CT: Signature Footer"},
+	{PGP_PTAG_CT_ARMOUR_HEADER, "CT: Armour Header"},
+	{PGP_PTAG_CT_ARMOUR_TRAILER, "CT: Armour Trailer"},
+	{PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER, "CT: Signed Cleartext Header"},
+	{PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY, "CT: Signed Cleartext Body"},
+	{PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, "CT: Signed Cleartext Trailer"},
+	{PGP_PTAG_CT_UNARMOURED_TEXT, "CT: Unarmoured Text"},
+	{PGP_PTAG_CT_ENCRYPTED_SECRET_KEY, "CT: Encrypted Secret Key"},
+	{PGP_PTAG_CT_SE_DATA_HEADER, "CT: Sym Encrypted Data Header"},
+	{PGP_PTAG_CT_SE_DATA_BODY, "CT: Sym Encrypted Data Body"},
+	{PGP_PTAG_CT_SE_IP_DATA_HEADER, "CT: Sym Encrypted IP Data Header"},
+	{PGP_PTAG_CT_SE_IP_DATA_BODY, "CT: Sym Encrypted IP Data Body"},
+	{PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, "CT: Encrypted PK Session Key"},
+	{PGP_GET_PASSPHRASE, "CMD: Get Secret Key Passphrase"},
+	{PGP_GET_SECKEY, "CMD: Get Secret Key"},
+	{PGP_PARSER_ERROR, "PGP_PARSER_ERROR"},
+	{PGP_PARSER_ERRCODE, "PGP_PARSER_ERRCODE"},
+
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t ss_type_map[] =
+{
+	{PGP_PTAG_SS_CREATION_TIME, "Signature Creation Time"},
+	{PGP_PTAG_SS_EXPIRATION_TIME, "Signature Expiration Time"},
+	{PGP_PTAG_SS_TRUST, "Trust Signature"},
+	{PGP_PTAG_SS_REGEXP, "Regular Expression"},
+	{PGP_PTAG_SS_REVOCABLE, "Revocable"},
+	{PGP_PTAG_SS_KEY_EXPIRY, "Key Expiration Time"},
+	{PGP_PTAG_SS_PREFERRED_SKA, "Preferred Symmetric Algorithms"},
+	{PGP_PTAG_SS_REVOCATION_KEY, "Revocation Key"},
+	{PGP_PTAG_SS_ISSUER_KEY_ID, "Issuer key ID"},
+	{PGP_PTAG_SS_NOTATION_DATA, "Notation Data"},
+	{PGP_PTAG_SS_PREFERRED_HASH, "Preferred Hash Algorithms"},
+	{PGP_PTAG_SS_PREF_COMPRESS, "Preferred Compression Algorithms"},
+	{PGP_PTAG_SS_KEYSERV_PREFS, "Key Server Preferences"},
+	{PGP_PTAG_SS_PREF_KEYSERV, "Preferred Key Server"},
+	{PGP_PTAG_SS_PRIMARY_USER_ID, "Primary User ID"},
+	{PGP_PTAG_SS_POLICY_URI, "Policy URI"},
+	{PGP_PTAG_SS_KEY_FLAGS, "Key Flags"},
+	{PGP_PTAG_SS_REVOCATION_REASON, "Reason for Revocation"},
+	{PGP_PTAG_SS_FEATURES, "Features"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+
+static pgp_map_t ss_rr_code_map[] =
+{
+	{0x00, "No reason specified"},
+	{0x01, "Key is superseded"},
+	{0x02, "Key material has been compromised"},
+	{0x03, "Key is retired and no longer used"},
+	{0x20, "User ID information is no longer valid"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t sig_type_map[] =
+{
+	{PGP_SIG_BINARY, "Signature of a binary document"},
+	{PGP_SIG_TEXT, "Signature of a canonical text document"},
+	{PGP_SIG_STANDALONE, "Standalone signature"},
+	{PGP_CERT_GENERIC, "Generic certification of a User ID and Public Key packet"},
+	{PGP_CERT_PERSONA, "Personal certification of a User ID and Public Key packet"},
+	{PGP_CERT_CASUAL, "Casual certification of a User ID and Public Key packet"},
+	{PGP_CERT_POSITIVE, "Positive certification of a User ID and Public Key packet"},
+	{PGP_SIG_SUBKEY, "Subkey Binding Signature"},
+	{PGP_SIG_PRIMARY, "Primary Key Binding Signature"},
+	{PGP_SIG_DIRECT, "Signature directly on a key"},
+	{PGP_SIG_REV_KEY, "Key revocation signature"},
+	{PGP_SIG_REV_SUBKEY, "Subkey revocation signature"},
+	{PGP_SIG_REV_CERT, "Certification revocation signature"},
+	{PGP_SIG_TIMESTAMP, "Timestamp signature"},
+	{PGP_SIG_3RD_PARTY, "Third-Party Confirmation signature"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t pubkey_alg_map[] =
+{
+	{PGP_PKA_RSA, "RSA (Encrypt or Sign)"},
+	{PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"},
+	{PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"},
+	{PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"},
+	{PGP_PKA_DSA, "DSA"},
+	{PGP_PKA_RESERVED_ELLIPTIC_CURVE, "Reserved for Elliptic Curve"},
+	{PGP_PKA_RESERVED_ECDSA, "Reserved for ECDSA"},
+	{PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Reserved (formerly Elgamal Encrypt or Sign"},
+	{PGP_PKA_RESERVED_DH, "Reserved for Diffie-Hellman (X9.42)"},
+	{PGP_PKA_PRIVATE00, "Private/Experimental"},
+	{PGP_PKA_PRIVATE01, "Private/Experimental"},
+	{PGP_PKA_PRIVATE02, "Private/Experimental"},
+	{PGP_PKA_PRIVATE03, "Private/Experimental"},
+	{PGP_PKA_PRIVATE04, "Private/Experimental"},
+	{PGP_PKA_PRIVATE05, "Private/Experimental"},
+	{PGP_PKA_PRIVATE06, "Private/Experimental"},
+	{PGP_PKA_PRIVATE07, "Private/Experimental"},
+	{PGP_PKA_PRIVATE08, "Private/Experimental"},
+	{PGP_PKA_PRIVATE09, "Private/Experimental"},
+	{PGP_PKA_PRIVATE10, "Private/Experimental"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t symm_alg_map[] =
+{
+	{PGP_SA_PLAINTEXT, "Plaintext or unencrypted data"},
+	{PGP_SA_IDEA, "IDEA"},
+	{PGP_SA_TRIPLEDES, "TripleDES"},
+	{PGP_SA_CAST5, "CAST5"},
+	{PGP_SA_BLOWFISH, "Blowfish"},
+	{PGP_SA_AES_128, "AES (128-bit key)"},
+	{PGP_SA_AES_192, "AES (192-bit key)"},
+	{PGP_SA_AES_256, "AES (256-bit key)"},
+	{PGP_SA_TWOFISH, "Twofish(256-bit key)"},
+	{PGP_SA_CAMELLIA_128, "Camellia (128-bit key)"},
+	{PGP_SA_CAMELLIA_192, "Camellia (192-bit key)"},
+	{PGP_SA_CAMELLIA_256, "Camellia (256-bit key)"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t hash_alg_map[] =
+{
+	{PGP_HASH_MD5, "MD5"},
+	{PGP_HASH_SHA1, "SHA1"},
+	{PGP_HASH_RIPEMD, "RIPEMD160"},
+	{PGP_HASH_SHA256, "SHA256"},
+	{PGP_HASH_SHA384, "SHA384"},
+	{PGP_HASH_SHA512, "SHA512"},
+	{PGP_HASH_SHA224, "SHA224"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+static pgp_map_t compression_alg_map[] =
+{
+	{PGP_C_NONE, "Uncompressed"},
+	{PGP_C_ZIP, "ZIP(RFC1951)"},
+	{PGP_C_ZLIB, "ZLIB(RFC1950)"},
+	{PGP_C_BZIP2, "Bzip2(BZ2)"},
+	{0x00, NULL},		/* this is the end-of-array marker */
+};
+
+/*
+ * Private functions
+ */
+
+static void
+list_init(pgp_list_t *list)
+{
+	list->size = 0;
+	list->used = 0;
+	list->strings = NULL;
+}
+
+static void
+list_free_strings(pgp_list_t *list)
+{
+	unsigned        i;
+
+	for (i = 0; i < list->used; i++) {
+		free(list->strings[i]);
+		list->strings[i] = NULL;
+	}
+}
+
+static void
+list_free(pgp_list_t *list)
+{
+	if (list->strings)
+		free(list->strings);
+	list_init(list);
+}
+
+/* find a bitfield in a map - serial search */
+static const char *
+find_bitfield(pgp_bit_map_t *map, uint8_t octet)
+{
+	pgp_bit_map_t  *row;
+
+	for (row = map; row->string != NULL && row->mask != octet ; row++) {
+	}
+	return (row->string) ? row->string : "Unknown";
+}
+
+/* ! generic function to initialise pgp_text_t structure */
+void
+pgp_text_init(pgp_text_t *text)
+{
+	list_init(&text->known);
+	list_init(&text->unknown);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * pgp_text_free() frees the memory used by an pgp_text_t structure
+ *
+ * \param text Pointer to a previously allocated structure. This structure and its contents will be freed.
+ */
+void
+pgp_text_free(pgp_text_t *text)
+{
+	/* Strings in "known" array will be constants, so don't free them */
+	list_free(&text->known);
+
+	/*
+	 * Strings in "unknown" array will be dynamically allocated, so do
+	 * free them
+	 */
+	list_free_strings(&text->unknown);
+	list_free(&text->unknown);
+
+	free(text);
+}
+
+/*
+ * Public Functions
+ */
+
+/**
+ * \ingroup Core_Print
+ * returns description of the Packet Tag
+ * \param packet_tag
+ * \return string or "Unknown"
+*/
+const char     *
+pgp_show_packet_tag(pgp_content_enum packet_tag)
+{
+	const char     *ret;
+
+	ret = pgp_str_from_map(packet_tag, packet_tag_map);
+	if (!ret) {
+		ret = "Unknown Tag";
+	}
+	return ret;
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of the Signature Sub-Packet type
+ * \param ss_type Signature Sub-Packet type
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_ss_type(pgp_content_enum ss_type)
+{
+	return pgp_str_from_map(ss_type, ss_type_map);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of the Revocation Reason code
+ * \param ss_rr_code Revocation Reason code
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_ss_rr_code(pgp_ss_rr_code_t ss_rr_code)
+{
+	return pgp_str_from_map(ss_rr_code, ss_rr_code_map);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of the given Signature type
+ * \param sig_type Signature type
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_sig_type(pgp_sig_type_t sig_type)
+{
+	return pgp_str_from_map(sig_type, sig_type_map);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of the given Public Key Algorithm
+ * \param pka Public Key Algorithm type
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_pka(pgp_pubkey_alg_t pka)
+{
+	return pgp_str_from_map(pka, pubkey_alg_map);
+}
+
+/**
+ * \ingroup Core_Print
+ * returns description of the Preferred Compression
+ * \param octet Preferred Compression
+ * \return string or "Unknown"
+*/
+const char     *
+pgp_show_ss_zpref(uint8_t octet)
+{
+	return pgp_str_from_map(octet, compression_alg_map);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of the Hash Algorithm type
+ * \param hash Hash Algorithm type
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_hash_alg(uint8_t hash)
+{
+	return pgp_str_from_map(hash, hash_alg_map);
+}
+
+const char     *
+pgp_show_symm_alg(uint8_t hash)
+{
+	return pgp_str_from_map(hash, symm_alg_map);
+}
+
+/**
+ * \ingroup Core_Print
+ * returns description of the given Preferred Symmetric Key Algorithm
+ * \param octet
+ * \return string or "Unknown"
+*/
+const char     *
+pgp_show_ss_skapref(uint8_t octet)
+{
+	return pgp_str_from_map(octet, symm_alg_map);
+}
+
+/**
+ * \ingroup Core_Print
+ * returns description of SS Key Flag
+ * \param octet
+ * \param map
+ * \return
+*/
+const char     *
+pgp_show_ss_key_flag(uint8_t octet, pgp_bit_map_t *map)
+{
+	return find_bitfield(map, octet);
+}
+
+/**
+ * \ingroup Core_Print
+ *
+ * returns description of one given Key Server Preference
+ *
+ * \param prefs Byte containing bitfield of preferences
+ * \param map
+ * \return string or "Unknown"
+ */
+const char     *
+pgp_show_keyserv_pref(uint8_t prefs, pgp_bit_map_t *map)
+{
+	return find_bitfield(map, prefs);
+}
+
diff --git a/netpgp/reader.c b/netpgp/reader.c
new file mode 100644
index 0000000000000000000000000000000000000000..f2fde6366f027dc72ed98a427ee4a1ffec729687
--- /dev/null
+++ b/netpgp/reader.c
@@ -0,0 +1,2395 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_OPENSSL_IDEA_H
+#include <openssl/cast.h>
+#endif
+
+#ifdef HAVE_OPENSSL_IDEA_H
+#include <openssl/idea.h>
+#endif
+
+#ifdef HAVE_OPENSSL_AES_H
+#include <openssl/aes.h>
+#endif
+
+#ifdef HAVE_OPENSSL_DES_H
+#include <openssl/des.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "netpgp/errors.h"
+#include "netpgp/crypto.h"
+#include "netpgp/create.h"
+#include "netpgp/signature.h"
+#include "netpgp/packet.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/packet-show.h"
+#include "netpgp/packet.h"
+#include "netpgp/keyring.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/netpgpdigest.h"
+
+
+/* get a pass phrase from the user */
+int
+pgp_getpassphrase(void *in, char *phrase, size_t size)
+{
+	char	*p;
+
+	if (in == NULL) {
+		while ((p = getpass("netpgp passphrase: ")) == NULL) {
+		}
+		(void) snprintf(phrase, size, "%s", p);
+	} else {
+		if (fgets(phrase, (int)size, in) == NULL) {
+			return 0;
+		}
+		phrase[strlen(phrase) - 1] = 0x0;
+	}
+	return 1;
+}
+
+/**
+ * \ingroup Internal_Readers_Generic
+ * \brief Starts reader stack
+ * \param stream Parse settings
+ * \param reader Reader to use
+ * \param destroyer Destroyer to use
+ * \param vp Reader-specific arg
+ */
+void
+pgp_reader_set(pgp_stream_t *stream,
+		pgp_reader_func_t *reader,
+		pgp_reader_destroyer_t *destroyer,
+		void *vp)
+{
+	stream->readinfo.reader = reader;
+	stream->readinfo.destroyer = destroyer;
+	stream->readinfo.arg = vp;
+}
+
+/**
+ * \ingroup Internal_Readers_Generic
+ * \brief Adds to reader stack
+ * \param stream Parse settings
+ * \param reader Reader to use
+ * \param destroyer Reader's destroyer
+ * \param vp Reader-specific arg
+ */
+void
+pgp_reader_push(pgp_stream_t *stream,
+		pgp_reader_func_t *reader,
+		pgp_reader_destroyer_t *destroyer,
+		void *vp)
+{
+	pgp_reader_t *readinfo;
+
+	if ((readinfo = calloc(1, sizeof(*readinfo))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_push: bad alloc\n");
+	} else {
+		*readinfo = stream->readinfo;
+		(void) memset(&stream->readinfo, 0x0, sizeof(stream->readinfo));
+		stream->readinfo.next = readinfo;
+		stream->readinfo.parent = stream;
+
+		/* should copy accumulate flags from other reader? RW */
+		stream->readinfo.accumulate = readinfo->accumulate;
+
+		pgp_reader_set(stream, reader, destroyer, vp);
+	}
+}
+
+/**
+ * \ingroup Internal_Readers_Generic
+ * \brief Removes from reader stack
+ * \param stream Parse settings
+ */
+void
+pgp_reader_pop(pgp_stream_t *stream)
+{
+	pgp_reader_t *next = stream->readinfo.next;
+
+	if (stream->readinfo.accumulate && stream->readinfo.asize > 0) {
+        free(stream->readinfo.accumulated);
+    }
+
+	stream->readinfo = *next;
+	free(next);
+}
+
+/**
+ * \ingroup Internal_Readers_Generic
+ * \brief Gets arg from reader
+ * \param readinfo Reader info
+ * \return Pointer to reader info's arg
+ */
+void           *
+pgp_reader_get_arg(pgp_reader_t *readinfo)
+{
+	return readinfo->arg;
+}
+
+/**************************************************************************/
+
+#define CRC24_POLY 0x1864cfbL
+
+enum {
+	NONE = 0,
+	BEGIN_PGP_MESSAGE,
+	BEGIN_PGP_PUBLIC_KEY_BLOCK,
+	BEGIN_PGP_PRIVATE_KEY_BLOCK,
+	BEGIN_PGP_MULTI,
+	BEGIN_PGP_SIGNATURE,
+
+	END_PGP_MESSAGE,
+	END_PGP_PUBLIC_KEY_BLOCK,
+	END_PGP_PRIVATE_KEY_BLOCK,
+	END_PGP_MULTI,
+	END_PGP_SIGNATURE,
+
+	BEGIN_PGP_SIGNED_MESSAGE
+};
+
+/**
+ * \struct dearmour_t
+ */
+typedef struct {
+	enum {
+		OUTSIDE_BLOCK = 0,
+		BASE64,
+		AT_TRAILER_NAME
+	} state;
+	int		lastseen;
+	pgp_stream_t *parse_info;
+	unsigned	seen_nl:1;
+	unsigned	prev_nl:1;
+	unsigned	allow_headers_without_gap:1;
+			/* !< allow headers in armoured data that are
+			* not separated from the data by a blank line
+			* */
+	unsigned	allow_no_gap:1;
+			/* !< allow no blank line at the start of
+			* armoured data */
+	unsigned	allow_trailing_whitespace:1;
+			/* !< allow armoured stuff to have trailing
+			* whitespace where we wouldn't strictly expect
+			* it */
+	/* it is an error to get a cleartext message without a sig */
+	unsigned   	expect_sig:1;
+	unsigned   	got_sig:1;
+	/* base64 stuff */
+	unsigned        buffered;
+	uint8_t		buffer[3];
+	unsigned	eof64;
+	uint32_t   checksum;
+	uint32_t   read_checksum;
+	/* unarmoured text blocks */
+	uint8_t   unarmoured[NETPGP_BUFSIZ];
+	size_t          unarmoredc;
+	/* pushed back data (stored backwards) */
+	uint8_t  *pushback;
+	unsigned        pushbackc;
+	/* armoured block headers */
+	pgp_headers_t	headers;
+} dearmour_t;
+
+static void
+push_back(dearmour_t *dearmour, const uint8_t *buf,
+	  unsigned length)
+{
+	unsigned        n;
+
+	if (dearmour->pushback) {
+		(void) fprintf(stderr, "push_back: already pushed back\n");
+	} else if ((dearmour->pushback = calloc(1, length)) == NULL) {
+		(void) fprintf(stderr, "push_back: bad alloc\n");
+	} else {
+		for (n = 0; n < length; ++n) {
+			dearmour->pushback[n] = buf[(length - n) - 1];
+		}
+		dearmour->pushbackc = length;
+	}
+}
+
+/* this struct holds a textual header line */
+typedef struct headerline_t {
+	const char	*s;		/* the header line */
+	size_t		 len;		/* its length */
+	int		 type;		/* the defined type */
+} headerline_t;
+
+static headerline_t	headerlines[] = {
+	{ "BEGIN PGP MESSAGE",		17, BEGIN_PGP_MESSAGE },
+	{ "BEGIN PGP PUBLIC KEY BLOCK",	26, BEGIN_PGP_PUBLIC_KEY_BLOCK },
+	{ "BEGIN PGP PRIVATE KEY BLOCK",27, BEGIN_PGP_PRIVATE_KEY_BLOCK },
+	{ "BEGIN PGP MESSAGE, PART ",	25, BEGIN_PGP_MULTI },
+	{ "BEGIN PGP SIGNATURE",	19, BEGIN_PGP_SIGNATURE },
+
+	{ "END PGP MESSAGE",		15, END_PGP_MESSAGE },
+	{ "END PGP PUBLIC KEY BLOCK",	24, END_PGP_PUBLIC_KEY_BLOCK },
+	{ "END PGP PRIVATE KEY BLOCK",	25, END_PGP_PRIVATE_KEY_BLOCK },
+	{ "END PGP MESSAGE, PART ",	22, END_PGP_MULTI },
+	{ "END PGP SIGNATURE",		17, END_PGP_SIGNATURE },
+
+	{ "BEGIN PGP SIGNED MESSAGE",	24, BEGIN_PGP_SIGNED_MESSAGE },
+
+	{ NULL,				0, -1	}
+};
+
+/* search through the table of header lines */
+static int
+findheaderline(char *headerline)
+{
+	headerline_t	*hp;
+
+	for (hp = headerlines ; hp->s ; hp++) {
+		if (strncmp(headerline, hp->s, hp->len) == 0) {
+			break;
+		}
+	}
+	return hp->type;
+}
+
+static int
+set_lastseen_headerline(dearmour_t *dearmour, char *hdr, pgp_error_t **errors)
+{
+	int	lastseen;
+	int	prev;
+
+	prev = dearmour->lastseen;
+	if ((lastseen = findheaderline(hdr)) == -1) {
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			"Unrecognised Header Line %s", hdr);
+		return 0;
+	}
+	dearmour->lastseen = lastseen;
+	if (pgp_get_debug_level(__FILE__)) {
+		printf("set header: hdr=%s, dearmour->lastseen=%d, prev=%d\n",
+			hdr, dearmour->lastseen, prev);
+	}
+	switch (dearmour->lastseen) {
+	case NONE:
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			"Unrecognised last seen Header Line %s", hdr);
+		break;
+
+	case END_PGP_MESSAGE:
+		if (prev != BEGIN_PGP_MESSAGE) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+				"Got END PGP MESSAGE, but not after BEGIN");
+		}
+		break;
+
+	case END_PGP_PUBLIC_KEY_BLOCK:
+		if (prev != BEGIN_PGP_PUBLIC_KEY_BLOCK) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			"Got END PGP PUBLIC KEY BLOCK, but not after BEGIN");
+		}
+		break;
+
+	case END_PGP_PRIVATE_KEY_BLOCK:
+		if (prev != BEGIN_PGP_PRIVATE_KEY_BLOCK) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			"Got END PGP PRIVATE KEY BLOCK, but not after BEGIN");
+		}
+		break;
+
+	case BEGIN_PGP_MULTI:
+	case END_PGP_MULTI:
+		PGP_ERROR_1(errors, PGP_E_R_UNSUPPORTED, "%s",
+			"Multi-part messages are not yet supported");
+		break;
+
+	case END_PGP_SIGNATURE:
+		if (prev != BEGIN_PGP_SIGNATURE) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			"Got END PGP SIGNATURE, but not after BEGIN");
+		}
+		break;
+
+	case BEGIN_PGP_MESSAGE:
+	case BEGIN_PGP_PUBLIC_KEY_BLOCK:
+	case BEGIN_PGP_PRIVATE_KEY_BLOCK:
+	case BEGIN_PGP_SIGNATURE:
+	case BEGIN_PGP_SIGNED_MESSAGE:
+		break;
+	}
+	return 1;
+}
+
+static int
+read_char(pgp_stream_t *stream, dearmour_t *dearmour,
+		pgp_error_t **errors,
+		pgp_reader_t *readinfo,
+		pgp_cbdata_t *cbinfo,
+		unsigned skip)
+{
+	uint8_t   c;
+
+	do {
+		if (dearmour->pushbackc) {
+            if (!dearmour->pushback) {
+                return -1;
+            }
+			c = dearmour->pushback[--dearmour->pushbackc];
+			if (dearmour->pushbackc == 0) {
+				free(dearmour->pushback);
+				dearmour->pushback = NULL;
+			}
+		} else if (pgp_stacked_read(stream, &c, 1, errors, readinfo,
+					cbinfo) != 1) {
+			return -1;
+		}
+	} while (skip && c == '\r');
+	dearmour->prev_nl = dearmour->seen_nl;
+	dearmour->seen_nl = c == '\n';
+	return c;
+}
+
+static int
+eat_whitespace(pgp_stream_t *stream, int first,
+	       dearmour_t *dearmour,
+	       pgp_error_t **errors,
+	       pgp_reader_t *readinfo,
+	       pgp_cbdata_t *cbinfo,
+	       unsigned skip)
+{
+	int             c = first;
+
+	while (c == ' ' || c == '\t') {
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, skip);
+	}
+	return c;
+}
+
+static int
+read_and_eat_whitespace(pgp_stream_t *stream, dearmour_t *dearmour,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo,
+			unsigned skip)
+{
+	int             c;
+
+	do {
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, skip);
+	} while (c == ' ' || c == '\t');
+	return c;
+}
+
+static void
+flush(dearmour_t *dearmour, pgp_cbdata_t *cbinfo)
+{
+	pgp_packet_t	content;
+
+	if (dearmour->unarmoredc > 0) {
+		content.u.unarmoured_text.data = dearmour->unarmoured;
+		content.u.unarmoured_text.length = (unsigned)dearmour->unarmoredc;
+		CALLBACK(PGP_PTAG_CT_UNARMOURED_TEXT, cbinfo, &content);
+		dearmour->unarmoredc = 0;
+	}
+}
+
+static int
+unarmoured_read_char(pgp_stream_t *stream, dearmour_t *dearmour,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo,
+			unsigned skip)
+{
+	int             c;
+
+	do {
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 0);
+		if (c < 0) {
+			return c;
+		}
+		dearmour->unarmoured[dearmour->unarmoredc++] = c;
+		if (dearmour->unarmoredc == sizeof(dearmour->unarmoured)) {
+			flush(dearmour, cbinfo);
+		}
+	} while (skip && c == '\r');
+	return c;
+}
+
+/**
+ * \param headers
+ * \param key
+ *
+ * \return header value if found, otherwise NULL
+ */
+static const char *
+find_header(pgp_headers_t *headers, const char *key)
+{
+	unsigned        n;
+
+	for (n = 0; n < headers->headerc; ++n) {
+		if (strcmp(headers->headers[n].key, key) == 0) {
+			return headers->headers[n].value;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * \param dest
+ * \param src
+ */
+static void
+dup_headers(pgp_headers_t *dest, const pgp_headers_t *src)
+{
+	unsigned        n;
+
+	if ((dest->headers = calloc(src->headerc, sizeof(*dest->headers))) == NULL) {
+		(void) fprintf(stderr, "dup_headers: bad alloc\n");
+	} else {
+		dest->headerc = src->headerc;
+		for (n = 0; n < src->headerc; ++n) {
+			dest->headers[n].key = netpgp_strdup(src->headers[n].key);
+			dest->headers[n].value = netpgp_strdup(src->headers[n].value);
+		}
+	}
+}
+
+/*
+ * Note that this skips CRs so implementations always see just straight LFs
+ * as line terminators
+ */
+static int
+process_dash_escaped(pgp_stream_t *stream, dearmour_t *dearmour,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo)
+{
+	pgp_fixed_body_t	*body;
+	pgp_packet_t		 content2;
+	pgp_packet_t		 content;
+	const char		*hashstr;
+	pgp_hash_t		*hash;
+	int			 total;
+
+	body = &content.u.cleartext_body;
+	if ((hash = calloc(1, sizeof(*hash))) == NULL) {
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			"process_dash_escaped: bad alloc");
+		return -1;
+	}
+	hashstr = find_header(&dearmour->headers, "Hash");
+	if (hashstr) {
+		pgp_hash_alg_t alg;
+
+		alg = pgp_str_to_hash_alg(hashstr);
+		if (!pgp_is_hash_alg_supported(&alg)) {
+			free(hash);
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+				"Unsupported hash algorithm '%s'", hashstr);
+			return -1;
+		}
+		if (alg == PGP_HASH_UNKNOWN) {
+			free(hash);
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+				"Unknown hash algorithm '%s'", hashstr);
+			return -1;
+		}
+		pgp_hash_any(hash, alg);
+	} else {
+		pgp_hash_md5(hash);
+	}
+
+	if (!hash->init(hash)) {
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			"can't initialise hash");
+		return -1;
+	}
+
+	body->length = 0;
+	total = 0;
+	for (;;) {
+		int             c;
+		unsigned        count;
+
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 1);
+		if (c < 0) {
+			return -1;
+		}
+		if (dearmour->prev_nl && c == '-') {
+			if ((c = read_char(stream, dearmour, errors, readinfo, cbinfo,
+						0)) < 0) {
+				return -1;
+			}
+			if (c != ' ') {
+				/* then this had better be a trailer! */
+				if (c != '-') {
+					PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+					    "%s", "Bad dash-escaping");
+				}
+				for (count = 2; count < 5; ++count) {
+					if ((c = read_char(stream, dearmour, errors,
+						readinfo, cbinfo, 0)) < 0) {
+						return -1;
+					}
+					if (c != '-') {
+						PGP_ERROR_1(errors,
+						PGP_E_R_BAD_FORMAT, "%s",
+						"Bad dash-escaping (2)");
+					}
+				}
+				dearmour->state = AT_TRAILER_NAME;
+				break;
+			}
+			/* otherwise we read the next character */
+			if ((c = read_char(stream, dearmour, errors, readinfo, cbinfo,
+						0)) < 0) {
+				return -1;
+			}
+		}
+		if (c == '\n' && body->length) {
+			if (memchr(body->data + 1, '\n', body->length - 1)
+						!= NULL) {
+				(void) fprintf(stderr,
+				"process_dash_escaped: newline found\n");
+				return -1;
+			}
+			if (body->data[0] == '\n') {
+				hash->add(hash, (const uint8_t *)"\r", 1);
+			}
+			hash->add(hash, body->data, body->length);
+			if (pgp_get_debug_level(__FILE__)) {
+				fprintf(stderr, "Got body:\n%s\n", body->data);
+			}
+			CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo,
+						&content);
+			body->length = 0;
+		}
+		body->data[body->length++] = c;
+		total += 1;
+		if (body->length == sizeof(body->data)) {
+			if (pgp_get_debug_level(__FILE__)) {
+				(void) fprintf(stderr, "Got body (2):\n%s\n",
+						body->data);
+			}
+			CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo,
+					&content);
+			body->length = 0;
+		}
+	}
+	if (body->data[0] != '\n') {
+		(void) fprintf(stderr,
+			"process_dash_escaped: no newline in body data\n");
+		return -1;
+	}
+	if (body->length != 1) {
+		(void) fprintf(stderr,
+			"process_dash_escaped: bad body length\n");
+		return -1;
+	}
+	/* don't send that one character, because it's part of the trailer */
+	(void) memset(&content2, 0x0, sizeof(content2));
+	CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, cbinfo, &content2);
+	return total;
+}
+
+static int
+add_header(dearmour_t *dearmour, const char *key, const char *value)
+{
+	int	n;
+
+	/*
+         * Check that the header is valid
+         */
+	if (strcmp(key, "Version") == 0 ||
+	    strcmp(key, "Comment") == 0 ||
+	    strcmp(key, "MessageID") == 0 ||
+	    strcmp(key, "Hash") == 0 ||
+	    strcmp(key, "Charset") == 0) {
+		n = dearmour->headers.headerc;
+		dearmour->headers.headers = realloc(dearmour->headers.headers,
+				(n + 1) * sizeof(*dearmour->headers.headers));
+		if (dearmour->headers.headers == NULL) {
+			(void) fprintf(stderr, "add_header: bad alloc\n");
+			return 0;
+		}
+		dearmour->headers.headers[n].key = netpgp_strdup(key);
+		dearmour->headers.headers[n].value = netpgp_strdup(value);
+		dearmour->headers.headerc = n + 1;
+		return 1;
+	}
+	return 0;
+}
+
+/* \todo what does a return value of 0 indicate? 1 is good, -1 is bad */
+static int
+parse_headers(pgp_stream_t *stream, dearmour_t *dearmour, pgp_error_t **errors,
+	      pgp_reader_t * readinfo, pgp_cbdata_t * cbinfo)
+{
+	unsigned        nbuf;
+	unsigned        size;
+	unsigned	first = 1;
+	char           *buf;
+	int             ret = 1;
+
+	nbuf = 0;
+	size = 80;
+	if ((buf = calloc(1, size)) == NULL) {
+		(void) fprintf(stderr, "parse_headers: bad calloc\n");
+		return -1;
+	}
+	for (;;) {
+		int             c;
+
+		if ((c = read_char(stream, dearmour, errors, readinfo, cbinfo, 1)) < 0) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Unexpected EOF");
+			ret = -1;
+			break;
+		}
+		if (c == '\n') {
+			char           *s;
+
+			if (nbuf == 0) {
+				break;
+			}
+
+			if (nbuf >= size) {
+				(void) fprintf(stderr,
+					"parse_headers: bad size\n");
+				return -1;
+			}
+			buf[nbuf] = '\0';
+
+			if ((s = strchr(buf, ':')) == NULL) {
+				if (!first && !dearmour->allow_headers_without_gap) {
+					/*
+					 * then we have seriously malformed
+					 * armour
+					 */
+					PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+					    "%s", "No colon in armour header");
+					ret = -1;
+					break;
+				} else {
+					if (first &&
+					    !(dearmour->allow_headers_without_gap || dearmour->allow_no_gap)) {
+						PGP_ERROR_1(errors,
+						    PGP_E_R_BAD_FORMAT,
+						    "%s", "No colon in"
+						    " armour header (2)");
+						/*
+						 * then we have a nasty
+						 * armoured block with no
+						 * headers, not even a blank
+						 * line.
+						 */
+						buf[nbuf] = '\n';
+						push_back(dearmour, (uint8_t *) buf, nbuf + 1);
+						ret = -1;
+						break;
+					}
+				}
+			} else {
+				*s = '\0';
+				if (s[1] != ' ') {
+					PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+					    "%s", "No space in armour header");
+					ret = -1;
+					goto end;
+				}
+				if (!add_header(dearmour, buf, s + 2)) {
+					PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "Invalid header %s", buf);
+					ret = -1;
+					goto end;
+				}
+				nbuf = 0;
+			}
+			first = 0;
+		} else {
+			if (size <= nbuf + 1) {
+				size += size + 80;
+				buf = realloc(buf, size);
+				if (buf == NULL) {
+					(void) fprintf(stderr, "bad alloc\n");
+					ret = -1;
+					goto end;
+				}
+			}
+			buf[nbuf++] = c;
+		}
+	}
+
+end:
+	free(buf);
+
+	return ret;
+}
+
+static int
+read4(pgp_stream_t *stream, dearmour_t *dearmour, pgp_error_t **errors,
+      pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo,
+      int *pc, unsigned *pn, uint32_t *pl)
+{
+	int  n = 0, c = 0;
+	uint32_t  l = 0;
+
+	for (n = 0; n < 4; ++n) {
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 1);
+		if (c < 0) {
+			dearmour->eof64 = 1;
+			return -1;
+		}
+		if (c == '-' || c == '=') {
+			break;
+		}
+		l <<= 6;
+		if (c >= 'A' && c <= 'Z') {
+			l += (uint32_t)(c - 'A');
+		} else if (c >= 'a' && c <= 'z') {
+			l += (uint32_t)(c - 'a') + 26;
+		} else if (c >= '0' && c <= '9') {
+			l += (uint32_t)(c - '0') + 52;
+		} else if (c == '+') {
+			l += 62;
+		} else if (c == '/') {
+			l += 63;
+		} else {
+			--n;
+			l >>= 6;
+		}
+	}
+
+	*pc = c;
+	*pn = n;
+	*pl = l;
+
+	return 4;
+}
+
+unsigned
+pgp_crc24(unsigned checksum, uint8_t c)
+{
+	unsigned        i;
+
+	checksum ^= c << 16;
+	for (i = 0; i < 8; i++) {
+		checksum <<= 1;
+		if (checksum & 0x1000000)
+			checksum ^= CRC24_POLY;
+	}
+	return (unsigned)(checksum & 0xffffffL);
+}
+
+static int
+decode64(pgp_stream_t *stream, dearmour_t *dearmour, pgp_error_t **errors,
+	 pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	unsigned        n;
+	int             n2;
+	uint32_t	l;
+	int             c;
+	int             ret;
+
+	if (dearmour->buffered) {
+		(void) fprintf(stderr, "decode64: bad dearmour->buffered\n");
+		return 0;
+	}
+
+	ret = read4(stream, dearmour, errors, readinfo, cbinfo, &c, &n, &l);
+	if (ret < 0) {
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+		    "Badly formed base64");
+		return 0;
+	}
+	if (n == 3) {
+		if (c != '=') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Badly terminated base64 (2)");
+			return 0;
+		}
+		dearmour->buffered = 2;
+		dearmour->eof64 = 1;
+		l >>= 2;
+	} else if (n == 2) {
+		if (c != '=') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Badly terminated base64 (3)");
+			return 0;
+		}
+		dearmour->buffered = 1;
+		dearmour->eof64 = 1;
+		l >>= 4;
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 0);
+		if (c != '=') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Badly terminated base64");
+			return 0;
+		}
+	} else if (n == 0) {
+		if (!dearmour->prev_nl || c != '=') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Badly terminated base64 (4)");
+			return 0;
+		}
+		dearmour->buffered = 0;
+	} else {
+		if (n != 4) {
+			(void) fprintf(stderr,
+				"decode64: bad n (!= 4)\n");
+			return 0;
+		}
+		dearmour->buffered = 3;
+		if (c == '-' || c == '=') {
+			(void) fprintf(stderr, "decode64: bad c\n");
+			return 0;
+		}
+	}
+
+	if (dearmour->buffered < 3 && dearmour->buffered > 0) {
+		/* then we saw padding */
+		if (c != '=') {
+			(void) fprintf(stderr, "decode64: bad c (=)\n");
+			return 0;
+		}
+		c = read_and_eat_whitespace(stream, dearmour, errors, readinfo, cbinfo,
+				1);
+		if (c != '\n') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "No newline at base64 end");
+			return 0;
+		}
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 0);
+		if (c != '=') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "No checksum at base64 end");
+			return 0;
+		}
+	}
+	if (c == '=') {
+		/* now we are at the checksum */
+		ret = read4(stream, dearmour, errors, readinfo, cbinfo, &c, &n,
+				&dearmour->read_checksum);
+		if (ret < 0 || n != 4) {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Error in checksum");
+			return 0;
+		}
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 1);
+		if (dearmour->allow_trailing_whitespace)
+			c = eat_whitespace(stream, c, dearmour, errors, readinfo, cbinfo,
+					1);
+		if (c != '\n') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Badly terminated checksum");
+			return 0;
+		}
+		c = read_char(stream, dearmour, errors, readinfo, cbinfo, 0);
+		if (c != '-') {
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+			    "%s", "Bad base64 trailer (2)");
+			return 0;
+		}
+	}
+	if (c == '-') {
+		for (n = 0; n < 4; ++n)
+			if (read_char(stream, dearmour, errors, readinfo, cbinfo,
+						0) != '-') {
+				PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+				    "Bad base64 trailer");
+				return 0;
+			}
+		dearmour->eof64 = 1;
+	} else {
+		if (!dearmour->buffered) {
+			(void) fprintf(stderr, "decode64: not buffered\n");
+			return 0;
+		}
+	}
+
+	for (n = 0; n < dearmour->buffered; ++n) {
+		dearmour->buffer[n] = (uint8_t)l;
+		l >>= 8;
+	}
+
+	for (n2 = dearmour->buffered - 1; n2 >= 0; --n2)
+		dearmour->checksum = pgp_crc24((unsigned)dearmour->checksum,
+					dearmour->buffer[n2]);
+
+	if (dearmour->eof64 && dearmour->read_checksum != dearmour->checksum) {
+		PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+		    "Checksum mismatch");
+		return 0;
+	}
+	return 1;
+}
+
+static void
+base64(dearmour_t *dearmour)
+{
+	dearmour->state = BASE64;
+	dearmour->checksum = CRC24_INIT;
+	dearmour->eof64 = 0;
+	dearmour->buffered = 0;
+}
+
+/* This reader is rather strange in that it can generate callbacks for */
+/* content - this is because plaintext is not encapsulated in PGP */
+/* packets... it also calls back for the text between the blocks. */
+
+static int
+armoured_data_reader(pgp_stream_t *stream, void *dest_, size_t length, pgp_error_t **errors,
+		     pgp_reader_t *readinfo,
+		     pgp_cbdata_t *cbinfo)
+{
+	pgp_packet_t	 content;
+	dearmour_t	*dearmour;
+	unsigned	 first;
+	uint8_t		*dest = dest_;
+	char		 buf[1024];
+	int		 saved;
+	int              ret;
+
+	dearmour = pgp_reader_get_arg(readinfo);
+	saved = (int)length;
+	if (dearmour->eof64 && !dearmour->buffered) {
+		if (dearmour->state != OUTSIDE_BLOCK &&
+		    dearmour->state != AT_TRAILER_NAME) {
+			(void) fprintf(stderr,
+				"armoured_data_reader: bad dearmour state\n");
+			return 0;
+		}
+	}
+
+	while (length > 0) {
+		unsigned        count;
+		unsigned        n;
+		int             c;
+
+		flush(dearmour, cbinfo);
+		switch (dearmour->state) {
+		case OUTSIDE_BLOCK:
+			/*
+			 * This code returns EOF rather than EARLY_EOF
+			 * because if we don't see a header line at all, then
+			 * it is just an EOF (and not a BLOCK_END)
+			 */
+			while (!dearmour->seen_nl) {
+				if ((c = unarmoured_read_char(stream, dearmour, errors,
+						readinfo, cbinfo, 1)) < 0) {
+					return 0;
+				}
+			}
+
+			/*
+			 * flush at this point so we definitely have room for
+			 * the header, and so we can easily erase it from the
+			 * buffer
+			 */
+			flush(dearmour, cbinfo);
+			/* Find and consume the 5 leading '-' */
+			for (count = 0; count < 5; ++count) {
+				if ((c = unarmoured_read_char(stream, dearmour, errors,
+						readinfo, cbinfo, 0)) < 0) {
+					return 0;
+				}
+				if (c != '-') {
+					goto reloop;
+				}
+			}
+
+			/* Now find the block type */
+			for (n = 0; n < sizeof(buf) - 1;) {
+				if ((c = unarmoured_read_char(stream, dearmour, errors,
+						readinfo, cbinfo, 0)) < 0) {
+					return 0;
+				}
+				if (c == '-') {
+					goto got_minus;
+				}
+				buf[n++] = c;
+			}
+			/* then I guess this wasn't a proper header */
+			break;
+
+got_minus:
+			buf[n] = '\0';
+
+			/* Consume trailing '-' */
+			for (count = 1; count < 5; ++count) {
+				if ((c = unarmoured_read_char(stream, dearmour, errors,
+						readinfo, cbinfo, 0)) < 0) {
+					return 0;
+				}
+				if (c != '-') {
+					/* wasn't a header after all */
+					goto reloop;
+				}
+			}
+
+			/* Consume final NL */
+			if ((c = unarmoured_read_char(stream, dearmour, errors, readinfo,
+						cbinfo, 1)) < 0) {
+				return 0;
+			}
+			if (dearmour->allow_trailing_whitespace) {
+				if ((c = eat_whitespace(stream, c, dearmour, errors,
+						readinfo, cbinfo, 1)) < 0) {
+					return 0;
+				}
+			}
+			if (c != '\n') {
+				/* wasn't a header line after all */
+				break;
+			}
+
+			/*
+			 * Now we've seen the header, scrub it from the
+			 * buffer
+			 */
+			dearmour->unarmoredc = 0;
+
+			/*
+			 * But now we've seen a header line, then errors are
+			 * EARLY_EOF
+			 */
+			if ((ret = parse_headers(stream, dearmour, errors, readinfo,
+					cbinfo)) <= 0) {
+				return -1;
+			}
+
+			if (!set_lastseen_headerline(dearmour, buf, errors)) {
+				return -1;
+			}
+
+			if (strcmp(buf, "BEGIN PGP SIGNED MESSAGE") == 0) {
+				dup_headers(&content.u.cleartext_head,
+					&dearmour->headers);
+				CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER,
+					cbinfo,
+					&content);
+				ret = process_dash_escaped(stream, dearmour, errors,
+						readinfo, cbinfo);
+				if (ret <= 0) {
+					return ret;
+				}
+			} else {
+				content.u.armour_header.type = buf;
+				content.u.armour_header.headers =
+						dearmour->headers;
+				(void) memset(&dearmour->headers, 0x0,
+						sizeof(dearmour->headers));
+				CALLBACK(PGP_PTAG_CT_ARMOUR_HEADER, cbinfo,
+						&content);
+				base64(dearmour);
+			}
+			break;
+
+		case BASE64:
+			first = 1;
+			while (length > 0) {
+				if (!dearmour->buffered) {
+					if (!dearmour->eof64) {
+						ret = decode64(stream, dearmour,
+							errors, readinfo, cbinfo);
+						if (ret <= 0) {
+							return ret;
+						}
+					}
+					if (!dearmour->buffered) {
+						if (!dearmour->eof64) {
+							(void) fprintf(stderr,
+"armoured_data_reader: bad dearmour eof64\n");
+							return 0;
+						}
+						if (first) {
+							dearmour->state =
+								AT_TRAILER_NAME;
+							goto reloop;
+						}
+						return -1;
+					}
+				}
+				if (!dearmour->buffered) {
+					(void) fprintf(stderr,
+			"armoured_data_reader: bad dearmour buffered\n");
+					return 0;
+				}
+				*dest = dearmour->buffer[--dearmour->buffered];
+				++dest;
+				--length;
+				first = 0;
+			}
+			if (dearmour->eof64 && !dearmour->buffered) {
+				dearmour->state = AT_TRAILER_NAME;
+			}
+			break;
+
+		case AT_TRAILER_NAME:
+			for (n = 0; n < sizeof(buf) - 1;) {
+				if ((c = read_char(stream, dearmour, errors, readinfo,
+						cbinfo, 0)) < 0) {
+					return -1;
+				}
+				if (c == '-') {
+					goto got_minus2;
+				}
+				buf[n++] = c;
+			}
+			/* then I guess this wasn't a proper trailer */
+			PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT, "%s",
+			    "Bad ASCII armour trailer");
+			break;
+
+got_minus2:
+			buf[n] = '\0';
+
+			if (!set_lastseen_headerline(dearmour, buf, errors)) {
+				return -1;
+			}
+
+			/* Consume trailing '-' */
+			for (count = 1; count < 5; ++count) {
+				if ((c = read_char(stream, dearmour, errors, readinfo,
+						cbinfo, 0)) < 0) {
+					return -1;
+				}
+				if (c != '-') {
+					/* wasn't a trailer after all */
+					PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+					    "%s",
+					    "Bad ASCII armour trailer (2)");
+				}
+			}
+
+			/* Consume final NL */
+			if ((c = read_char(stream, dearmour, errors, readinfo, cbinfo,
+						1)) < 0) {
+				return -1;
+			}
+			if (dearmour->allow_trailing_whitespace) {
+				if ((c = eat_whitespace(stream, c, dearmour, errors,
+						readinfo, cbinfo, 1)) < 0) {
+					return 0;
+				}
+			}
+			if (c != '\n') {
+				/* wasn't a trailer line after all */
+				PGP_ERROR_1(errors, PGP_E_R_BAD_FORMAT,
+				    "%s", "Bad ASCII armour trailer (3)");
+			}
+
+			if (strncmp(buf, "BEGIN ", 6) == 0) {
+				if (!set_lastseen_headerline(dearmour, buf,
+						errors)) {
+					return -1;
+				}
+				if ((ret = parse_headers(stream, dearmour, errors,
+						readinfo, cbinfo)) <= 0) {
+					return ret;
+				}
+				content.u.armour_header.type = buf;
+				content.u.armour_header.headers =
+						dearmour->headers;
+				(void) memset(&dearmour->headers, 0x0,
+						sizeof(dearmour->headers));
+				CALLBACK(PGP_PTAG_CT_ARMOUR_HEADER, cbinfo,
+						&content);
+				base64(dearmour);
+			} else {
+				content.u.armour_trailer = buf;
+				CALLBACK(PGP_PTAG_CT_ARMOUR_TRAILER, cbinfo,
+						&content);
+				dearmour->state = OUTSIDE_BLOCK;
+			}
+			break;
+		}
+reloop:
+		continue;
+	}
+
+	return saved;
+}
+
+static void
+armoured_data_destroyer(pgp_reader_t *readinfo)
+{
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/**
+ * \ingroup Core_Readers_Armour
+ * \brief Pushes dearmouring reader onto stack
+ * \param parse_info Usual structure containing information about to how to do the parse
+ * \sa pgp_reader_pop_dearmour()
+ */
+void
+pgp_reader_push_dearmour(pgp_stream_t *parse_info)
+/*
+ * This function originally had these params to cater for packets which
+ * didn't strictly match the RFC. The initial 0.5 release is only going to
+ * support strict checking. If it becomes desirable to support loose checking
+ * of armoured packets and these params are reinstated, parse_headers() must
+ * be fixed so that these flags work correctly.
+ *
+ * // Allow headers in armoured data that are not separated from the data by a
+ * blank line unsigned without_gap,
+ *
+ * // Allow no blank line at the start of armoured data unsigned no_gap,
+ *
+ * //Allow armoured data to have trailing whitespace where we strictly would not
+ * expect it			      unsigned trailing_whitespace
+ */
+{
+	dearmour_t *dearmour;
+
+	if ((dearmour = calloc(1, sizeof(*dearmour))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_push_dearmour: bad alloc\n");
+	} else {
+		dearmour->seen_nl = 1;
+		/*
+		    dearmour->allow_headers_without_gap=without_gap;
+		    dearmour->allow_no_gap=no_gap;
+		    dearmour->allow_trailing_whitespace=trailing_whitespace;
+		*/
+		dearmour->expect_sig = 0;
+		dearmour->got_sig = 0;
+
+		pgp_reader_push(parse_info, armoured_data_reader,
+			armoured_data_destroyer, dearmour);
+	}
+}
+
+/**
+ * \ingroup Core_Readers_Armour
+ * \brief Pops dearmour reader from stock
+ * \param stream
+ * \sa pgp_reader_push_dearmour()
+ */
+void
+pgp_reader_pop_dearmour(pgp_stream_t *stream)
+{
+	dearmour_t *dearmour;
+
+	dearmour = pgp_reader_get_arg(pgp_readinfo(stream));
+	free(dearmour);
+	pgp_reader_pop(stream);
+}
+
+/**************************************************************************/
+
+/* this is actually used for *decrypting* */
+typedef struct {
+	uint8_t		 decrypted[1024 * 15];
+	size_t		 c;
+	size_t		 off;
+	pgp_crypt_t	*decrypt;
+	pgp_region_t	*region;
+	// unsigned	 prevplain:1;
+} encrypted_t;
+
+static int
+encrypted_data_reader(pgp_stream_t *stream, void *dest,
+			size_t length,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo)
+{
+	encrypted_t	*encrypted;
+	char		*cdest;
+	int		 saved;
+
+	encrypted = pgp_reader_get_arg(readinfo);
+	saved = (int)length;
+
+#if 0
+	/*
+	 * V3 MPIs have the count plain and the cipher is reset after each
+	 * count
+	 */
+	if (encrypted->prevplain && !readinfo->parent->reading_mpi_len) {
+		if (!readinfo->parent->reading_v3_secret) {
+			(void) fprintf(stderr,
+				"encrypted_data_reader: bad v3 secret\n");
+			return -1;
+		}
+		encrypted->decrypt->decrypt_resync(encrypted->decrypt);
+		encrypted->prevplain = 0;
+	} else if (readinfo->parent->reading_v3_secret &&
+		   readinfo->parent->reading_mpi_len) {
+		encrypted->prevplain = 1;
+	}
+#endif
+
+	while (length > 0) {
+		if (encrypted->c) {
+			unsigned        n;
+
+			/*
+			 * if we are reading v3 we should never read
+			 * more than we're asked for
+			if (length < encrypted->c &&
+			     (readinfo->parent->reading_v3_secret ||
+			      readinfo->parent->exact_read)) {
+				(void) fprintf(stderr,
+					"encrypted_data_reader: bad v3 read\n");
+				return 0;
+			}
+             */
+			n = (int)MIN(length, encrypted->c);
+			(void) memcpy(dest,
+				encrypted->decrypted + encrypted->off, n);
+			encrypted->c -= n;
+			encrypted->off += n;
+			length -= n;
+			cdest = dest;
+			cdest += n;
+			dest = cdest;
+		} else {
+			unsigned	n = encrypted->region->length;
+			uint8_t		buffer[1024];
+
+			if (!n) {
+				return -1;
+			}
+			if (!encrypted->region->indeterminate) {
+				n -= encrypted->region->readc;
+				if (n == 0) {
+					return (int)(saved - length);
+				}
+				if (n > sizeof(buffer)) {
+					n = sizeof(buffer);
+				}
+			} else {
+				n = sizeof(buffer);
+			}
+
+			/*
+			 * we can only read as much as we're asked for
+			 * in v3 keys because they're partially
+			 * unencrypted!
+			if ((readinfo->parent->reading_v3_secret ||
+			     readinfo->parent->exact_read) && n > length) {
+				n = (unsigned)length;
+			}
+            */
+
+			if (!pgp_stacked_limited_read(stream, buffer, n,
+				encrypted->region, errors, readinfo, cbinfo)) {
+				return -1;
+			}
+			//if (!readinfo->parent->reading_v3_secret ||
+			//    !readinfo->parent->reading_mpi_len) {
+				encrypted->c =
+					pgp_decrypt_se_ip(encrypted->decrypt,
+					encrypted->decrypted, buffer, n);
+
+				if (pgp_get_debug_level(__FILE__)) {
+					hexdump(stderr, "encrypted", buffer, 16);
+					hexdump(stderr, "decrypted", encrypted->decrypted, 16);
+				}
+			//} else {
+			//	(void) memcpy(
+                //&encrypted->decrypted[encrypted->off], buffer, n);
+				//encrypted->c = n;
+			//}
+
+			if (encrypted->c == 0) {
+				(void) fprintf(stderr,
+				"encrypted_data_reader: 0 decrypted count\n");
+				return 0;
+			}
+
+			encrypted->off = 0;
+		}
+	}
+
+	return saved;
+}
+
+static void
+encrypted_data_destroyer(pgp_reader_t *readinfo)
+{
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/**
+ * \ingroup Core_Readers_SE
+ * \brief Pushes decryption reader onto stack
+ * \sa pgp_reader_pop_decrypt()
+ */
+void
+pgp_reader_push_decrypt(pgp_stream_t *stream, pgp_crypt_t *decrypt,
+			pgp_region_t *region)
+{
+	encrypted_t	*encrypted;
+
+	if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_push_decrypted: bad alloc\n");
+	} else {
+		encrypted->decrypt = decrypt;
+		encrypted->region = region;
+		pgp_decrypt_init(encrypted->decrypt);
+		pgp_reader_push(stream, encrypted_data_reader,
+			encrypted_data_destroyer, encrypted);
+	}
+}
+
+/**
+ * \ingroup Core_Readers_Encrypted
+ * \brief Pops decryption reader from stack
+ * \sa pgp_reader_push_decrypt()
+ */
+void
+pgp_reader_pop_decrypt(pgp_stream_t *stream)
+{
+	encrypted_t	*encrypted;
+
+	encrypted = pgp_reader_get_arg(pgp_readinfo(stream));
+	encrypted->decrypt->decrypt_finish(encrypted->decrypt);
+	free(encrypted);
+	pgp_reader_pop(stream);
+}
+
+/**************************************************************************/
+
+typedef struct {
+	/* boolean: 0 once we've done the preamble/MDC checks */
+	/* and are reading from the plaintext */
+	int              passed_checks;
+	uint8_t		*plaintext;
+	size_t           plaintext_available;
+	size_t           plaintext_offset;
+	pgp_region_t	*region;
+	pgp_crypt_t	*decrypt;
+} decrypt_se_ip_t;
+
+/*
+  Gets entire SE_IP data packet.
+  Verifies leading preamble
+  Verifies trailing MDC packet
+  Then passes up plaintext as requested
+*/
+static int
+se_ip_data_reader(pgp_stream_t *stream, void *dest_,
+			size_t len,
+			pgp_error_t **errors,
+			pgp_reader_t *readinfo,
+			pgp_cbdata_t *cbinfo)
+{
+	decrypt_se_ip_t	*se_ip;
+	pgp_region_t	 decrypted_region;
+	unsigned	 n = 0;
+
+	se_ip = pgp_reader_get_arg(readinfo);
+	if (!se_ip->passed_checks) {
+		uint8_t		*buf = NULL;
+		uint8_t		hashed[PGP_SHA1_HASH_SIZE];
+		uint8_t		*preamble;
+		uint8_t		*plaintext;
+		uint8_t		*mdc;
+		uint8_t		*mdc_hash;
+		pgp_hash_t	hash;
+		size_t		b;
+		size_t          sz_preamble;
+		size_t          sz_mdc_hash;
+		size_t          sz_mdc;
+		size_t          sz_plaintext;
+
+		pgp_hash_any(&hash, PGP_HASH_SHA1);
+		if (!hash.init(&hash)) {
+			(void) fprintf(stderr,
+				"se_ip_data_reader: can't init hash\n");
+			return -1;
+		}
+
+		pgp_init_subregion(&decrypted_region, NULL);
+		decrypted_region.length =
+			se_ip->region->length - se_ip->region->readc;
+		if ((buf = calloc(1, decrypted_region.length)) == NULL) {
+			(void) fprintf(stderr, "se_ip_data_reader: bad alloc\n");
+			return -1;
+		}
+
+		/* read entire SE IP packet */
+		if (!pgp_stacked_limited_read(stream, buf, decrypted_region.length,
+				&decrypted_region, errors, readinfo, cbinfo)) {
+			free(buf);
+			return -1;
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "SE IP packet", buf, decrypted_region.length);
+		}
+		/* verify leading preamble */
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "preamble", buf, se_ip->decrypt->blocksize);
+		}
+		b = se_ip->decrypt->blocksize;
+		if (buf[b - 2] != buf[b] || buf[b - 1] != buf[b + 1]) {
+			fprintf(stderr,
+			"Bad symmetric decrypt (%02x%02x vs %02x%02x)\n",
+				buf[b - 2], buf[b - 1], buf[b], buf[b + 1]);
+			PGP_ERROR_1(errors, PGP_E_PROTO_BAD_SYMMETRIC_DECRYPT,
+			    "%s", "Bad symmetric decrypt when parsing SE IP"
+			    " packet");
+			free(buf);
+			return -1;
+		}
+		/* Verify trailing MDC hash */
+
+		sz_preamble = se_ip->decrypt->blocksize + 2;
+		sz_mdc_hash = PGP_SHA1_HASH_SIZE;
+		sz_mdc = 1 + 1 + sz_mdc_hash;
+		sz_plaintext = (decrypted_region.length - sz_preamble) - sz_mdc;
+
+		preamble = buf;
+		plaintext = buf + sz_preamble;
+		mdc = plaintext + sz_plaintext;
+		mdc_hash = mdc + 2;
+
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "plaintext", plaintext, sz_plaintext);
+			hexdump(stderr, "mdc", mdc, sz_mdc);
+		}
+		pgp_calc_mdc_hash(preamble, sz_preamble, plaintext,
+				(unsigned)sz_plaintext, hashed);
+
+		if (memcmp(mdc_hash, hashed, PGP_SHA1_HASH_SIZE) != 0) {
+			PGP_ERROR_1(errors, PGP_E_V_BAD_HASH, "%s",
+			    "Bad hash in MDC packet");
+			free(buf);
+			return 0;
+		}
+		/* all done with the checks */
+		/* now can start reading from the plaintext */
+		if (se_ip->plaintext) {
+			(void) fprintf(stderr,
+				"se_ip_data_reader: bad plaintext\n");
+			return 0;
+		}
+		if ((se_ip->plaintext = calloc(1, sz_plaintext)) == NULL) {
+			(void) fprintf(stderr,
+				"se_ip_data_reader: bad alloc\n");
+			return 0;
+		}
+		memcpy(se_ip->plaintext, plaintext, sz_plaintext);
+		se_ip->plaintext_available = sz_plaintext;
+
+		se_ip->passed_checks = 1;
+
+		free(buf);
+	}
+	n = (unsigned)len;
+	if (n > se_ip->plaintext_available) {
+		n = (unsigned)se_ip->plaintext_available;
+	}
+
+	memcpy(dest_, se_ip->plaintext + se_ip->plaintext_offset, n);
+	se_ip->plaintext_available -= n;
+	se_ip->plaintext_offset += n;
+	/* len -= n; - not used at all, for info only */
+
+	return n;
+}
+
+static void
+se_ip_data_destroyer(pgp_reader_t *readinfo)
+{
+	decrypt_se_ip_t	*se_ip;
+
+	se_ip = pgp_reader_get_arg(readinfo);
+	free(se_ip->plaintext);
+	free(se_ip);
+}
+
+/**
+   \ingroup Internal_Readers_SEIP
+*/
+void
+pgp_reader_push_se_ip_data(pgp_stream_t *stream, pgp_crypt_t *decrypt,
+			   pgp_region_t * region)
+{
+	decrypt_se_ip_t *se_ip;
+
+	if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_push_se_ip_data: bad alloc\n");
+	} else {
+		se_ip->region = region;
+		se_ip->decrypt = decrypt;
+		pgp_reader_push(stream, se_ip_data_reader, se_ip_data_destroyer,
+				se_ip);
+	}
+}
+
+/**
+   \ingroup Internal_Readers_SEIP
+ */
+void
+pgp_reader_pop_se_ip_data(pgp_stream_t *stream)
+{
+	/*
+	 * decrypt_se_ip_t
+	 * *se_ip=pgp_reader_get_arg(pgp_readinfo(stream));
+	 */
+	/* free(se_ip); */
+	pgp_reader_pop(stream);
+}
+
+/**************************************************************************/
+
+/** Arguments for reader_fd
+ */
+typedef struct mmap_reader_t {
+	void		*mem;		/* memory mapped file */
+	uint64_t	 size;		/* size of file */
+	uint64_t	 offset;	/* current offset in file */
+	int		 fd;		/* file descriptor */
+} mmap_reader_t;
+
+
+/**
+ * \ingroup Core_Readers
+ *
+ * pgp_reader_fd() attempts to read up to "plength" bytes from the file
+ * descriptor in "parse_info" into the buffer starting at "dest" using the
+ * rules contained in "flags"
+ *
+ * \param	dest	Pointer to previously allocated buffer
+ * \param	plength Number of bytes to try to read
+ * \param	flags	Rules about reading to use
+ * \param	readinfo	Reader info
+ * \param	cbinfo	Callback info
+ *
+ * \return	n	Number of bytes read
+ *
+ * PGP_R_EARLY_EOF and PGP_R_ERROR push errors on the stack
+ */
+static int
+fd_reader(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+	  pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	mmap_reader_t	*reader;
+	int		 n;
+
+	__PGP_USED(cbinfo);
+	reader = pgp_reader_get_arg(readinfo);
+
+    n = (int)read(reader->fd, dest, length);
+
+	if (n == 0) {
+		return 0;
+	}
+	if (n < 0) {
+		PGP_SYSTEM_ERROR_1(errors, PGP_E_R_READ_FAILED, "read",
+				   "file descriptor %d", reader->fd);
+		return -1;
+	}
+	return n;
+}
+
+static void
+reader_fd_destroyer(pgp_reader_t *readinfo)
+{
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/**
+   \ingroup Core_Readers_First
+   \brief Starts stack with file reader
+*/
+
+void
+pgp_reader_set_fd(pgp_stream_t *stream, int fd)
+{
+	mmap_reader_t *reader;
+
+	if ((reader = calloc(1, sizeof(*reader))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_set_fd: bad alloc\n");
+	} else {
+		reader->fd = fd;
+		pgp_reader_set(stream, fd_reader, reader_fd_destroyer, reader);
+	}
+}
+
+/**************************************************************************/
+
+typedef struct {
+	const uint8_t *buffer;
+	size_t          length;
+	size_t          offset;
+} reader_mem_t;
+
+static int
+mem_reader(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+	   pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	reader_mem_t *reader = pgp_reader_get_arg(readinfo);
+	unsigned        n;
+
+	__PGP_USED(cbinfo);
+	__PGP_USED(errors);
+
+    if (reader->offset + length > reader->length) {
+        n = (unsigned)(reader->length - reader->offset);
+    } else {
+        n = (unsigned)length;
+    }
+    if (n == (unsigned)0) {
+        return 0;
+    }
+    memcpy(dest, reader->buffer + reader->offset, n);
+    reader->offset += n;
+
+	return n;
+}
+
+static void
+mem_destroyer(pgp_reader_t *readinfo)
+{
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/**
+   \ingroup Core_Readers_First
+   \brief Starts stack with memory reader
+*/
+
+void
+pgp_reader_set_memory(pgp_stream_t *stream, const void *buffer,
+		      size_t length)
+{
+	reader_mem_t *mem;
+
+	if ((mem = calloc(1, sizeof(*mem))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_set_memory: bad alloc\n");
+	} else {
+		mem->buffer = buffer;
+		mem->length = length;
+		mem->offset = 0;
+		pgp_reader_set(stream, mem_reader, mem_destroyer, mem);
+	}
+}
+
+/**************************************************************************/
+
+/**
+ \ingroup Core_Writers
+ \brief Create and initialise output and mem; Set for writing to mem
+ \param output Address where new output pointer will be set
+ \param mem Address when new mem pointer will be set
+ \param bufsz Initial buffer size (will automatically be increased when necessary)
+ \note It is the caller's responsiblity to free output and mem.
+ \sa pgp_teardown_memory_write()
+*/
+void
+pgp_setup_memory_write(pgp_output_t **output, pgp_memory_t **mem, size_t bufsz)
+{
+	/*
+         * initialise needed structures for writing to memory
+         */
+
+	*output = pgp_output_new();
+	*mem = pgp_memory_new();
+
+	pgp_memory_init(*mem, bufsz);
+
+	pgp_writer_set_memory(*output, *mem);
+}
+
+/**
+   \ingroup Core_Writers
+   \brief Closes writer and frees output and mem
+   \param output
+   \param mem
+   \sa pgp_setup_memory_write()
+*/
+void
+pgp_teardown_memory_write(pgp_output_t *output, pgp_memory_t *mem)
+{
+	pgp_writer_close(output);/* new */
+	pgp_output_delete(output);
+	pgp_memory_free(mem);
+}
+
+/**
+   \ingroup Core_Readers
+   \brief Create parse_info and sets to read from memory
+   \param stream Address where new parse_info will be set
+   \param mem Memory to read from
+   \param arg Reader-specific arg
+   \param callback Callback to use with reader
+   \param accumulate Set if we need to accumulate as we read. (Usually 0 unless doing signature verification)
+   \note It is the caller's responsiblity to free parse_info
+   \sa pgp_teardown_memory_read()
+*/
+void
+pgp_setup_memory_read(pgp_io_t *io,
+			pgp_stream_t **stream,
+			pgp_memory_t *mem,
+			void *vp,
+			pgp_cb_ret_t callback(const pgp_packet_t *,
+						pgp_cbdata_t *),
+			unsigned accumulate)
+{
+	*stream = pgp_new(sizeof(**stream));
+	(*stream)->io = (*stream)->cbinfo.io = io;
+	pgp_set_callback(*stream, callback, vp);
+	pgp_reader_set_memory(*stream,
+			      pgp_mem_data(mem),
+			      pgp_mem_len(mem));
+	if (accumulate) {
+		(*stream)->readinfo.accumulate = 1;
+	}
+}
+
+/**
+   \ingroup Core_Readers
+   \brief Frees stream and mem
+   \param stream
+   \param mem
+   \sa pgp_setup_memory_read()
+*/
+void
+pgp_teardown_memory_read(pgp_stream_t *stream, pgp_memory_t *mem)
+{
+	pgp_stream_delete(stream);
+	pgp_memory_free(mem);
+}
+
+/**
+ \ingroup Core_Writers
+ \brief Create and initialise output and mem; Set for writing to file
+ \param output Address where new output pointer will be set
+ \param filename File to write to
+ \param allow_overwrite Allows file to be overwritten, if set.
+ \return Newly-opened file descriptor
+ \note It is the caller's responsiblity to free output and to close fd.
+ \sa pgp_teardown_file_write()
+*/
+#if 0 //////
+int
+pgp_setup_file_write(pgp_output_t **output, const char *filename,
+			unsigned allow_overwrite)
+{
+	int             fd = 0;
+	int             flags = 0;
+
+	/*
+         * initialise needed structures for writing to file
+         */
+	if (filename == NULL) {
+		/* write to stdout */
+		fd = STDOUT_FILENO;
+	} else {
+		flags = O_WRONLY | O_CREAT;
+		if (allow_overwrite)
+			flags |= O_TRUNC;
+		else
+			flags |= O_EXCL;
+#ifdef O_BINARY
+		flags |= O_BINARY;
+#endif
+		fd = open(filename, flags, 0600);
+		if (fd < 0) {
+			perror(filename);
+			return fd;
+		}
+	}
+	*output = pgp_output_new();
+	pgp_writer_set_fd(*output, fd);
+	return fd;
+}
+#endif //////
+
+/**
+   \ingroup Core_Writers
+   \brief Closes writer, frees info, closes fd
+   \param output
+   \param fd
+*/
+#if 0 //////
+void
+pgp_teardown_file_write(pgp_output_t *output, int fd)
+{
+	pgp_writer_close(output);
+	close(fd);
+	pgp_output_delete(output);
+}
+#endif //////
+
+/**
+   \ingroup Core_Writers
+   \brief As pgp_setup_file_write, but appends to file
+*/
+#if 0 //////
+int
+pgp_setup_file_append(pgp_output_t **output, const char *filename)
+{
+	int	fd;
+
+	/*
+         * initialise needed structures for writing to file
+         */
+#ifdef O_BINARY
+	fd = open(filename, O_WRONLY | O_APPEND | O_BINARY, 0600);
+#else
+	fd = open(filename, O_WRONLY | O_APPEND, 0600);
+#endif
+	if (fd >= 0) {
+		*output = pgp_output_new();
+		pgp_writer_set_fd(*output, fd);
+	}
+	return fd;
+}
+#endif //////
+
+/**
+   \ingroup Core_Writers
+   \brief As pgp_teardown_file_write()
+*/
+#if 0 //////
+void
+pgp_teardown_file_append(pgp_output_t *output, int fd)
+{
+	pgp_teardown_file_write(output, fd);
+}
+#endif //////
+
+/**
+   \ingroup Core_Readers
+   \brief Creates parse_info, opens file, and sets to read from file
+   \param stream Address where new parse_info will be set
+   \param filename Name of file to read
+   \param vp Reader-specific arg
+   \param callback Callback to use when reading
+   \param accumulate Set if we need to accumulate as we read. (Usually 0 unless doing signature verification)
+   \note It is the caller's responsiblity to free parse_info and to close fd
+   \sa pgp_teardown_file_read()
+*/
+#if 0 //////
+int
+pgp_setup_file_read(pgp_io_t *io,
+			pgp_stream_t **stream,
+			const char *filename,
+			void *vp,
+			pgp_cb_ret_t callback(const pgp_packet_t *,
+						pgp_cbdata_t *),
+			unsigned accumulate)
+{
+	int	fd;
+
+#ifdef O_BINARY
+	fd = open(filename, O_RDONLY | O_BINARY);
+#else
+	fd = open(filename, O_RDONLY);
+#endif
+	if (fd < 0) {
+		(void) fprintf(io->errs, "can't open \"%s\"\n", filename);
+		return fd;
+	}
+	*stream = pgp_new(sizeof(**stream));
+	(*stream)->io = (*stream)->cbinfo.io = io;
+	pgp_set_callback(*stream, callback, vp);
+#ifdef USE_MMAP_FOR_FILES
+	pgp_reader_set_mmap(*stream, fd);
+#else
+	pgp_reader_set_fd(*stream, fd);
+#endif
+	if (accumulate) {
+		(*stream)->readinfo.accumulate = 1;
+	}
+	return fd;
+}
+#endif //////
+
+/**
+   \ingroup Core_Readers
+   \brief Frees stream and closes fd
+   \param stream
+   \param fd
+   \sa pgp_setup_file_read()
+*/
+#if 0 //////
+void
+pgp_teardown_file_read(pgp_stream_t *stream, int fd)
+{
+	close(fd);
+	pgp_stream_delete(stream);
+}
+#endif //////
+
+pgp_cb_ret_t
+pgp_litdata_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	*content = &pkt->u;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		printf("pgp_litdata_cb: ");
+		//pgp_print_packet(&cbinfo->printstate, pkt);
+	}
+	/* Read data from packet into static buffer */
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_LITDATA_BODY:
+		/* if writer enabled, use it */
+		if (cbinfo->output) {
+			if (pgp_get_debug_level(__FILE__)) {
+				printf("pgp_litdata_cb: length is %u\n",
+					content->litdata_body.length);
+			}
+			pgp_write(cbinfo->output,
+					content->litdata_body.data,
+					content->litdata_body.length);
+		}
+		break;
+
+	case PGP_PTAG_CT_LITDATA_HEADER:
+		/* ignore */
+		break;
+
+	default:
+		break;
+	}
+
+	return PGP_RELEASE_MEMORY;
+}
+
+pgp_cb_ret_t
+pgp_pk_sesskey_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	*content = &pkt->u;
+	unsigned		 from;
+	pgp_io_t		*io;
+
+	io = cbinfo->io;
+	if (pgp_get_debug_level(__FILE__)) {
+		//pgp_print_packet(&cbinfo->printstate, pkt);
+	}
+	/* Read data from packet into static buffer */
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_PK_SESSION_KEY:
+		if (pgp_get_debug_level(__FILE__)) {
+			printf("PGP_PTAG_CT_PK_SESSION_KEY\n");
+		}
+		if (!cbinfo->cryptinfo.secring) {
+			(void) fprintf(io->errs,
+				"pgp_pk_sesskey_cb: bad keyring\n");
+			return (pgp_cb_ret_t)0;
+		}
+		from = 0;
+		cbinfo->cryptinfo.keydata =
+			pgp_getkeybyid(io, cbinfo->cryptinfo.secring,
+				content->pk_sesskey.key_id, &from, NULL, NULL,
+                0, 0); /* accepts revoked and expired */
+		if (!cbinfo->cryptinfo.keydata) {
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return PGP_RELEASE_MEMORY;
+}
+
+/**
+ \ingroup Core_Callbacks
+
+\brief Callback to get secret key, decrypting if necessary.
+
+@verbatim
+ This callback does the following:
+ * finds the session key in the keyring
+ * gets a passphrase if required
+ * decrypts the secret key, if necessary
+ * sets the seckey in the content struct
+@endverbatim
+*/
+
+pgp_cb_ret_t
+pgp_get_seckey_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	*content = &pkt->u;
+    pgp_seckey_t	*secret;
+	// const pgp_key_t		*pubkey;
+	//const pgp_key_t		*keypair;
+	unsigned		 from;
+	pgp_io_t		*io;
+	//int			 i;
+
+    pgp_cryptinfo_t *cryptinfo = &cbinfo->cryptinfo;
+    key_id_t *key_id;
+
+	io = cbinfo->io;
+	if (pgp_get_debug_level(__FILE__)) {
+		//pgp_print_packet(&cbinfo->printstate, pkt);
+	}
+	switch (pkt->tag) {
+	case PGP_GET_SECKEY:
+		/* print key from pubring */
+		// from = 0;
+		// pubkey = pgp_getkeybyid(io, cbinfo->cryptinfo.pubring,
+		// 		content->get_seckey.pk_sesskey->key_id,
+		// 		&from, NULL);
+		// /* validate key from secring */
+
+        EXPAND_ARRAY(cryptinfo, recipients_key_ids);
+        key_id = &cryptinfo->recipients_key_idss[cryptinfo->recipients_key_idsc++];
+        memcpy(key_id, content->get_seckey.pk_sesskey->key_id, sizeof(key_id_t));
+
+		from = 0;
+		cbinfo->cryptinfo.keydata =
+			pgp_getkeybyid(io, cbinfo->cryptinfo.secring,
+				content->get_seckey.pk_sesskey->key_id,
+				&from, NULL, &secret, 0, 0);
+		if (!cbinfo->cryptinfo.keydata ||
+            !secret ||
+		    !pgp_is_key_secret(cbinfo->cryptinfo.keydata)) {
+			return (pgp_cb_ret_t)0;
+		}
+
+        // FIXME : support encrypted seckeys again
+		// keypair = cbinfo->cryptinfo.keydata;
+        // if (pubkey == NULL) {
+        // 	pubkey = keypair;
+        // }
+		// secret = NULL;
+		// cbinfo->gotpass = 0;
+		// for (i = 0 ; cbinfo->numtries == -1 || i < cbinfo->numtries ; i++) {
+		// 	/* print out the user id */
+		// 	pgp_print_keydata(io, cbinfo->cryptinfo.pubring, pubkey,
+		// 		"signature ", &pubkey->key.pubkey, 0);
+		// 	/* now decrypt key */
+        //     pgp_decrypt_seckey(keypair, cbinfo->passfp);
+		// 	if (secret != NULL) {
+		// 		break;
+		// 	}
+		// 	(void) fprintf(io->errs, "Bad passphrase\n");
+		// }
+		// if (secret == NULL) {
+		// 	(void) fprintf(io->errs, "Exhausted passphrase attempts\n");
+		// 	return (pgp_cb_ret_t)PGP_RELEASE_MEMORY;
+		// }
+
+		cbinfo->gotpass = 1;
+		*content->get_seckey.seckey = secret;
+		break;
+
+	default:
+		break;
+	}
+
+	return PGP_RELEASE_MEMORY;
+}
+
+#if 0 //////
+unsigned
+pgp_reader_set_accumulate(pgp_stream_t *stream, unsigned state)
+{
+	return stream->readinfo.accumulate = state;
+}
+#endif //////
+
+/**************************************************************************/
+
+static int
+hash_reader(pgp_stream_t *stream, void *dest,
+		size_t length,
+		pgp_error_t **errors,
+		pgp_reader_t *readinfo,
+		pgp_cbdata_t *cbinfo)
+{
+	pgp_hash_t	*hash = pgp_reader_get_arg(readinfo);
+	int		 r;
+
+	r = pgp_stacked_read(stream, dest, length, errors, readinfo, cbinfo);
+	if (r <= 0) {
+		return r;
+	}
+	hash->add(hash, dest, (unsigned)r);
+	return r;
+}
+
+/**
+   \ingroup Internal_Readers_Hash
+   \brief Push hashed data reader on stack
+*/
+void
+pgp_reader_push_hash(pgp_stream_t *stream, pgp_hash_t *hash)
+{
+	if (!hash->init(hash)) {
+		(void) fprintf(stderr, "pgp_reader_push_hash: can't init hash\n");
+		/* just continue and die */
+		/* XXX - agc - no way to return failure */
+	}
+	pgp_reader_push(stream, hash_reader, NULL, hash);
+}
+
+/**
+   \ingroup Internal_Readers_Hash
+   \brief Pop hashed data reader from stack
+*/
+void
+pgp_reader_pop_hash(pgp_stream_t *stream)
+{
+	pgp_reader_pop(stream);
+}
+
+/* read memory from the previously mmap-ed file */
+static int
+mmap_reader(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+	  pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
+{
+	mmap_reader_t	*mem = pgp_reader_get_arg(readinfo);
+	unsigned	 n;
+	char		*cmem = mem->mem;
+
+	__PGP_USED(errors);
+	__PGP_USED(cbinfo);
+
+    n = (unsigned)MIN(length, (unsigned)(mem->size - mem->offset));
+    if (n > 0) {
+        (void) memcpy(dest, &cmem[(int)mem->offset], (unsigned)n);
+        mem->offset += n;
+    }
+	return (int)n;
+}
+
+/* tear down the mmap, close the fd */
+static void
+mmap_destroyer(pgp_reader_t *readinfo)
+{
+	mmap_reader_t *mem = pgp_reader_get_arg(readinfo);
+
+	(void) munmap(mem->mem, (unsigned)mem->size);
+	(void) close(mem->fd);
+	free(pgp_reader_get_arg(readinfo));
+}
+
+/* set up the file to use mmap-ed memory if available, file IO otherwise */
+void
+pgp_reader_set_mmap(pgp_stream_t *stream, int fd)
+{
+	mmap_reader_t	*mem;
+	struct stat	 st;
+
+	if (fstat(fd, &st) != 0) {
+		(void) fprintf(stderr, "pgp_reader_set_mmap: can't fstat\n");
+	} else if ((mem = calloc(1, sizeof(*mem))) == NULL) {
+		(void) fprintf(stderr, "pgp_reader_set_mmap: bad alloc\n");
+	} else {
+		mem->size = (uint64_t)st.st_size;
+		mem->offset = 0;
+		mem->fd = fd;
+		mem->mem = mmap(NULL, (size_t)st.st_size, PROT_READ,
+				MAP_PRIVATE | MAP_FILE, fd, 0);
+		if (mem->mem == MAP_FAILED) {
+			pgp_reader_set(stream, fd_reader, reader_fd_destroyer,
+					mem);
+		} else {
+			pgp_reader_set(stream, mmap_reader, mmap_destroyer,
+					mem);
+		}
+	}
+}
diff --git a/netpgp/signature.c b/netpgp/signature.c
new file mode 100644
index 0000000000000000000000000000000000000000..69b98ca5e7a612773d16bfebc1e5ca715039e428
--- /dev/null
+++ b/netpgp/signature.c
@@ -0,0 +1,1384 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_OPENSSL_DSA_H
+#include <openssl/dsa.h>
+#endif
+
+#include "netpgp/signature.h"
+#include "netpgp/crypto.h"
+#include "netpgp/create.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/validate.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/netpgpdigest.h"
+
+
+/**
+   \ingroup Core_Signature
+   Creates new pgp_create_sig_t
+   \return new pgp_create_sig_t
+   \note It is the caller's responsibility to call pgp_create_sig_delete()
+   \sa pgp_create_sig_delete()
+*/
+pgp_create_sig_t *
+pgp_create_sig_new(void)
+{
+	return calloc(1, sizeof(pgp_create_sig_t));
+}
+
+/**
+   \ingroup Core_Signature
+   Free signature and memory associated with it
+   \param sig struct to free
+   \sa pgp_create_sig_new()
+*/
+void
+pgp_create_sig_delete(pgp_create_sig_t *sig)
+{
+	pgp_output_delete(sig->output);
+	sig->output = NULL;
+	free(sig);
+}
+
+#if 0
+void
+pgp_dump_sig(pgp_sig_t *sig)
+{
+}
+#endif
+
+static uint8_t prefix_md5[] = {
+	0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
+	0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
+};
+
+static uint8_t prefix_sha1[] = {
+	0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E, 0x03, 0x02,
+	0x1A, 0x05, 0x00, 0x04, 0x14
+};
+
+static uint8_t prefix_sha256[] = {
+	0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+	0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+
+static uint8_t prefix_sha512[] = {
+    0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+    0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+    0x00, 0x04, 0x40
+};
+
+/* XXX: both this and verify would be clearer if the signature were */
+/* treated as an MPI. */
+static int
+rsa_sign(pgp_hash_t *hash,
+	const pgp_rsa_pubkey_t *pubrsa,
+	const pgp_rsa_seckey_t *secrsa,
+	pgp_output_t *out)
+{
+	unsigned        prefixsize;
+	unsigned        expected;
+	unsigned        hashsize;
+	unsigned        keysize;
+	unsigned        n;
+	unsigned        t;
+	uint8_t		hashbuf[NETPGP_BUFSIZ];
+	uint8_t		sigbuf[NETPGP_BUFSIZ];
+	uint8_t		*prefix;
+	BIGNUM         *bn;
+
+	if (strcmp(hash->name, "SHA1") == 0) {
+		hashsize = PGP_SHA1_HASH_SIZE + sizeof(prefix_sha1);
+		prefix = prefix_sha1;
+		prefixsize = sizeof(prefix_sha1);
+		expected = PGP_SHA1_HASH_SIZE;
+	} else if (strcmp(hash->name, "SHA256") == 0) {
+		hashsize = PGP_SHA256_HASH_SIZE + sizeof(prefix_sha256);
+		prefix = prefix_sha256;
+		prefixsize = sizeof(prefix_sha256);
+		expected = PGP_SHA256_HASH_SIZE;
+	} else {
+		hashsize = PGP_SHA512_HASH_SIZE + sizeof(prefix_sha512);
+		prefix = prefix_sha512;
+		prefixsize = sizeof(prefix_sha512);
+		expected = PGP_SHA512_HASH_SIZE;
+	}
+	keysize = (BN_num_bits(pubrsa->n) + 7) / 8;
+	if (keysize > sizeof(hashbuf)) {
+		(void) fprintf(stderr, "rsa_sign: keysize too big\n");
+		return 0;
+	}
+	if (10 + hashsize > keysize) {
+		(void) fprintf(stderr, "rsa_sign: hashsize too big\n");
+		return 0;
+	}
+
+	hashbuf[0] = 0;
+	hashbuf[1] = 1;
+	if (pgp_get_debug_level(__FILE__)) {
+		printf("rsa_sign: PS is %d\n", keysize - hashsize - 1 - 2);
+	}
+	for (n = 2; n < keysize - hashsize - 1; ++n) {
+		hashbuf[n] = 0xff;
+	}
+	hashbuf[n++] = 0;
+
+	(void) memcpy(&hashbuf[n], prefix, prefixsize);
+	n += prefixsize;
+	if ((t = hash->finish(hash, &hashbuf[n])) != expected) {
+		(void) fprintf(stderr, "rsa_sign: short %s hash\n", hash->name);
+		return 0;
+	}
+
+	pgp_write(out, &hashbuf[n], 2);
+
+	n += t;
+	if (n != keysize) {
+		(void) fprintf(stderr, "rsa_sign: n != keysize\n");
+		return 0;
+	}
+
+	t = pgp_rsa_private_encrypt(sigbuf, hashbuf, keysize, secrsa, pubrsa);
+	bn = BN_bin2bn(sigbuf, (int)t, NULL);
+	pgp_write_mpi(out, bn);
+	BN_free(bn);
+	return 1;
+}
+
+static int
+dsa_sign(pgp_hash_t *hash,
+	 const pgp_dsa_pubkey_t *dsa,
+	 const pgp_dsa_seckey_t *sdsa,
+	 pgp_output_t *output)
+{
+	unsigned        hashsize;
+	unsigned        t;
+	uint8_t		hashbuf[NETPGP_BUFSIZ];
+	pgp_dsa_sig_t        *pgpdsasig;
+
+	/* hashsize must be "equal in size to the number of bits of q,  */
+	/* the group generated by the DSA key's generator value */
+	/* 160/8 = 20 */
+
+	hashsize = 20;
+
+	/* finalise hash */
+	t = hash->finish(hash, &hashbuf[0]);
+	if (t != 20) {
+		(void) fprintf(stderr, "dsa_sign: hashfinish not 20\n");
+		return 0;
+	}
+
+	pgp_write(output, &hashbuf[0], 2);
+
+	/* write signature to buf */
+	pgpdsasig = pgp_dsa_sign(hashbuf, hashsize, sdsa, dsa);
+
+    if(pgpdsasig == NULL)
+        return 0;
+
+	/* convert and write the sig out to memory */
+	pgp_write_mpi(output, pgpdsasig->r);
+	pgp_write_mpi(output, pgpdsasig->s);
+
+    BN_free(pgpdsasig->r);
+    BN_free(pgpdsasig->s);
+	free(pgpdsasig);
+	return 1;
+}
+
+static unsigned
+rsa_verify(pgp_hash_alg_t type,
+	   const uint8_t *hash,
+	   size_t hash_length,
+	   const pgp_rsa_sig_t *sig,
+	   const pgp_rsa_pubkey_t *pubrsa)
+{
+	const uint8_t	*prefix;
+	unsigned       	 n;
+	unsigned       	 keysize;
+	unsigned	 plen;
+	unsigned	 debug_len_decrypted;
+	uint8_t   	 sigbuf[NETPGP_BUFSIZ];
+	uint8_t   	 hashbuf_from_sig[NETPGP_BUFSIZ];
+
+	keysize = BN_num_bytes(pubrsa->n);
+	/* RSA key can't be bigger than 65535 bits, so... */
+	if (keysize > sizeof(hashbuf_from_sig)) {
+		(void) fprintf(stderr, "rsa_verify: keysize too big\n");
+		return 0;
+	}
+	if ((unsigned) BN_num_bits(sig->sig) > 8 * sizeof(sigbuf)) {
+		(void) fprintf(stderr, "rsa_verify: BN_numbits too big\n");
+		return 0;
+	}
+	BN_bn2bin(sig->sig, sigbuf);
+
+	n = pgp_rsa_public_decrypt(hashbuf_from_sig, sigbuf,
+		(unsigned)(BN_num_bits(sig->sig) + 7) / 8, pubrsa);
+	debug_len_decrypted = n;
+
+	if (n != keysize) {
+		/* obviously, this includes error returns */
+		return 0;
+	}
+
+	/* XXX: why is there a leading 0? The first byte should be 1... */
+	/* XXX: because the decrypt should use keysize and not sigsize? */
+	if (hashbuf_from_sig[0] != 0 || hashbuf_from_sig[1] != 1) {
+		return 0;
+	}
+
+	switch (type) {
+	case PGP_HASH_MD5:
+		prefix = prefix_md5;
+		plen = sizeof(prefix_md5);
+		break;
+	case PGP_HASH_SHA1:
+		prefix = prefix_sha1;
+		plen = sizeof(prefix_sha1);
+		break;
+	case PGP_HASH_SHA256:
+		prefix = prefix_sha256;
+		plen = sizeof(prefix_sha256);
+		break;
+	case PGP_HASH_SHA512:
+		prefix = prefix_sha512;
+		plen = sizeof(prefix_sha512);
+		break;
+	default:
+		(void) fprintf(stderr, "Unknown hash algorithm: %d\n", type);
+		return 0;
+	}
+
+	if (keysize - plen - hash_length < 10) {
+		return 0;
+	}
+
+	for (n = 2; n < keysize - plen - hash_length - 1; ++n) {
+		if (hashbuf_from_sig[n] != 0xff) {
+			return 0;
+		}
+	}
+
+	if (hashbuf_from_sig[n++] != 0) {
+		return 0;
+	}
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "sig hashbuf", hashbuf_from_sig, debug_len_decrypted);
+		hexdump(stderr, "prefix", prefix, plen);
+		hexdump(stderr, "sig hash", &hashbuf_from_sig[n + plen], hash_length);
+		hexdump(stderr, "input hash", hash, hash_length);
+	}
+	return (memcmp(&hashbuf_from_sig[n], prefix, plen) == 0 &&
+	        memcmp(&hashbuf_from_sig[n + plen], hash, hash_length) == 0);
+}
+
+static void
+hash_add_key(pgp_hash_t *hash, const pgp_pubkey_t *key)
+{
+	pgp_memory_t	*mem = pgp_memory_new();
+	const unsigned 	 dontmakepacket = 0;
+	size_t		 len;
+
+	pgp_build_pubkey(mem, key, dontmakepacket);
+	len = pgp_mem_len(mem);
+	pgp_hash_add_int(hash, 0x99, 1);
+	pgp_hash_add_int(hash, (unsigned)len, 2);
+	hash->add(hash, pgp_mem_data(mem), (unsigned)len);
+
+	pgp_memory_free(mem);
+}
+
+static void
+initialise_hash(pgp_hash_t *hash, const pgp_sig_t *sig)
+{
+	pgp_hash_any(hash, sig->info.hash_alg);
+	if (!hash->init(hash)) {
+		(void) fprintf(stderr,
+			"initialise_hash: bad hash init\n");
+		/* just continue and die */
+		/* XXX - agc - no way to return failure */
+	}
+}
+
+static void
+init_key_sig(pgp_hash_t *hash, const pgp_sig_t *sig,
+		   const pgp_pubkey_t *key)
+{
+	initialise_hash(hash, sig);
+	hash_add_key(hash, key);
+}
+
+static void
+hash_add_trailer(pgp_hash_t *hash, const pgp_sig_t *sig)
+{
+	if (sig->info.version == PGP_V4) {
+		if (sig->info.v4_hashlen) {
+			hash->add(hash, sig->info.v4_hashed,
+				  (unsigned)sig->info.v4_hashlen);
+		}
+		pgp_hash_add_int(hash, (unsigned)sig->info.version, 1);
+		pgp_hash_add_int(hash, 0xff, 1);
+		pgp_hash_add_int(hash, (unsigned)sig->info.v4_hashlen, 4);
+	} else {
+		pgp_hash_add_int(hash, (unsigned)sig->info.type, 1);
+		pgp_hash_add_int(hash, (unsigned)sig->info.birthtime, 4);
+	}
+}
+
+/**
+   \ingroup Core_Signature
+   \brief Checks a signature
+   \param hash Signature Hash to be checked
+   \param length Signature Length
+   \param sig The Signature to be checked
+   \param signer The signer's public key
+   \return 1 if good; else 0
+*/
+unsigned
+pgp_check_sig(const uint8_t *hash, unsigned length,
+		    const pgp_sig_t * sig,
+		    const pgp_pubkey_t * signer)
+{
+	unsigned   ret;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stdout, "hash", hash, length);
+	}
+
+    switch (sig->info.key_alg) {
+	case PGP_PKA_DSA:
+		ret = pgp_dsa_verify(hash, length, &sig->info.sig.dsa,
+				&signer->key.dsa);
+		break;
+
+	case PGP_PKA_RSA:
+		ret = rsa_verify(sig->info.hash_alg, hash, length,
+				&sig->info.sig.rsa,
+				&signer->key.rsa);
+		break;
+
+	default:
+		(void) fprintf(stderr, "pgp_check_sig: unusual alg\n");
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static unsigned
+hash_and_check_sig(pgp_hash_t *hash,
+			 const pgp_sig_t *sig,
+			 const pgp_pubkey_t *signer)
+{
+	uint8_t   hashout[PGP_MAX_HASH_SIZE];
+	unsigned	n;
+
+	n = hash->finish(hash, hashout);
+	return pgp_check_sig(hashout, n, sig, signer);
+}
+
+static unsigned
+finalise_sig(pgp_hash_t *hash,
+		   const pgp_sig_t *sig,
+		   const pgp_pubkey_t *signer)
+{
+	hash_add_trailer(hash, sig);
+	return hash_and_check_sig(hash, sig, signer);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * \brief Verify a certification signature.
+ *
+ * \param key The public key that was signed.
+ * \param id The user ID that was signed
+ * \param sig The signature.
+ * \param signer The public key of the signer.
+ * \param raw_packet The raw signature packet.
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_check_useridcert_sig(const pgp_pubkey_t *key,
+			  const uint8_t *id,
+			  const pgp_sig_t *sig,
+			  const pgp_pubkey_t *signer)
+{
+	pgp_hash_t	hash;
+	size_t          userid_len;
+
+	userid_len = strlen((const char *) id);
+	init_key_sig(&hash, sig, key);
+	if (sig->info.version == PGP_V4) {
+		pgp_hash_add_int(&hash, 0xb4, 1);
+		pgp_hash_add_int(&hash, (unsigned)userid_len, 4);
+	}
+	hash.add(&hash, id, (unsigned)userid_len);
+	return finalise_sig(&hash, sig, signer);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Verify a certification signature.
+ *
+ * \param key The public key that was signed.
+ * \param attribute The user attribute that was signed
+ * \param sig The signature.
+ * \param signer The public key of the signer.
+ * \param raw_packet The raw signature packet.
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_check_userattrcert_sig(const pgp_pubkey_t *key,
+				const pgp_data_t *attribute,
+				const pgp_sig_t *sig,
+				const pgp_pubkey_t *signer)
+{
+	pgp_hash_t      hash;
+
+	init_key_sig(&hash, sig, key);
+	if (sig->info.version == PGP_V4) {
+		pgp_hash_add_int(&hash, 0xd1, 1);
+		pgp_hash_add_int(&hash, (unsigned)attribute->len, 4);
+	}
+	hash.add(&hash, attribute->contents, (unsigned)attribute->len);
+	return finalise_sig(&hash, sig, signer);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Verify a subkey signature.
+ *
+ * \param key The public key whose subkey was signed.
+ * \param subkey The subkey of the public key that was signed.
+ * \param sig The signature.
+ * \param signer The public key of the signer.
+ * \param raw_packet The raw signature packet.
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_check_subkey_sig(const pgp_pubkey_t *key,
+			   const pgp_pubkey_t *subkey,
+			   const pgp_sig_t *sig,
+			   const pgp_pubkey_t *signer)
+{
+	pgp_hash_t	hash;
+	unsigned	ret;
+
+	init_key_sig(&hash, sig, key);
+	hash_add_key(&hash, subkey);
+	ret = finalise_sig(&hash, sig, signer);
+	return ret;
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Verify a direct signature.
+ *
+ * \param key The public key which was signed.
+ * \param sig The signature.
+ * \param signer The public key of the signer.
+ * \param raw_packet The raw signature packet.
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_check_direct_sig(const pgp_pubkey_t *key,
+			   const pgp_sig_t *sig,
+			   const pgp_pubkey_t *signer)
+{
+	pgp_hash_t	hash;
+	unsigned	ret;
+
+	init_key_sig(&hash, sig, key);
+	ret = finalise_sig(&hash, sig, signer);
+	return ret;
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Verify a signature on a hash (the hash will have already been fed
+ * the material that was being signed, for example signed cleartext).
+ *
+ * \param hash A hash structure of appropriate type that has been fed
+ * the material to be signed. This MUST NOT have been finalised.
+ * \param sig The signature to be verified.
+ * \param signer The public key of the signer.
+ * \return 1 if OK; else 0
+ */
+unsigned
+pgp_check_hash_sig(pgp_hash_t *hash,
+			 const pgp_sig_t *sig,
+			 const pgp_pubkey_t *signer)
+{
+	return (sig->info.hash_alg == hash->alg) ?
+		finalise_sig(hash, sig, signer) :
+		0;
+}
+
+static void
+start_sig_in_mem(pgp_create_sig_t *sig)
+{
+	/* since this has subpackets and stuff, we have to buffer the whole */
+	/* thing to get counts before writing. */
+	sig->mem = pgp_memory_new();
+	pgp_memory_init(sig->mem, 100);
+	pgp_writer_set_memory(sig->output, sig->mem);
+
+	/* write nearly up to the first subpacket */
+	pgp_write_scalar(sig->output, (unsigned)sig->sig.info.version, 1);
+	pgp_write_scalar(sig->output, (unsigned)sig->sig.info.type, 1);
+	pgp_write_scalar(sig->output, (unsigned)sig->sig.info.key_alg, 1);
+	pgp_write_scalar(sig->output, (unsigned)sig->sig.info.hash_alg, 1);
+
+	/* dummy hashed subpacket count */
+	sig->hashoff = (unsigned)pgp_mem_len(sig->mem);
+	pgp_write_scalar(sig->output, 0, 2);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * pgp_sig_start() creates a V4 public key signature with a SHA1 hash.
+ *
+ * \param sig The signature structure to initialise
+ * \param key The public key to be signed
+ * \param id The user ID being bound to the key
+ * \param type Signature type
+ */
+void
+pgp_sig_start_key_sig(pgp_create_sig_t *sig,
+				  const pgp_pubkey_t *key,
+				  const pgp_pubkey_t *subkey, // added by Delta Chat to allow subkey binding signatures, EDIT BY MR (bp)
+				  const uint8_t *id,
+				  pgp_sig_type_t type)
+{
+	sig->output = pgp_output_new();
+
+	/* XXX:  refactor with check (in several ways - check should
+	 * probably use the buffered writer to construct packets
+	 * (done), and also should share code for hash calculation) */
+	sig->sig.info.version = PGP_V4;
+	sig->sig.info.hash_alg = PGP_HASH_SHA256; // changed by Delta Chat from PGP_HASH_SHA1 to PGP_HASH_SHA256, EDIT BY MR (bp)
+	sig->sig.info.key_alg = key->alg;
+	sig->sig.info.type = type;
+	sig->hashlen = (unsigned)-1;
+	init_key_sig(&sig->hash, &sig->sig, key);
+	if( subkey ) {
+		hash_add_key(&sig->hash, subkey);  // added by Delta Chat to allow subkey binding signatures, EDIT BY MR (bp)
+	}
+    
+	if( id ) {  // condition added by Delta Chat to allow subkey binding signatures, EDIT BY MR (bp)
+		pgp_hash_add_int(&sig->hash, 0xb4, 1);
+		//pgp_hash_add_int(&sig->hash, (unsigned)strlen((const char *) id), 4);
+		//sig->hash.add(&sig->hash, id, (unsigned)strlen((const char *) id));
+	}
+    
+	start_sig_in_mem(sig);
+}
+
+void
+pgp_sig_start_key_rev(pgp_create_sig_t *sig,
+				  const pgp_pubkey_t *key,
+				  pgp_sig_type_t type)
+{
+	sig->output = pgp_output_new();
+
+	sig->sig.info.version = PGP_V4;
+	sig->sig.info.hash_alg = PGP_HASH_SHA1;
+	sig->sig.info.key_alg = key->alg;
+	sig->sig.info.type = type;
+	sig->hashlen = (unsigned)-1;
+	init_key_sig(&sig->hash, &sig->sig, key);
+	start_sig_in_mem(sig);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Create a V4 public key signature over some cleartext.
+ *
+ * \param sig The signature structure to initialise
+ * \param id
+ * \param type
+ * \todo Expand description. Allow other hashes.
+ */
+
+void
+pgp_start_sig(pgp_create_sig_t *sig,
+	      const pgp_seckey_t *key,
+	      const pgp_hash_alg_t hash,
+	      const pgp_sig_type_t type)
+{
+	sig->output = pgp_output_new();
+
+	/* XXX:  refactor with check (in several ways - check should
+	 * probably use the buffered writer to construct packets
+	 * (done), and also should share code for hash calculation) */
+	sig->sig.info.version = PGP_V4;
+	sig->sig.info.key_alg = key->pubkey.alg;
+	sig->sig.info.hash_alg = hash;
+	sig->sig.info.type = type;
+
+	sig->hashlen = (unsigned)-1;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		fprintf(stderr, "initialising hash for sig in mem\n");
+	}
+	initialise_hash(&sig->hash, &sig->sig);
+	start_sig_in_mem(sig);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Add plaintext data to a signature-to-be.
+ *
+ * \param sig The signature-to-be.
+ * \param buf The plaintext data.
+ * \param length The amount of plaintext data.
+ */
+void
+pgp_sig_add_data(pgp_create_sig_t *sig, const void *buf, size_t length)
+{
+	sig->hash.add(&sig->hash, buf, (unsigned)length);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Mark the end of the hashed subpackets in the signature
+ *
+ * \param sig
+ */
+
+unsigned
+pgp_end_hashed_subpkts(pgp_create_sig_t *sig)
+{
+	sig->hashlen = (unsigned)(pgp_mem_len(sig->mem) - sig->hashoff - 2);
+	pgp_memory_place_int(sig->mem, sig->hashoff, sig->hashlen, 2);
+	/* dummy unhashed subpacket count */
+	sig->unhashoff = (unsigned)pgp_mem_len(sig->mem);
+	return pgp_write_scalar(sig->output, 0, 2);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Write out a signature
+ *
+ * \param sig
+ * \param key
+ * \param seckey
+ * \param info
+ *
+ */
+
+unsigned
+pgp_write_sig(pgp_output_t *output,
+			pgp_create_sig_t *sig,
+			const pgp_pubkey_t *key,
+			const pgp_seckey_t *seckey)
+{
+	unsigned	ret = 0;
+	size_t		len = pgp_mem_len(sig->mem);
+
+	/* check key not decrypted */
+	switch (seckey->pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (seckey->key.rsa.d == NULL) {
+			(void) fprintf(stderr, "pgp_write_sig: null rsa.d\n");
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_DSA:
+		if (seckey->key.dsa.x == NULL) {
+			(void) fprintf(stderr, "pgp_write_sig: null dsa.x\n");
+			return 0;
+		}
+		break;
+
+	default:
+		(void) fprintf(stderr, "Unsupported algorithm %d\n",
+				seckey->pubkey.alg);
+		return 0;
+	}
+
+	if (sig->hashlen == (unsigned) -1) {
+		(void) fprintf(stderr,
+				"ops_write_sig: bad hashed data len\n");
+		return 0;
+	}
+
+	pgp_memory_place_int(sig->mem, sig->unhashoff,
+			     (unsigned)(len - sig->unhashoff - 2), 2);
+
+	/* add the packet from version number to end of hashed subpackets */
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "ops_write_sig: hashed packet info\n");
+	}
+	sig->hash.add(&sig->hash, pgp_mem_data(sig->mem), sig->unhashoff);
+
+	/* add final trailer */
+	pgp_hash_add_int(&sig->hash, (unsigned)sig->sig.info.version, 1);
+	pgp_hash_add_int(&sig->hash, 0xff, 1);
+	/* +6 for version, type, pk alg, hash alg, hashed subpacket length */
+	pgp_hash_add_int(&sig->hash, sig->hashlen + 6, 4);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr, "ops_write_sig: done writing hashed\n");
+	}
+	/* XXX: technically, we could figure out how big the signature is */
+	/* and write it directly to the output instead of via memory. */
+	switch (seckey->pubkey.alg) {
+	case PGP_PKA_RSA:
+	case PGP_PKA_RSA_ENCRYPT_ONLY:
+	case PGP_PKA_RSA_SIGN_ONLY:
+		if (!rsa_sign(&sig->hash, &key->key.rsa, &seckey->key.rsa,
+				sig->output)) {
+			(void) fprintf(stderr,
+				"pgp_write_sig: rsa_sign failure\n");
+			return 0;
+		}
+		break;
+
+	case PGP_PKA_DSA:
+		if (!dsa_sign(&sig->hash, &key->key.dsa, &seckey->key.dsa,
+				sig->output)) {
+			(void) fprintf(stderr,
+				"pgp_write_sig: dsa_sign failure\n");
+			return 0;
+		}
+		break;
+
+	default:
+		(void) fprintf(stderr, "Unsupported algorithm %d\n",
+					seckey->pubkey.alg);
+		return 0;
+	}
+
+	ret = pgp_write_ptag(output, PGP_PTAG_CT_SIGNATURE);
+	if (ret) {
+		len = pgp_mem_len(sig->mem);
+		ret = pgp_write_length(output, (unsigned)len) &&
+			pgp_write(output, pgp_mem_data(sig->mem), (unsigned)len);
+	}
+	pgp_memory_free(sig->mem);
+
+	if (ret == 0) {
+		PGP_ERROR_1(&output->errors, PGP_E_W, "%s",
+		    "Cannot write signature");
+	}
+	return ret;
+}
+
+unsigned
+pgp_add_creation_time(pgp_create_sig_t *sig, time_t when)
+{
+	pgp_content_enum	tag;
+
+	tag = PGP_PTAG_SS_CREATION_TIME;
+
+    sig->sig.info.birthtime = when;
+    sig->sig.info.birthtime_set = 1;
+
+    return pgp_write_ss_header(sig->output, 5, tag) &&
+		pgp_write_scalar(sig->output, (unsigned int)when, 4);
+}
+
+unsigned
+pgp_add_sig_expiration_time(pgp_create_sig_t *sig, time_t duration)
+{
+	pgp_content_enum	tag;
+
+	tag = PGP_PTAG_SS_EXPIRATION_TIME;
+
+    sig->sig.info.duration = duration;
+    sig->sig.info.duration_set = 1;
+
+    return pgp_write_ss_header(sig->output, 5, tag) &&
+		pgp_write_scalar(sig->output, (unsigned int)duration, 4);
+}
+
+unsigned
+pgp_add_key_expiration_time(pgp_create_sig_t *sig, time_t duration)
+{
+	pgp_content_enum	tag;
+
+	tag = PGP_PTAG_SS_KEY_EXPIRY;
+
+    sig->sig.info.key_expiry = duration;
+    sig->sig.info.key_expiry_set = 1;
+
+	return pgp_write_ss_header(sig->output, 5, tag) &&
+		pgp_write_scalar(sig->output, (unsigned int)duration, 4);
+}
+
+unsigned
+pgp_add_key_flags(pgp_create_sig_t *sig, uint8_t flags)
+{
+    pgp_content_enum	tag;
+
+    tag = PGP_PTAG_SS_KEY_FLAGS;
+
+    sig->sig.info.key_flags = flags;
+    sig->sig.info.key_flags_set = 1;
+
+    return pgp_write_ss_header(sig->output, 2, tag) &&
+    pgp_write_scalar(sig->output, (unsigned int)flags, 1);
+}
+
+#if 0 ///////
+unsigned
+pgp_add_key_prefs(pgp_create_sig_t *sig)
+{
+
+    /* Mimic of GPG default settings, limited to supported algos */
+
+    return
+        /* Symmetric algo prefs */
+        pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_SKA) &&
+        pgp_write_scalar(sig->output, PGP_SA_AES_256, 1) &&
+        pgp_write_scalar(sig->output, PGP_SA_AES_128, 1) &&
+        pgp_write_scalar(sig->output, PGP_SA_CAST5, 1) &&
+        pgp_write_scalar(sig->output, PGP_SA_TRIPLEDES, 1) &&
+        pgp_write_scalar(sig->output, PGP_SA_IDEA, 1) &&
+
+        /* Hash algo prefs */
+        pgp_write_ss_header(sig->output, 6, PGP_PTAG_SS_PREFERRED_HASH) &&
+        pgp_write_scalar(sig->output, PGP_HASH_SHA256, 1) &&
+        pgp_write_scalar(sig->output, PGP_HASH_SHA1, 1) &&
+        pgp_write_scalar(sig->output, PGP_HASH_SHA384, 1) &&
+        pgp_write_scalar(sig->output, PGP_HASH_SHA512, 1) &&
+        pgp_write_scalar(sig->output, PGP_HASH_SHA224, 1) &&
+
+        /* Compression algo prefs */
+        pgp_write_ss_header(sig->output, 3, PGP_PTAG_SS_PREF_COMPRESS) &&
+        pgp_write_scalar(sig->output, PGP_C_ZLIB, 1) &&
+        pgp_write_scalar(sig->output, PGP_C_BZIP2, 1);
+}
+#endif //////
+
+unsigned
+pgp_add_key_features(pgp_create_sig_t *sig)
+{
+    pgp_content_enum	tag;
+
+    tag = PGP_PTAG_SS_FEATURES;
+
+    return pgp_write_ss_header(sig->output, 2, tag) &&
+    pgp_write_scalar(sig->output, 0x01, 1);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Adds issuer's key ID to the signature
+ *
+ * \param sig
+ * \param keyid
+ */
+
+unsigned
+pgp_add_issuer_keyid(pgp_create_sig_t *sig,
+				const uint8_t keyid[PGP_KEY_ID_SIZE])
+{
+	return pgp_write_ss_header(sig->output, PGP_KEY_ID_SIZE + 1,
+				PGP_PTAG_SS_ISSUER_KEY_ID) &&
+		pgp_write(sig->output, keyid, PGP_KEY_ID_SIZE);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Adds primary user ID to the signature
+ *
+ * \param sig
+ * \param primary
+ */
+void
+pgp_add_primary_userid(pgp_create_sig_t *sig, unsigned primary)
+{
+	pgp_write_ss_header(sig->output, 2, PGP_PTAG_SS_PRIMARY_USER_ID);
+	pgp_write_scalar(sig->output, primary, 1);
+}
+
+unsigned
+pgp_add_revocation_reason(
+        pgp_create_sig_t *sig,
+        uint8_t code, const char *reason)
+{
+    size_t rlen = reason ? strlen(reason) : 0;
+	return pgp_write_ss_header(
+               sig->output, rlen + 1,
+	           PGP_PTAG_SS_REVOCATION_REASON)      &&
+		   pgp_write_scalar(sig->output, code, 1) &&
+		   pgp_write(sig->output, reason, (unsigned int)rlen);
+}
+
+/**
+ * \ingroup Core_Signature
+ *
+ * Get the hash structure in use for the signature.
+ *
+ * \param sig The signature structure.
+ * \return The hash structure.
+ */
+pgp_hash_t     *
+pgp_sig_get_hash(pgp_create_sig_t *sig)
+{
+	return &sig->hash;
+}
+
+/* open up an output file */
+#if 0 //////
+static int
+open_output_file(pgp_output_t **output,
+			const char *inname,
+			const char *outname,
+			const char *suffix,
+			const unsigned overwrite)
+{
+	int             fd;
+
+	/* setup output file */
+	if (outname) {
+		fd = pgp_setup_file_write(output, outname, overwrite);
+	} else {
+		unsigned        flen = (unsigned)(strlen(inname) + 4 + 1);
+		char           *f = NULL;
+
+		if ((f = calloc(1, flen)) == NULL) {
+			(void) fprintf(stderr, "open_output_file: bad alloc\n");
+			fd = -1;
+		} else {
+			(void) snprintf(f, flen, "%s.%s", inname, suffix);
+			fd = pgp_setup_file_write(output, f, overwrite);
+			free(f);
+		}
+	}
+	return fd;
+}
+#endif //////
+
+/**
+\ingroup HighLevel_Sign
+\brief Sign a file
+\param inname Input filename
+\param outname Output filename. If NULL, a name is constructed from the input filename.
+\param seckey Secret Key to use for signing
+\param armored Write armoured text, if set.
+\param overwrite May overwrite existing file, if set.
+\return 1 if OK; else 0;
+
+*/
+#if 0 //////
+unsigned
+pgp_sign_file(pgp_io_t *io,
+		const char *inname,
+		const char *outname,
+		const pgp_seckey_t *seckey,
+		const char *hashname,
+		const time_t from,
+		const time_t duration,
+		const unsigned armored,
+		const unsigned cleartext,
+		const unsigned overwrite)
+{
+	pgp_create_sig_t	*sig;
+	pgp_sig_type_t	 sig_type;
+	pgp_hash_alg_t	 hash_alg;
+	pgp_memory_t		*infile;
+	pgp_output_t		*output;
+	pgp_hash_t		*hash;
+	unsigned		 ret;
+	uint8_t			 keyid[PGP_KEY_ID_SIZE];
+	int			 fd_out;
+
+	sig = NULL;
+	sig_type = PGP_SIG_BINARY;
+	infile = NULL;
+	output = NULL;
+	hash = NULL;
+
+	/* find the hash algorithm */
+	hash_alg = pgp_str_to_hash_alg(hashname);
+	if (hash_alg == PGP_HASH_UNKNOWN) {
+		(void) fprintf(io->errs,
+			"pgp_sign_file: unknown hash algorithm: \"%s\"\n",
+			hashname);
+		return 0;
+	}
+
+	/* read input file into buf */
+	infile = pgp_memory_new();
+	if (!pgp_mem_readfile(infile, inname)) {
+		return 0;
+	}
+
+	/* setup output file */
+	fd_out = open_output_file(&output, inname, outname,
+				(armored) ? "asc" : "gpg", overwrite);
+	if (fd_out < 0) {
+		pgp_memory_free(infile);
+		return 0;
+	}
+
+	/* set up signature */
+	sig = pgp_create_sig_new();
+	if (!sig) {
+		pgp_memory_free(infile);
+		pgp_teardown_file_write(output, fd_out);
+		return 0;
+	}
+
+	pgp_start_sig(sig, seckey, hash_alg, sig_type);
+
+	if (cleartext) {
+		if (pgp_writer_push_clearsigned(output, sig) != 1) {
+			return 0;
+		}
+
+		/* Do the signing */
+		pgp_write(output, pgp_mem_data(infile), (unsigned)pgp_mem_len(infile));
+		pgp_memory_free(infile);
+
+		/* add signature with subpackets: */
+		/* - creation time */
+		/* - key id */
+		ret = pgp_writer_use_armored_sig(output) &&
+				pgp_add_creation_time(sig, from) &&
+				pgp_add_sig_expiration_time(sig, duration);
+		if (ret == 0) {
+			pgp_teardown_file_write(output, fd_out);
+			return 0;
+		}
+
+		pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, hash_alg);
+		ret = pgp_add_issuer_keyid(sig, keyid) &&
+			pgp_end_hashed_subpkts(sig) &&
+			pgp_write_sig(output, sig, &seckey->pubkey, seckey);
+
+		pgp_teardown_file_write(output, fd_out);
+
+		if (ret == 0) {
+			PGP_ERROR_1(&output->errors, PGP_E_W, "%s",
+			    "Cannot sign file as cleartext");
+		}
+	} else {
+		/* set armoured/not armoured here */
+		if (armored) {
+			pgp_writer_push_armor_msg(output);
+		}
+
+		/* write one_pass_sig */
+		pgp_write_one_pass_sig(output, seckey, hash_alg, sig_type);
+
+		/* hash file contents */
+		hash = pgp_sig_get_hash(sig);
+		hash->add(hash, pgp_mem_data(infile), (unsigned)pgp_mem_len(infile));
+
+#if 1
+		/* output file contents as Literal Data packet */
+		pgp_write_litdata(output, pgp_mem_data(infile),
+			(const int)pgp_mem_len(infile),
+			PGP_LDT_BINARY);
+#else
+		/* XXX - agc - sync with writer.c 1094 for ops_writez */
+		pgp_setup_memory_write(&litoutput, &litmem, bufsz);
+		pgp_setup_memory_write(&zoutput, &zmem, bufsz);
+		pgp_write_litdata(litoutput,
+			pgp_mem_data(pgp_mem_data(infile),
+			(const int)pgp_mem_len(infile), PGP_LDT_BINARY);
+		pgp_writez(zoutput, pgp_mem_data(litmem), pgp_mem_len(litmem));
+#endif
+
+		/* add creation time to signature */
+		pgp_add_creation_time(sig, from);
+		pgp_add_sig_expiration_time(sig, duration);
+		/* add key id to signature */
+		pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, hash_alg);
+		pgp_add_issuer_keyid(sig, keyid);
+		pgp_end_hashed_subpkts(sig);
+		pgp_write_sig(output, sig, &seckey->pubkey, seckey);
+
+		/* tidy up */
+		pgp_teardown_file_write(output, fd_out);
+
+		pgp_create_sig_delete(sig);
+		pgp_memory_free(infile);
+
+		ret = 1;
+	}
+
+	return ret;
+}
+#endif ///////
+
+/**
+\ingroup HighLevel_Sign
+\brief Signs a buffer
+\param input Input text to be signed
+\param input_len Length of input text
+\param sig_type Signature type
+\param seckey Secret Key
+\param armored Write armoured text, if set
+\return New pgp_memory_t struct containing signed text
+\note It is the caller's responsibility to call pgp_memory_free(me)
+
+*/
+pgp_memory_t *
+pgp_sign_buf(pgp_io_t *io,
+		const void *input,
+		const size_t insize,
+		const pgp_seckey_t *seckey,
+		const time_t from,
+		const time_t duration,
+		const char *hashname,
+		const unsigned armored,
+		const unsigned cleartext)
+{
+	pgp_litdata_enum	 ld_type;
+	pgp_create_sig_t	*sig;
+	pgp_sig_type_t	 sig_type;
+	pgp_hash_alg_t	 hash_alg;
+	pgp_output_t		*output;
+	pgp_memory_t		*mem;
+	uint8_t			 keyid[PGP_KEY_ID_SIZE];
+	pgp_hash_t		*hash;
+	unsigned		 ret;
+
+	sig = NULL;
+	sig_type = PGP_SIG_BINARY;
+	output = NULL;
+	mem = pgp_memory_new();
+	hash = NULL;
+
+	hash_alg = pgp_str_to_hash_alg(hashname);
+	if (hash_alg == PGP_HASH_UNKNOWN) {
+		(void) fprintf(io->errs,
+			"pgp_sign_buf: unknown hash algorithm: \"%s\"\n",
+			hashname);
+		return NULL;
+	}
+
+	/* setup literal data packet type */
+	ld_type = (cleartext) ? PGP_LDT_TEXT : PGP_LDT_BINARY;
+
+	if (input == NULL) {
+		(void) fprintf(io->errs,
+			"pgp_sign_buf: null input\n");
+		return NULL;
+	}
+
+	/* set up signature */
+	if ((sig = pgp_create_sig_new()) == NULL) {
+		return NULL;
+	}
+	pgp_start_sig(sig, seckey, hash_alg, sig_type);
+
+	/* setup writer */
+	pgp_setup_memory_write(&output, &mem, insize);
+
+	if (cleartext) {
+		/* Do the signing */
+		/* add signature with subpackets: */
+		/* - creation time */
+		/* - key id */
+		ret = pgp_writer_push_clearsigned(output, sig) &&
+			pgp_write(output, input, (unsigned)insize) &&
+			pgp_writer_use_armored_sig(output) &&
+			pgp_add_creation_time(sig, from) &&
+			pgp_add_sig_expiration_time(sig, duration);
+		if (ret == 0) {
+			return NULL;
+		}
+		pgp_output_delete(output);
+	} else {
+		/* set armoured/not armoured here */
+		if (armored) {
+			pgp_writer_push_armor_msg(output);
+		}
+		if (pgp_get_debug_level(__FILE__)) {
+			fprintf(io->errs, "** Writing out one pass sig\n");
+		}
+		/* write one_pass_sig */
+		pgp_write_one_pass_sig(output, seckey, hash_alg, sig_type);
+
+		/* hash memory */
+		hash = pgp_sig_get_hash(sig);
+		hash->add(hash, input, (unsigned)insize);
+
+		/* output file contents as Literal Data packet */
+		if (pgp_get_debug_level(__FILE__)) {
+			(void) fprintf(stderr, "** Writing out data now\n");
+		}
+		pgp_write_litdata(output, input, (const int)insize, ld_type);
+		if (pgp_get_debug_level(__FILE__)) {
+			fprintf(stderr, "** After Writing out data now\n");
+		}
+
+		/* add creation time to signature */
+		pgp_add_creation_time(sig, from);
+		pgp_add_sig_expiration_time(sig, duration);
+		/* add key id to signature */
+		pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, hash_alg);
+		pgp_add_issuer_keyid(sig, keyid);
+		pgp_end_hashed_subpkts(sig);
+
+		/* write out sig */
+		pgp_write_sig(output, sig, &seckey->pubkey, seckey);
+
+		/* tidy up */
+		pgp_writer_close(output);
+		pgp_create_sig_delete(sig);
+	}
+	return mem;
+}
+
+/* sign a file, and put the signature in a separate file */
+#if 0 //////
+int
+pgp_sign_detached(pgp_io_t *io,
+			const char *f,
+			char *sigfile,
+			const pgp_seckey_t *seckey,
+			const char *hash,
+			const time_t from,
+			const time_t duration,
+			const unsigned armored, const unsigned overwrite)
+{
+	pgp_create_sig_t	*sig;
+	pgp_hash_alg_t	 hash_alg;
+	pgp_output_t		*output;
+	pgp_memory_t		*mem;
+	uint8_t	 	 	 keyid[PGP_KEY_ID_SIZE];
+	int			 fd;
+
+	/* find out which hash algorithm to use */
+	hash_alg = pgp_str_to_hash_alg(hash);
+	if (hash_alg == PGP_HASH_UNKNOWN) {
+		(void) fprintf(io->errs,"Unknown hash algorithm: %s\n", hash);
+		return 0;
+	}
+
+	/* setup output file */
+	fd = open_output_file(&output, f, sigfile,
+				(armored) ? "asc" : "sig", overwrite);
+	if (fd < 0) {
+		(void) fprintf(io->errs,"Can't open output file: %s\n", f);
+		return 0;
+	}
+
+	/* create a new signature */
+	sig = pgp_create_sig_new();
+	pgp_start_sig(sig, seckey, hash_alg, PGP_SIG_BINARY);
+
+	/* read the contents of 'f', and add that to the signature */
+	mem = pgp_memory_new();
+	if (!pgp_mem_readfile(mem, f)) {
+		pgp_teardown_file_write(output, fd);
+		return 0;
+	}
+	/* set armoured/not armoured here */
+	if (armored) {
+		pgp_writer_push_armor_msg(output);
+	}
+	pgp_sig_add_data(sig, pgp_mem_data(mem), pgp_mem_len(mem));
+	pgp_memory_free(mem);
+
+	/* calculate the signature */
+	pgp_add_creation_time(sig, from);
+	pgp_add_sig_expiration_time(sig, duration);
+	pgp_keyid(keyid, sizeof(keyid), &seckey->pubkey, hash_alg);
+	pgp_add_issuer_keyid(sig, keyid);
+	pgp_end_hashed_subpkts(sig);
+	pgp_write_sig(output, sig, &seckey->pubkey, seckey);
+	pgp_teardown_file_write(output, fd);
+
+	return 1;
+}
+#endif //////
diff --git a/netpgp/symmetric.c b/netpgp/symmetric.c
new file mode 100644
index 0000000000000000000000000000000000000000..033d1d04b7dcd82fe8112424a8825bbd725f83d8
--- /dev/null
+++ b/netpgp/symmetric.c
@@ -0,0 +1,802 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include "netpgp/crypto.h"
+#include "netpgp/packet-show.h"
+
+#include <string.h>
+
+#ifdef HAVE_OPENSSL_CAST_H
+#include <openssl/cast.h>
+#endif
+
+#ifdef HAVE_OPENSSL_IDEA_H
+#include <openssl/idea.h>
+#endif
+
+#ifdef HAVE_OPENSSL_AES_H
+#include <openssl/aes.h>
+#endif
+
+#ifdef HAVE_OPENSSL_DES_H
+#include <openssl/des.h>
+#endif
+
+#ifdef HAVE_OPENSSL_CAMELLIA_H
+#include <openssl/camellia.h>
+#endif
+
+#include "netpgp/crypto.h"
+#include "netpgp/netpgpdefs.h"
+
+
+static void
+std_set_iv(pgp_crypt_t *crypt, const uint8_t *iv)
+{
+	(void) memcpy(crypt->iv, iv, crypt->blocksize);
+	crypt->num = 0;
+}
+
+static void
+std_set_key(pgp_crypt_t *crypt, const uint8_t *key)
+{
+	(void) memcpy(crypt->key, key, crypt->keysize);
+}
+
+static void
+std_resync(pgp_crypt_t *decrypt)
+{
+	if ((size_t) decrypt->num == decrypt->blocksize) {
+		return;
+	}
+
+	memmove(decrypt->civ + decrypt->blocksize - decrypt->num, decrypt->civ,
+		(unsigned)decrypt->num);
+	(void) memcpy(decrypt->civ, decrypt->siv + decrypt->num,
+	       decrypt->blocksize - decrypt->num);
+	decrypt->num = 0;
+}
+
+static void
+std_finish(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+		crypt->encrypt_key = NULL;
+	}
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+		crypt->decrypt_key = NULL;
+	}
+}
+
+static int
+cast5_init(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(CAST_KEY))) == NULL) {
+		(void) fprintf(stderr, "cast5_init: alloc failure\n");
+		return 0;
+	}
+	CAST_set_key(crypt->encrypt_key, (int)crypt->keysize, crypt->key);
+	if ((crypt->decrypt_key = calloc(1, sizeof(CAST_KEY))) == NULL) {
+		(void) fprintf(stderr, "cast5_init: alloc failure\n");
+		return 0;
+	}
+	CAST_set_key(crypt->decrypt_key, (int)crypt->keysize, crypt->key);
+	return 1;
+}
+
+static void
+cast5_block_encrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	CAST_ecb_encrypt(in, out, crypt->encrypt_key, CAST_ENCRYPT);
+}
+
+static void
+cast5_block_decrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	CAST_ecb_encrypt(in, out, crypt->encrypt_key, CAST_DECRYPT);
+}
+
+static void
+cast5_cfb_encrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	CAST_cfb64_encrypt(in, out, (long)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   CAST_ENCRYPT);
+}
+
+static void
+cast5_cfb_decrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	CAST_cfb64_encrypt(in, out, (long)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   CAST_DECRYPT);
+}
+
+#define TRAILER		"","","","",0,NULL,NULL
+
+static pgp_crypt_t cast5 =
+{
+	PGP_SA_CAST5,
+	CAST_BLOCK,
+	CAST_KEY_LENGTH,
+	std_set_iv,
+	std_set_key,
+	cast5_init,
+	std_resync,
+	cast5_block_encrypt,
+	cast5_block_decrypt,
+	cast5_cfb_encrypt,
+	cast5_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+
+#ifndef OPENSSL_NO_IDEA
+static int
+idea_init(pgp_crypt_t *crypt)
+{
+	if (crypt->keysize != IDEA_KEY_LENGTH) {
+		(void) fprintf(stderr, "idea_init: keysize wrong\n");
+		return 0;
+	}
+
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(IDEA_KEY_SCHEDULE))) == NULL) {
+		(void) fprintf(stderr, "idea_init: alloc failure\n");
+		return 0;
+	}
+
+	/* note that we don't invert the key when decrypting for CFB mode */
+	idea_set_encrypt_key(crypt->key, crypt->encrypt_key);
+
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+	}
+	if ((crypt->decrypt_key = calloc(1, sizeof(IDEA_KEY_SCHEDULE))) == NULL) {
+		(void) fprintf(stderr, "idea_init: alloc failure\n");
+		return 0;
+	}
+
+	idea_set_decrypt_key(crypt->encrypt_key, crypt->decrypt_key);
+	return 1;
+}
+
+static void
+idea_block_encrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	idea_ecb_encrypt(in, out, crypt->encrypt_key);
+}
+
+static void
+idea_block_decrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	idea_ecb_encrypt(in, out, crypt->decrypt_key);
+}
+
+static void
+idea_cfb_encrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	idea_cfb64_encrypt(in, out, (long)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   CAST_ENCRYPT);
+}
+
+static void
+idea_cfb_decrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	idea_cfb64_encrypt(in, out, (long)count,
+			   crypt->decrypt_key, crypt->iv, &crypt->num,
+			   CAST_DECRYPT);
+}
+
+static const pgp_crypt_t idea =
+{
+	PGP_SA_IDEA,
+	IDEA_BLOCK,
+	IDEA_KEY_LENGTH,
+	std_set_iv,
+	std_set_key,
+	idea_init,
+	std_resync,
+	idea_block_encrypt,
+	idea_block_decrypt,
+	idea_cfb_encrypt,
+	idea_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+#endif				/* OPENSSL_NO_IDEA */
+
+/* AES with 128-bit key (AES) */
+
+#define KEYBITS_AES128 128
+
+static int
+aes128_init(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(AES_KEY))) == NULL) {
+		(void) fprintf(stderr, "aes128_init: alloc failure\n");
+		return 0;
+	}
+	if (AES_set_encrypt_key(crypt->key, KEYBITS_AES128,
+			crypt->encrypt_key)) {
+		fprintf(stderr, "aes128_init: Error setting encrypt_key\n");
+	}
+
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+	}
+	if ((crypt->decrypt_key = calloc(1, sizeof(AES_KEY))) == NULL) {
+		(void) fprintf(stderr, "aes128_init: alloc failure\n");
+		return 0;
+	}
+	if (AES_set_decrypt_key(crypt->key, KEYBITS_AES128,
+				crypt->decrypt_key)) {
+		fprintf(stderr, "aes128_init: Error setting decrypt_key\n");
+	}
+	return 1;
+}
+
+static void
+aes_block_encrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	AES_encrypt(in, out, crypt->encrypt_key);
+}
+
+static void
+aes_block_decrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	AES_decrypt(in, out, crypt->decrypt_key);
+}
+
+static void
+aes_cfb_encrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	AES_cfb128_encrypt(in, out, (unsigned)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   AES_ENCRYPT);
+}
+
+static void
+aes_cfb_decrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	AES_cfb128_encrypt(in, out, (unsigned)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   AES_DECRYPT);
+}
+
+static const pgp_crypt_t aes128 =
+{
+	PGP_SA_AES_128,
+	AES_BLOCK_SIZE,
+	KEYBITS_AES128 / 8,
+	std_set_iv,
+	std_set_key,
+	aes128_init,
+	std_resync,
+	aes_block_encrypt,
+	aes_block_decrypt,
+	aes_cfb_encrypt,
+	aes_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+
+/* AES with 256-bit key */
+
+#define KEYBITS_AES256 256
+
+static int
+aes256_init(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(AES_KEY))) == NULL) {
+		(void) fprintf(stderr, "aes256_init: alloc failure\n");
+		return 0;
+	}
+	if (AES_set_encrypt_key(crypt->key, KEYBITS_AES256,
+			crypt->encrypt_key)) {
+		fprintf(stderr, "aes256_init: Error setting encrypt_key\n");
+		free(crypt->encrypt_key);
+		crypt->encrypt_key = NULL;
+		return 0;
+	}
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+	}
+	if ((crypt->decrypt_key = calloc(1, sizeof(AES_KEY))) == NULL) {
+		(void) fprintf(stderr, "aes256_init: alloc failure\n");
+		free(crypt->encrypt_key);
+		crypt->encrypt_key = NULL;
+		return 0;
+	}
+	if (AES_set_decrypt_key(crypt->key, KEYBITS_AES256,
+			crypt->decrypt_key)) {
+		fprintf(stderr, "aes256_init: Error setting decrypt_key\n");
+		free(crypt->encrypt_key);
+		crypt->encrypt_key = NULL;
+		free(crypt->decrypt_key);
+		crypt->decrypt_key = NULL;
+		return 0;
+	}
+	return 1;
+}
+
+static const pgp_crypt_t aes256 =
+{
+	PGP_SA_AES_256,
+	AES_BLOCK_SIZE,
+	KEYBITS_AES256 / 8,
+	std_set_iv,
+	std_set_key,
+	aes256_init,
+	std_resync,
+	aes_block_encrypt,
+	aes_block_decrypt,
+	aes_cfb_encrypt,
+	aes_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+
+/* Triple DES */
+
+static int
+tripledes_init(pgp_crypt_t *crypt)
+{
+	DES_key_schedule *keys;
+	int             n;
+
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((keys = crypt->encrypt_key = calloc(1, 3 * sizeof(DES_key_schedule))) == NULL) {
+		(void) fprintf(stderr, "tripledes_init: alloc failure\n");
+		return 0;
+	}
+	for (n = 0; n < 3; ++n) {
+		DES_set_key((DES_cblock *)(void *)(crypt->key + n * 8),
+			&keys[n]);
+	}
+	return 1;
+}
+
+static void
+tripledes_block_encrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	DES_key_schedule *keys = crypt->encrypt_key;
+
+	DES_ecb3_encrypt(__UNCONST(in), out, &keys[0], &keys[1], &keys[2],
+			DES_ENCRYPT);
+}
+
+static void
+tripledes_block_decrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	DES_key_schedule *keys = crypt->encrypt_key;
+
+	DES_ecb3_encrypt(__UNCONST(in), out, &keys[0], &keys[1], &keys[2],
+			DES_DECRYPT);
+}
+
+static void
+tripledes_cfb_encrypt(pgp_crypt_t *crypt, void *out, const void *in,
+			size_t count)
+{
+	DES_key_schedule *keys = crypt->encrypt_key;
+
+	DES_ede3_cfb64_encrypt(in, out, (long)count,
+		&keys[0], &keys[1], &keys[2], (DES_cblock *)(void *)crypt->iv,
+		&crypt->num, DES_ENCRYPT);
+}
+
+static void
+tripledes_cfb_decrypt(pgp_crypt_t *crypt, void *out, const void *in,
+			size_t count)
+{
+	DES_key_schedule *keys = crypt->encrypt_key;
+
+	DES_ede3_cfb64_encrypt(in, out, (long)count,
+		&keys[0], &keys[1], &keys[2], (DES_cblock *)(void *)crypt->iv,
+		&crypt->num, DES_DECRYPT);
+}
+
+static const pgp_crypt_t tripledes =
+{
+	PGP_SA_TRIPLEDES,
+	8,
+	24,
+	std_set_iv,
+	std_set_key,
+	tripledes_init,
+	std_resync,
+	tripledes_block_encrypt,
+	tripledes_block_decrypt,
+	tripledes_cfb_encrypt,
+	tripledes_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+
+#if defined(HAVE_OPENSSL_CAMELLIA_H) && !defined(OPENSSL_NO_CAMELLIA)
+/* Camellia with 128-bit key (CAMELLIA) */
+
+#define KEYBITS_CAMELLIA128 128
+
+static int
+camellia128_init(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(CAMELLIA_KEY))) == NULL) {
+		(void) fprintf(stderr, "camellia128_init: alloc failure\n");
+		return 0;
+	}
+	if (Camellia_set_key(crypt->key, KEYBITS_CAMELLIA128, crypt->encrypt_key)) {
+		fprintf(stderr, "camellia128_init: Error setting encrypt_key\n");
+	}
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+	}
+	if ((crypt->decrypt_key = calloc(1, sizeof(CAMELLIA_KEY))) == NULL) {
+		(void) fprintf(stderr, "camellia128_init: alloc failure\n");
+		return 0;
+	}
+	if (Camellia_set_key(crypt->key, KEYBITS_CAMELLIA128, crypt->decrypt_key)) {
+		fprintf(stderr, "camellia128_init: Error setting decrypt_key\n");
+	}
+	return 1;
+}
+
+static void
+camellia_block_encrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	Camellia_encrypt(in, out, crypt->encrypt_key);
+}
+
+static void
+camellia_block_decrypt(pgp_crypt_t *crypt, void *out, const void *in)
+{
+	Camellia_decrypt(in, out, crypt->decrypt_key);
+}
+
+static void
+camellia_cfb_encrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	Camellia_cfb128_encrypt(in, out, (unsigned)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   CAMELLIA_ENCRYPT);
+}
+
+static void
+camellia_cfb_decrypt(pgp_crypt_t *crypt, void *out, const void *in, size_t count)
+{
+	Camellia_cfb128_encrypt(in, out, (unsigned)count,
+			   crypt->encrypt_key, crypt->iv, &crypt->num,
+			   CAMELLIA_DECRYPT);
+}
+
+static const pgp_crypt_t camellia128 =
+{
+	PGP_SA_CAMELLIA_128,
+	CAMELLIA_BLOCK_SIZE,
+	KEYBITS_CAMELLIA128 / 8,
+	std_set_iv,
+	std_set_key,
+	camellia128_init,
+	std_resync,
+	camellia_block_encrypt,
+	camellia_block_decrypt,
+	camellia_cfb_encrypt,
+	camellia_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+
+/* Camellia with 256-bit key (CAMELLIA) */
+
+#define KEYBITS_CAMELLIA256 256
+
+static int
+camellia256_init(pgp_crypt_t *crypt)
+{
+	if (crypt->encrypt_key) {
+		free(crypt->encrypt_key);
+	}
+	if ((crypt->encrypt_key = calloc(1, sizeof(CAMELLIA_KEY))) == NULL) {
+		(void) fprintf(stderr, "camellia256_init: alloc failure\n");
+		return 0;
+	}
+	if (Camellia_set_key(crypt->key, KEYBITS_CAMELLIA256, crypt->encrypt_key)) {
+		fprintf(stderr, "camellia256_init: Error setting encrypt_key\n");
+	}
+	if (crypt->decrypt_key) {
+		free(crypt->decrypt_key);
+	}
+	if ((crypt->decrypt_key = calloc(1, sizeof(CAMELLIA_KEY))) == NULL) {
+		(void) fprintf(stderr, "camellia256_init: alloc failure\n");
+		return 0;
+	}
+	if (Camellia_set_key(crypt->key, KEYBITS_CAMELLIA256, crypt->decrypt_key)) {
+		fprintf(stderr, "camellia256_init: Error setting decrypt_key\n");
+	}
+	return 1;
+}
+
+static const pgp_crypt_t camellia256 =
+{
+	PGP_SA_CAMELLIA_256,
+	CAMELLIA_BLOCK_SIZE,
+	KEYBITS_CAMELLIA256 / 8,
+	std_set_iv,
+	std_set_key,
+	camellia256_init,
+	std_resync,
+	camellia_block_encrypt,
+	camellia_block_decrypt,
+	camellia_cfb_encrypt,
+	camellia_cfb_decrypt,
+	std_finish,
+	TRAILER
+};
+#endif
+
+
+static const pgp_crypt_t *
+get_proto(pgp_symm_alg_t alg)
+{
+	switch (alg) {
+	case PGP_SA_CAST5:
+		return &cast5;
+#ifndef OPENSSL_NO_IDEA
+	case PGP_SA_IDEA:
+		return &idea;
+#endif				/* OPENSSL_NO_IDEA */
+	case PGP_SA_AES_128:
+		return &aes128;
+	case PGP_SA_AES_256:
+		return &aes256;
+#if defined(HAVE_OPENSSL_CAMELLIA_H) && !defined(OPENSSL_NO_CAMELLIA)
+	case PGP_SA_CAMELLIA_128:
+		return &camellia128;
+	case PGP_SA_CAMELLIA_256:
+		return &camellia256;
+#endif
+	case PGP_SA_TRIPLEDES:
+		return &tripledes;
+	default:
+		(void) fprintf(stderr, "Unknown algorithm: %d (%s)\n",
+			alg, pgp_show_symm_alg(alg));
+	}
+	return NULL;
+}
+
+int
+pgp_crypt_any(pgp_crypt_t *crypt, pgp_symm_alg_t alg)
+{
+	const pgp_crypt_t *ptr = get_proto(alg);
+
+	if (ptr) {
+		*crypt = *ptr;
+		return 1;
+	} else {
+		(void) memset(crypt, 0x0, sizeof(*crypt));
+		return 0;
+	}
+}
+
+unsigned
+pgp_block_size(pgp_symm_alg_t alg)
+{
+	const pgp_crypt_t *p = get_proto(alg);
+
+	return (p == NULL) ? 0 : (unsigned)p->blocksize;
+}
+
+unsigned
+pgp_key_size(pgp_symm_alg_t alg)
+{
+	const pgp_crypt_t *p = get_proto(alg);
+
+	return (p == NULL) ? 0 : (unsigned)p->keysize;
+}
+
+void
+pgp_encrypt_init(pgp_crypt_t *encrypt)
+{
+	/* \todo should there be a separate pgp_encrypt_init? */
+	pgp_decrypt_init(encrypt);
+}
+
+void
+pgp_decrypt_init(pgp_crypt_t *decrypt)
+{
+	decrypt->base_init(decrypt);
+	decrypt->block_encrypt(decrypt, decrypt->siv, decrypt->iv);
+	(void) memcpy(decrypt->civ, decrypt->siv, decrypt->blocksize);
+	decrypt->num = 0;
+}
+
+size_t
+pgp_decrypt_se(pgp_crypt_t *decrypt, void *outvoid, const void *invoid,
+		size_t count)
+{
+	const uint8_t	*in = invoid;
+	uint8_t		*out = outvoid;
+	int              saved = (int)count;
+
+	/*
+	 * in order to support v3's weird resyncing we have to implement CFB
+	 * mode ourselves
+	 */
+	while (count-- > 0) {
+		uint8_t   t;
+
+		if ((size_t) decrypt->num == decrypt->blocksize) {
+			(void) memcpy(decrypt->siv, decrypt->civ,
+					decrypt->blocksize);
+			decrypt->block_decrypt(decrypt, decrypt->civ,
+					decrypt->civ);
+			decrypt->num = 0;
+		}
+		t = decrypt->civ[decrypt->num];
+		*out++ = t ^ (decrypt->civ[decrypt->num++] = *in++);
+	}
+
+	return (size_t)saved;
+}
+
+size_t
+pgp_encrypt_se(pgp_crypt_t *encrypt, void *outvoid, const void *invoid,
+	       size_t count)
+{
+	const uint8_t	*in = invoid;
+	uint8_t		*out = outvoid;
+	int              saved = (int)count;
+
+	/*
+	 * in order to support v3's weird resyncing we have to implement CFB
+	 * mode ourselves
+	 */
+	while (count-- > 0) {
+		if ((size_t) encrypt->num == encrypt->blocksize) {
+			(void) memcpy(encrypt->siv, encrypt->civ,
+					encrypt->blocksize);
+			encrypt->block_encrypt(encrypt, encrypt->civ,
+					encrypt->civ);
+			encrypt->num = 0;
+		}
+		encrypt->civ[encrypt->num] = *out++ =
+				encrypt->civ[encrypt->num] ^ *in++;
+		++encrypt->num;
+	}
+
+	return (size_t)saved;
+}
+
+/**
+\ingroup HighLevel_Supported
+\brief Is this Symmetric Algorithm supported?
+\param alg Symmetric Algorithm to check
+\return 1 if supported; else 0
+*/
+unsigned
+pgp_is_sa_supported(pgp_symm_alg_t alg)
+{
+	switch (alg) {
+	case PGP_SA_AES_128:
+	case PGP_SA_AES_256:
+	case PGP_SA_CAST5:
+	case PGP_SA_TRIPLEDES:
+#if defined(HAVE_OPENSSL_CAMELLIA_H) && !defined(OPENSSL_NO_CAMELLIA)
+	case PGP_SA_CAMELLIA_128:
+	case PGP_SA_CAMELLIA_256:
+#endif
+#ifndef OPENSSL_NO_IDEA
+	case PGP_SA_IDEA:
+#endif
+		return 1;
+
+	default:
+		fprintf(stderr, "\nWarning: %s not supported\n",
+			pgp_show_symm_alg(alg));
+		return 0;
+	}
+}
+
+size_t
+pgp_encrypt_se_ip(pgp_crypt_t *crypt, void *out, const void *in,
+		  size_t count)
+{
+	if (!pgp_is_sa_supported(crypt->alg)) {
+		return 0;
+	}
+
+	crypt->cfb_encrypt(crypt, out, in, count);
+
+	/* \todo test this number was encrypted */
+	return count;
+}
+
+size_t
+pgp_decrypt_se_ip(pgp_crypt_t *crypt, void *out, const void *in,
+		  size_t count)
+{
+	if (!pgp_is_sa_supported(crypt->alg)) {
+		return 0;
+	}
+
+	crypt->cfb_decrypt(crypt, out, in, count);
+
+	/* \todo check this number was in fact decrypted */
+	return count;
+}
diff --git a/netpgp/validate.c b/netpgp/validate.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8228bc23070b2e82e72fcbeba87c046bec816bd
--- /dev/null
+++ b/netpgp/validate.c
@@ -0,0 +1,1216 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "netpgp/packet-parse.h"
+#include "netpgp/packet-show.h"
+#include "netpgp/keyring.h"
+#include "netpgp/signature.h"
+#include "netpgp/netpgpsdk.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/memory.h"
+#include "netpgp/packet.h"
+#include "netpgp/crypto.h"
+#include "netpgp/validate.h"
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if defined(ANDROID) || defined(__ANDROID__)
+char* getpass (const char *prompt)
+{
+	return strdup("");
+}
+#endif
+
+// FIXME to support seckey decryption again.
+//
+// static int
+// keydata_reader(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
+// 	       pgp_reader_t *readinfo,
+// 	       pgp_cbdata_t *cbinfo)
+// {
+// 	validate_reader_t *reader = pgp_reader_get_arg(readinfo);
+//
+// 	__PGP_USED(stream);
+// 	__PGP_USED(errors);
+// 	__PGP_USED(cbinfo);
+// 	if (reader->offset == reader->key->packets[reader->packet].length) {
+// 		reader->packet += 1;
+// 		reader->offset = 0;
+// 	}
+// 	if (reader->packet == reader->key->packetc) {
+// 		return 0;
+// 	}
+//
+// 	/*
+// 	 * we should never be asked to cross a packet boundary in a single
+// 	 * read
+// 	 */
+// 	if (reader->key->packets[reader->packet].length <
+// 			reader->offset + length) {
+// 		(void) fprintf(stderr, "keydata_reader: weird length\n");
+// 		return 0;
+// 	}
+//
+// 	(void) memcpy(dest,
+// 		&reader->key->packets[reader->packet].raw[reader->offset],
+// 		length);
+// 	reader->offset += (unsigned)length;
+//
+// 	return (int)length;
+// }
+
+static void
+free_sig_info(pgp_sig_info_t *sig)
+{
+    pgp_free_sig_info(sig);
+	free(sig);
+}
+
+
+static int
+add_sig_to_list(const pgp_sig_info_t *sig, pgp_sig_info_t **sigs,
+			unsigned *count)
+{
+	pgp_sig_info_t	*newsigs;
+
+	if (*count == 0) {
+		newsigs = calloc(*count + 1, sizeof(pgp_sig_info_t));
+	} else {
+		newsigs = realloc(*sigs,
+				(*count + 1) * sizeof(pgp_sig_info_t));
+	}
+	if (newsigs == NULL) {
+		(void) fprintf(stderr, "add_sig_to_list: alloc failure\n");
+		return 0;
+	}
+	*sigs = newsigs;
+	copy_sig_info(&(*sigs)[*count], sig);
+	*count += 1;
+	return 1;
+}
+
+/*
+The hash value is calculated by the following method:
++ hash the data using the given digest algorithm
++ hash the hash value onto the end
++ hash the trailer - 6 bytes
+  [PGP_V4][0xff][len >> 24][len >> 16][len >> 8][len & 0xff]
+to give the final hash value that is checked against the one in the signature
+*/
+
+/* Does the signed hash match the given hash? */
+unsigned
+check_binary_sig(const uint8_t *data,
+		const unsigned len,
+		const pgp_sig_t *sig,
+		const pgp_pubkey_t *signer)
+{
+	unsigned    hashedlen;
+	pgp_hash_t	hash;
+	unsigned	n;
+	uint8_t		hashout[PGP_MAX_HASH_SIZE];
+	uint8_t		trailer[6];
+
+	pgp_hash_any(&hash, sig->info.hash_alg);
+	if (!hash.init(&hash)) {
+		(void) fprintf(stderr, "check_binary_sig: bad hash init\n");
+		return 0;
+	}
+	hash.add(&hash, data, len);
+	switch (sig->info.version) {
+	case PGP_V3:
+		trailer[0] = sig->info.type;
+		trailer[1] = (unsigned)(sig->info.birthtime) >> 24;
+		trailer[2] = (unsigned)(sig->info.birthtime) >> 16;
+		trailer[3] = (unsigned)(sig->info.birthtime) >> 8;
+		trailer[4] = (uint8_t)(sig->info.birthtime);
+		hash.add(&hash, trailer, 5);
+		break;
+
+	case PGP_V4:
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "v4 hash", sig->info.v4_hashed,
+					sig->info.v4_hashlen);
+		}
+		hash.add(&hash, sig->info.v4_hashed, (unsigned)sig->info.v4_hashlen);
+		trailer[0] = 0x04;	/* version */
+		trailer[1] = 0xFF;
+		hashedlen = (unsigned)sig->info.v4_hashlen;
+		trailer[2] = (uint8_t)(hashedlen >> 24);
+		trailer[3] = (uint8_t)(hashedlen >> 16);
+		trailer[4] = (uint8_t)(hashedlen >> 8);
+		trailer[5] = (uint8_t)(hashedlen);
+		hash.add(&hash, trailer, 6);
+		break;
+
+	default:
+		(void) fprintf(stderr, "Invalid signature version %d\n",
+				sig->info.version);
+		return 0;
+	}
+
+	n = hash.finish(&hash, hashout);
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stdout, "hash out", hashout, n);
+	}
+	return pgp_check_sig(hashout, n, sig, signer);
+}
+
+static void validate_key_cb_free (validate_key_cb_t *vdata){
+
+    /* Free according to previous allocated type */
+    if (vdata->type == PGP_PTAG_CT_PUBLIC_KEY) {
+        pgp_pubkey_free(&vdata->key.pubkey);
+        pgp_pubkey_free(&vdata->subkey.pubkey);
+    } else if (vdata->type == PGP_PTAG_CT_SECRET_KEY) {
+        pgp_seckey_free(&vdata->key.seckey);
+        if(vdata->subkey.seckey.pubkey.alg)
+            pgp_seckey_free(&vdata->subkey.seckey);
+    }
+    memset(&vdata->key, 0, sizeof(vdata->key));
+    memset(&vdata->subkey, 0, sizeof(vdata->subkey));
+	vdata->type = PGP_PTAG_CT_RESERVED; /* 0 */
+
+	pgp_userid_free(&vdata->userid);
+	pgp_data_free(&vdata->userattr);
+
+	if(vdata->valid_sig_info.key_alg) {
+        pgp_free_sig_info(&vdata->valid_sig_info);
+    }
+}
+
+pgp_cb_ret_t
+pgp_validate_key_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	 *content = &pkt->u;
+	validate_key_cb_t	 *vdata;
+	pgp_error_t		**errors;
+	pgp_io_t		 *io;
+	unsigned		  valid = 0;
+
+	io = cbinfo->io;
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(io->errs, "%s\n",
+				pgp_show_packet_tag(pkt->tag));
+	}
+	vdata = pgp_callback_arg(cbinfo);
+	errors = pgp_callback_errors(cbinfo);
+
+    vdata->sig_is_valid &= pkt->tag == PGP_PARSER_PACKET_END;
+
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_PUBLIC_KEY:
+        validate_key_cb_free(vdata);
+		vdata->key.pubkey = content->pubkey;
+	    pgp_keyid(vdata->pubkeyid, PGP_KEY_ID_SIZE,
+                  &vdata->key.pubkey, PGP_HASH_SHA1); /* TODO v3*/
+
+		vdata->last_seen = LS_PRIMARY;
+	    vdata->type = PGP_PTAG_CT_PUBLIC_KEY;
+        vdata->not_commited = 1;
+		return PGP_KEEP_MEMORY;
+
+	case PGP_PTAG_CT_SECRET_KEY:
+        /* TODO
+         * check pubkey seckey consistency  */
+        validate_key_cb_free(vdata);
+		vdata->key.seckey = content->seckey;
+	    pgp_keyid(vdata->pubkeyid, PGP_KEY_ID_SIZE,
+                  &vdata->key.seckey.pubkey, PGP_HASH_SHA1); /* TODO v3*/
+		vdata->last_seen = LS_PRIMARY;
+	    vdata->type = PGP_PTAG_CT_SECRET_KEY;
+        vdata->not_commited = 1;
+		return PGP_KEEP_MEMORY;
+
+	case PGP_PTAG_CT_PUBLIC_SUBKEY:
+		if(vdata->type == PGP_PTAG_CT_PUBLIC_KEY && (
+               vdata->last_seen == LS_ID ||
+               vdata->last_seen == LS_ATTRIBUTE)){
+            pgp_pubkey_free(&vdata->subkey.pubkey);
+            vdata->subkey.pubkey = content->pubkey;
+            vdata->last_seen = LS_SUBKEY;
+            return PGP_KEEP_MEMORY;
+        }else{
+            (void) fprintf(io->errs,
+                "pgp_validate_key_cb: unexpected public subkey packet");
+            vdata->last_seen = LS_UNKNOWN;
+			return PGP_RELEASE_MEMORY;
+        }
+
+	case PGP_PTAG_CT_SECRET_SUBKEY:
+        /* TODO
+         * check pubkey seckey consistency */
+		if(vdata->type == PGP_PTAG_CT_SECRET_KEY && (
+               vdata->last_seen == LS_ID ||
+               vdata->last_seen == LS_ATTRIBUTE)){
+	        if(vdata->subkey.seckey.pubkey.alg)
+                pgp_seckey_free(&vdata->subkey.seckey);
+            vdata->subkey.seckey = content->seckey;
+            vdata->last_seen = LS_SUBKEY;
+            return PGP_KEEP_MEMORY;
+        }else{
+            (void) fprintf(io->errs,
+                "pgp_validate_key_cb: unexpected secret subkey packet");
+            vdata->last_seen = LS_UNKNOWN;
+			return PGP_RELEASE_MEMORY;
+        }
+
+	case PGP_PTAG_CT_USER_ID:
+		if(vdata->last_seen == LS_PRIMARY ||
+           vdata->last_seen == LS_ATTRIBUTE ||
+           vdata->last_seen == LS_ID){
+            if (vdata->userid) {
+                pgp_userid_free(&vdata->userid);
+            }
+            vdata->userid = content->userid;
+            vdata->last_seen = LS_ID;
+            return PGP_KEEP_MEMORY;
+        }else{
+            (void) fprintf(io->errs,
+                "pgp_validate_key_cb: unexpected userID packet");
+            vdata->last_seen = LS_UNKNOWN;
+			return PGP_RELEASE_MEMORY;
+        }
+
+	case PGP_PTAG_CT_USER_ATTR:
+		if(vdata->last_seen == LS_PRIMARY ||
+           vdata->last_seen == LS_ATTRIBUTE ||
+           vdata->last_seen == LS_ID){
+            if (content->userattr.len == 0) {
+                (void) fprintf(io->errs,
+                    "pgp_validate_key_cb: user attribute length 0");
+                vdata->last_seen = LS_UNKNOWN;
+			    return PGP_RELEASE_MEMORY;
+            }
+            (void) fprintf(io->outs, "user attribute, length=%d\n",
+                (int) content->userattr.len);
+            if (vdata->userattr.len) {
+                pgp_data_free(&vdata->userattr);
+            }
+            vdata->userattr = content->userattr;
+            vdata->last_seen = LS_ATTRIBUTE;
+            return PGP_KEEP_MEMORY;
+        }else{
+			(void) fprintf(io->errs,
+				"pgp_validate_key_cb: unexpected user attribute\n");
+            vdata->last_seen = LS_UNKNOWN;
+			return PGP_RELEASE_MEMORY;
+        }
+	case PGP_PTAG_CT_SIGNATURE:	/* V3 sigs */
+	case PGP_PTAG_CT_SIGNATURE_FOOTER:{	/* V4 sigs */
+        pgp_pubkey_t *sigkey = NULL;
+        pgp_pubkey_t *primary_pubkey;
+
+		if(vdata->last_seen == LS_UNKNOWN)
+            break;
+
+        primary_pubkey =
+                   (vdata->type == PGP_PTAG_CT_PUBLIC_KEY) ?
+                       &vdata->key.pubkey:
+                       &vdata->key.seckey.pubkey;
+
+        if(vdata->keyring){
+            unsigned		  from;
+            from = 0;
+            /* Returned key ignored, care about ID-targeted pubkey only */
+            pgp_getkeybyid(io, vdata->keyring,
+                         content->sig.info.signer_id,
+                         &from, &sigkey, NULL,
+                         1, 0); /* reject revoked, accept expired */
+        } else {
+            /* If no keyring is given to check against
+             * then this is a self certification check.
+             * First ensure signature issuer ID is pubkey's ID*/
+            if(memcmp(vdata->pubkeyid,
+                      content->sig.info.signer_id,
+                      PGP_KEY_ID_SIZE) == 0){
+                sigkey = primary_pubkey;
+            }
+        }
+        if (!sigkey) {
+            if (vdata->result && !add_sig_to_list(&content->sig.info,
+                &vdata->result->unknown_sigs,
+                &vdata->result->unknownc)) {
+                    (void) fprintf(io->errs,
+                    "pgp_validate_key_cb: out of memory");
+                    return PGP_FINISHED;
+            }
+            break;
+        }
+		switch (content->sig.info.type) {
+		case PGP_CERT_GENERIC:
+		case PGP_CERT_PERSONA:
+		case PGP_CERT_CASUAL:
+		case PGP_CERT_POSITIVE:
+		case PGP_SIG_REV_CERT:
+			if(vdata->last_seen == LS_ID){
+			    valid = pgp_check_useridcert_sig(
+                    primary_pubkey,
+					vdata->userid,
+					&content->sig,
+					sigkey);
+            } else if(vdata->last_seen == LS_ATTRIBUTE) {
+			    valid = pgp_check_userattrcert_sig(
+                    primary_pubkey,
+					&vdata->userattr,
+					&content->sig,
+                    sigkey);
+            }
+			break;
+
+		case PGP_SIG_REV_SUBKEY:
+		case PGP_SIG_SUBKEY:
+			/*
+			 * we ensure that the signing key is the
+			 * primary key we are validating, "vdata->pubkey".
+			 */
+			if(vdata->last_seen == LS_SUBKEY &&
+               memcmp(vdata->pubkeyid,
+                      content->sig.info.signer_id,
+                      PGP_KEY_ID_SIZE) == 0 )
+            {
+                valid = pgp_check_subkey_sig(
+                    primary_pubkey,
+                    (vdata->type == PGP_PTAG_CT_PUBLIC_KEY) ?
+                        &vdata->subkey.pubkey:
+                        &vdata->subkey.seckey.pubkey,
+                    &content->sig,
+                    primary_pubkey);
+            }
+			break;
+
+		case PGP_SIG_REV_KEY:
+		case PGP_SIG_DIRECT:
+			if(vdata->last_seen == LS_PRIMARY){
+                valid = pgp_check_direct_sig(
+                    primary_pubkey,
+                    &content->sig,
+                    sigkey);
+            }
+			break;
+
+		case PGP_SIG_STANDALONE:
+		case PGP_SIG_PRIMARY:
+		case PGP_SIG_TIMESTAMP:
+		case PGP_SIG_3RD_PARTY:
+            if (vdata->result){
+                PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
+                    "Sig Verification type 0x%02x not done yet\n",
+                    content->sig.info.type);
+                break;
+            }
+
+		default:
+            if (vdata->result){
+                PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
+                        "Unexpected signature type 0x%02x\n",
+                            content->sig.info.type);
+            }
+		}
+
+		if (valid) {
+			if (vdata->result && !add_sig_to_list(&content->sig.info,
+				&vdata->result->valid_sigs,
+				&vdata->result->validc)) {
+				PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED, "%s",
+				    "Can't add valid sig to list\n");
+			}
+	        vdata->sig_is_valid = 1;
+            copy_sig_info(&vdata->valid_sig_info,
+                          &content->sig.info);
+		} else if (vdata->result){
+			PGP_ERROR_1(errors, PGP_E_V_BAD_SIGNATURE, "%s",
+			    "Bad Sig");
+			if (!add_sig_to_list(&content->sig.info,
+				&vdata->result->invalid_sigs,
+				&vdata->result->invalidc)) {
+				PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED, "%s",
+				    "Can't add invalid sig to list\n");
+			}
+		}
+		break;
+    }
+	case PGP_PARSER_PACKET_END:
+        if(vdata->sig_is_valid){
+            pgp_cb_ret_t ret = PGP_RELEASE_MEMORY;
+            if(vdata->on_valid){
+                ret = vdata->on_valid(vdata, &content->packet);
+            }
+	        vdata->sig_is_valid = 0;
+            vdata->not_commited = 0;
+            return ret;
+        }
+        return PGP_RELEASE_MEMORY;
+
+	/* ignore these */
+	case PGP_PARSER_PTAG:
+	case PGP_PTAG_CT_SIGNATURE_HEADER:
+	case PGP_PTAG_CT_TRUST:
+		break;
+
+	// case PGP_GET_PASSPHRASE:
+	// 	if (vdata->getpassphrase) {
+	// 		return vdata->getpassphrase(pkt, cbinfo);
+	// 	}
+	// 	break;
+
+	default:
+		// (void) fprintf(stderr, "unexpected tag=0x%x\n", pkt->tag);
+	    return PGP_RELEASE_MEMORY;
+	}
+	return PGP_RELEASE_MEMORY;
+}
+
+pgp_cb_ret_t
+validate_data_cb(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
+{
+	const pgp_contents_t	 *content = &pkt->u;
+    pgp_key_t	 *signer;
+	validate_data_cb_t	 *data;
+	pgp_pubkey_t		 *sigkey;
+	pgp_error_t		**errors;
+	pgp_io_t		 *io;
+	unsigned		  from;
+	unsigned		  valid = 0;
+
+	io = cbinfo->io;
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(io->errs, "validate_data_cb: %s\n",
+				pgp_show_packet_tag(pkt->tag));
+	}
+	data = pgp_callback_arg(cbinfo);
+	errors = pgp_callback_errors(cbinfo);
+	switch (pkt->tag) {
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
+		/*
+		 * ignore - this gives us the "Armor Header" line "Hash:
+		 * SHA1" or similar
+		 */
+		break;
+
+	case PGP_PTAG_CT_LITDATA_HEADER:
+		/* ignore */
+		break;
+
+	case PGP_PTAG_CT_LITDATA_BODY:
+		data->data.litdata_body = content->litdata_body;
+		data->type = LITDATA;
+		pgp_memory_add(data->mem, data->data.litdata_body.data,
+				       data->data.litdata_body.length);
+		return PGP_KEEP_MEMORY;
+
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
+		data->data.cleartext_body = content->cleartext_body;
+		data->type = SIGNED_CLEARTEXT;
+		pgp_memory_add(data->mem, data->data.cleartext_body.data,
+			       data->data.cleartext_body.length);
+		return PGP_KEEP_MEMORY;
+
+	case PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
+		/* this gives us an pgp_hash_t struct */
+		break;
+
+	case PGP_PTAG_CT_SIGNATURE:	/* V3 sigs */
+	case PGP_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(io->outs, "hashed data", content->sig.info.v4_hashed,
+					content->sig.info.v4_hashlen);
+			hexdump(io->outs, "signer id", content->sig.info.signer_id,
+				sizeof(content->sig.info.signer_id));
+		}
+		from = 0;
+        sigkey = NULL;
+		signer = pgp_getkeybyid(io, data->keyring,
+					 content->sig.info.signer_id, &from, &sigkey, NULL,
+                     0, 0); /* check neither revocation nor expiry */
+		if (!signer || !sigkey) {
+			PGP_ERROR_1(errors, PGP_E_V_UNKNOWN_SIGNER,
+			    "%s", "Unknown Signer");
+			if (!add_sig_to_list(&content->sig.info,
+					&data->result->unknown_sigs,
+					&data->result->unknownc)) {
+				PGP_ERROR_1(errors, PGP_E_V_UNKNOWN_SIGNER,
+				    "%s", "Can't add unknown sig to list");
+			}
+			break;
+		}
+		if (content->sig.info.birthtime_set) {
+			data->result->birthtime = content->sig.info.birthtime;
+		}
+		if (content->sig.info.duration_set) {
+			data->result->duration = content->sig.info.duration;
+		}
+		switch (content->sig.info.type) {
+		case PGP_SIG_BINARY:
+		case PGP_SIG_TEXT:
+			if (pgp_mem_len(data->mem) == 0){
+               if(data->detachname) {
+				/* check we have seen some data */
+				/* if not, need to read from detached name */
+				(void) fprintf(io->errs,
+				"netpgp: assuming signed data in \"%s\"\n",
+					data->detachname);
+				data->mem = pgp_memory_new();
+				pgp_mem_readfile(data->mem, data->detachname);
+               }
+            }
+			if (pgp_get_debug_level(__FILE__)) {
+				hexdump(stderr, "sig dump", (const uint8_t *)(const void *)&content->sig,
+					sizeof(content->sig));
+			}
+			valid = check_binary_sig(pgp_mem_data(data->mem),
+					(const unsigned)pgp_mem_len(data->mem),
+					&content->sig,
+					sigkey);
+			break;
+
+		default:
+			PGP_ERROR_1(errors, PGP_E_UNIMPLEMENTED,
+				    "No Sig Verification type 0x%02x yet\n",
+				    content->sig.info.type);
+			break;
+
+		}
+
+		if (valid) {
+			if (!add_sig_to_list(&content->sig.info,
+					&data->result->valid_sigs,
+					&data->result->validc)) {
+				PGP_ERROR_1(errors, PGP_E_V_BAD_SIGNATURE,
+				    "%s", "Can't add good sig to list");
+			}
+		} else {
+			PGP_ERROR_1(errors, PGP_E_V_BAD_SIGNATURE,
+			    "%s", "Bad Signature");
+			if (!add_sig_to_list(&content->sig.info,
+					&data->result->invalid_sigs,
+					&data->result->invalidc)) {
+				PGP_ERROR_1(errors, PGP_E_V_BAD_SIGNATURE, "%s",
+					"Can't add good sig to list");
+			}
+		}
+		break;
+
+		/* ignore these */
+	case PGP_PARSER_PTAG:
+	case PGP_PTAG_CT_SIGNATURE_HEADER:
+	case PGP_PTAG_CT_ARMOUR_HEADER:
+	case PGP_PTAG_CT_ARMOUR_TRAILER:
+	case PGP_PTAG_CT_1_PASS_SIG:
+		break;
+
+	case PGP_PARSER_PACKET_END:
+		break;
+
+	default:
+		PGP_ERROR_1(errors, PGP_E_V_NO_SIGNATURE, "%s", "No signature");
+		break;
+	}
+	return PGP_RELEASE_MEMORY;
+}
+
+static char *
+fmtsecs(int64_t n, char *buf, size_t size)
+{
+	if (n > 365 * 24 * 60 * 60) {
+		n /= (365 * 24 * 60 * 60);
+		(void) snprintf(buf, size, "%" PRId64 " year%s", n, (n == 1) ? "" : "s");
+		return buf;
+	}
+	if (n > 30 * 24 * 60 * 60) {
+		n /= (30 * 24 * 60 * 60);
+		(void) snprintf(buf, size, "%" PRId64 " month%s", n, (n == 1) ? "" : "s");
+		return buf;
+	}
+	if (n > 24 * 60 * 60) {
+		n /= (24 * 60 * 60);
+		(void) snprintf(buf, size, "%" PRId64 " day%s", n, (n == 1) ? "" : "s");
+		return buf;
+	}
+	if (n > 60 * 60) {
+		n /= (60 * 60);
+		(void) snprintf(buf, size, "%" PRId64 " hour%s", n, (n == 1) ? "" : "s");
+		return buf;
+	}
+	if (n > 60) {
+		n /= 60;
+		(void) snprintf(buf, size, "%" PRId64 " minute%s", n, (n == 1) ? "" : "s");
+		return buf;
+	}
+	(void) snprintf(buf, size, "%" PRId64 " second%s", n, (n == 1) ? "" : "s");
+	return buf;
+}
+
+/**
+ * \ingroup HighLevel_Verify
+ * \brief Indicicates whether any errors were found
+ * \param result Validation result to check
+ * \return 0 if any invalid signatures or unknown signers
+ 	or no valid signatures; else 1
+ */
+static unsigned
+validate_result_status(FILE *errs, const char *f, pgp_validation_t *val)
+{
+	time_t	now;
+	time_t	t;
+	char	buf[128];
+
+	now = time(NULL);
+	if (now < val->birthtime) {
+		/* signature is not valid yet! */
+		if (f) {
+			(void) fprintf(errs, "\"%s\": ", f);
+		} else {
+			(void) fprintf(errs, "memory ");
+		}
+		(void) fprintf(errs,
+			"signature not valid until %.24s (%s)\n",
+			ctime(&val->birthtime),
+			fmtsecs((int64_t)(val->birthtime - now), buf, sizeof(buf)));
+		return 0;
+	}
+	if (val->duration != 0 && now > val->birthtime + val->duration) {
+		/* signature has expired */
+		t = val->duration + val->birthtime;
+		if (f) {
+			(void) fprintf(errs, "\"%s\": ", f);
+		} else {
+			(void) fprintf(errs, "memory ");
+		}
+		(void) fprintf(errs,
+			"signature not valid after %.24s (%s ago)\n",
+			ctime(&t),
+			fmtsecs((int64_t)(now - t), buf, sizeof(buf)));
+		return 0;
+	}
+	return val->validc && !val->invalidc && !val->unknownc;
+}
+
+typedef struct key_filter_cb_t{
+	pgp_keyring_t *destpubring;
+	pgp_keyring_t *destsecring;
+    pgp_key_t *pubkey;
+    pgp_key_t *seckey;
+} key_filter_cb_t;
+
+static pgp_cb_ret_t key_filter_cb (
+    validate_key_cb_t *vdata,
+    const pgp_subpacket_t *sigpkt)
+{
+    pgp_key_t		*pubkey;
+    pgp_key_t		*seckey = NULL;
+    key_filter_cb_t *filter = vdata->on_valid_args;
+
+    if(vdata->not_commited){
+
+        if((filter->pubkey = pgp_ensure_pubkey(filter->destpubring,
+                (vdata->type == PGP_PTAG_CT_PUBLIC_KEY) ?
+                    &vdata->key.pubkey :
+                    &vdata->key.seckey.pubkey,
+                vdata->pubkeyid))==NULL){
+            return PGP_RELEASE_MEMORY;
+        }
+
+        filter->seckey = NULL;
+	    if (vdata->type == PGP_PTAG_CT_SECRET_KEY && filter->destsecring) {
+            if((filter->seckey = pgp_ensure_seckey(
+                            filter->destsecring,
+                            &vdata->key.seckey,
+                            vdata->pubkeyid))==NULL){
+                return PGP_RELEASE_MEMORY;
+            }
+        }
+        /* TODO get seckey by ID id even if given key is public
+         *      in order to update uids an attributes from pubkey */
+    }
+
+    pubkey = filter->pubkey;
+    if(pubkey == NULL)
+        return PGP_RELEASE_MEMORY;
+
+    if (vdata->type == PGP_PTAG_CT_SECRET_KEY) {
+        seckey = filter->seckey;
+    }
+
+    switch(vdata->last_seen){
+    case LS_PRIMARY:
+
+        pgp_add_directsig(pubkey, sigpkt, &vdata->valid_sig_info);
+
+	    if (seckey) {
+            pgp_add_directsig(seckey, sigpkt, &vdata->valid_sig_info);
+        }
+        break;
+    case LS_ID:
+
+        pgp_update_userid(pubkey, vdata->userid, sigpkt, &vdata->valid_sig_info);
+	    if (seckey) {
+            pgp_update_userid(seckey, vdata->userid, sigpkt, &vdata->valid_sig_info);
+        }
+
+        break;
+    case LS_ATTRIBUTE:
+        /* TODO */
+        break;
+    case LS_SUBKEY:
+        pgp_update_subkey(pubkey,
+                vdata->type, &vdata->subkey,
+                sigpkt, &vdata->valid_sig_info);
+	    if (seckey) {
+            pgp_update_subkey(seckey,
+                    vdata->type, &vdata->subkey,
+                    sigpkt, &vdata->valid_sig_info);
+        }
+
+        break;
+    default:
+        break;
+    }
+	return PGP_RELEASE_MEMORY;
+}
+
+#if 0 //////
+unsigned
+pgp_filter_keys_fileread(
+            pgp_io_t *io,
+            pgp_keyring_t *destpubring,
+            pgp_keyring_t *destsecring,
+            pgp_keyring_t *certring,
+			const unsigned armour,
+			const char *filename)
+{
+	pgp_stream_t	*stream;
+	validate_key_cb_t vdata;
+    key_filter_cb_t filter;
+	unsigned	 res = 1;
+	int		 fd;
+
+	(void) memset(&vdata, 0x0, sizeof(vdata));
+	vdata.result = NULL;
+	vdata.getpassphrase = NULL;
+
+	(void) memset(&filter, 0x0, sizeof(filter));
+    filter.destpubring = destpubring;
+    filter.destsecring = destsecring;
+
+    fd = pgp_setup_file_read(io,
+			&stream,filename,
+			&vdata,
+            pgp_validate_key_cb,
+			1);
+
+	if (fd < 0) {
+		perror(filename);
+		return 0;
+	}
+
+	pgp_parse_options(stream, PGP_PTAG_SS_ALL, PGP_PARSE_PARSED);
+
+	if (armour) {
+		pgp_reader_push_dearmour(stream);
+	}
+
+	vdata.keyring = certring;
+
+	vdata.on_valid = &key_filter_cb;
+	vdata.on_valid_args = &filter;
+
+	res = pgp_parse(stream, 0);
+
+    validate_key_cb_free(&vdata);
+
+	if (armour) {
+		pgp_reader_pop_dearmour(stream);
+	}
+
+	(void)close(fd);
+
+	pgp_stream_delete(stream);
+
+	return res;
+}
+#endif
+
+unsigned
+pgp_filter_keys_from_mem(
+            pgp_io_t *io,
+            pgp_keyring_t *destpubring,
+            pgp_keyring_t *destsecring,
+            pgp_keyring_t *certring,
+            const unsigned armour,
+            pgp_memory_t *mem)
+{
+	pgp_stream_t *stream;
+	validate_key_cb_t vdata;
+    key_filter_cb_t filter;
+	unsigned res;
+
+	(void) memset(&vdata, 0x0, sizeof(vdata));
+	vdata.result = NULL;
+	vdata.getpassphrase = NULL;
+
+	(void) memset(&filter, 0x0, sizeof(filter));
+    filter.destpubring = destpubring;
+    filter.destsecring = destsecring;
+
+	//stream = pgp_new(sizeof(*stream)); -- Memory leak fixed by Delta Chat: not needed, stream is overwritten in pgp_setup_memory_read()
+	pgp_setup_memory_read(io, &stream, mem, &vdata, pgp_validate_key_cb, 1);
+	pgp_parse_options(stream, PGP_PTAG_SS_ALL, PGP_PARSE_PARSED); // the original code does not set PGP_PARSE_PARSED, however this seems to be a bug as this function was called before pgp_setup_memory_read() - as pgp_filter_keys_fileread() uses the same callback, I assume, PGP_PARSE_PARSED is the expected behaviour.
+
+	if (armour) {
+		pgp_reader_push_dearmour(stream);
+	}
+
+	vdata.keyring = certring;
+
+	vdata.on_valid = &key_filter_cb;
+	vdata.on_valid_args = &filter;
+
+	res = pgp_parse(stream, 0);
+
+    validate_key_cb_free(&vdata);
+
+	if (armour) {
+		pgp_reader_pop_dearmour(stream);
+	}
+
+	/* don't call teardown_memory_read because memory was passed in */
+	pgp_stream_delete(stream);
+	return res;
+}
+
+/**
+   \ingroup HighLevel_Verify
+   \brief Frees validation result and associated memory
+   \param result Struct to be freed
+   \note Must be called after validation functions
+*/
+void
+pgp_validate_result_free(pgp_validation_t *result)
+{
+	if (result != NULL) {
+		if (result->valid_sigs) {
+			free_sig_info(result->valid_sigs);
+		}
+		if (result->invalid_sigs) {
+			free_sig_info(result->invalid_sigs);
+		}
+		if (result->unknown_sigs) {
+			free_sig_info(result->unknown_sigs);
+		}
+		free(result);
+		/* result = NULL; - XXX unnecessary */
+	}
+}
+
+/**
+   \ingroup HighLevel_Verify
+   \brief Verifies the signatures in a signed file
+   \param result Where to put the result
+   \param filename Name of file to be validated
+   \param armoured Treat file as armoured, if set
+   \param keyring Keyring to use
+   \return 1 if signatures validate successfully;
+   	0 if signatures fail or there are no signatures
+   \note After verification, result holds the details of all keys which
+   have passed, failed and not been recognised.
+   \note It is the caller's responsiblity to call
+   	pgp_validate_result_free(result) after use.
+*/
+#if 0 ///////
+unsigned
+pgp_validate_file(pgp_io_t *io,
+			pgp_validation_t *result,
+			const char *infile,
+			const char *outfile,
+			const int user_says_armoured,
+			const pgp_keyring_t *keyring)
+{
+	validate_data_cb_t	 validation;
+	pgp_stream_t		*parse = NULL;
+	struct stat		 st;
+	const char		*signame;
+	const int		 printerrors = 1;
+	unsigned		 ret;
+	char			 f[MAXPATHLEN];
+	char			*dataname;
+	int			 realarmour;
+	int			 outfd = 0;
+	int			 infd;
+	int			 cc;
+
+	if (stat(infile, &st) < 0) {
+		(void) fprintf(io->errs,
+			"pgp_validate_file: can't open '%s'\n", infile);
+		return 0;
+	}
+	realarmour = user_says_armoured;
+	dataname = NULL;
+	signame = NULL;
+	cc = snprintf(f, sizeof(f), "%s", infile);
+	if (strcmp(&f[cc - 4], ".sig") == 0) {
+		/* we've been given a sigfile as infile */
+		f[cc - 4] = 0x0;
+		/* set dataname to name of file which was signed */
+		dataname = f;
+		signame = infile;
+	} else if (strcmp(&f[cc - 4], ".asc") == 0) {
+		/* we've been given an armored sigfile as infile */
+		f[cc - 4] = 0x0;
+		/* set dataname to name of file which was signed */
+		dataname = f;
+		signame = infile;
+		realarmour = 1;
+	} else {
+		signame = infile;
+	}
+	(void) memset(&validation, 0x0, sizeof(validation));
+	infd = pgp_setup_file_read(io, &parse, signame, &validation,
+				validate_data_cb, 1);
+	if (infd < 0) {
+		return 0;
+	}
+
+	if (dataname) {
+		validation.detachname = netpgp_strdup(dataname);
+	}
+
+	/* Set verification reader and handling options */
+	validation.result = result;
+	validation.keyring = keyring;
+	validation.mem = pgp_memory_new();
+	pgp_memory_init(validation.mem, 128);
+
+	if (realarmour) {
+		pgp_reader_push_dearmour(parse);
+	}
+
+	/* Do the verification */
+	pgp_parse(parse, !printerrors);
+
+	/* Tidy up */
+	if (realarmour) {
+		pgp_reader_pop_dearmour(parse);
+	}
+	pgp_teardown_file_read(parse, infd);
+
+	ret = validate_result_status(io->errs, infile, result);
+
+	/* this is triggered only for --cat output */
+	if (outfile) {
+		/* need to send validated output somewhere */
+		if (strcmp(outfile, "-") == 0) {
+			outfd = STDOUT_FILENO;
+		} else {
+			outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
+		}
+		if (outfd < 0) {
+			/* even if the signature was good, we can't
+			* write the file, so send back a bad return
+			* code */
+			ret = 0;
+		} else if (validate_result_status(io->errs, infile, result)) {
+			unsigned	 len;
+			char		*cp;
+			int		 i;
+
+			len = (unsigned)pgp_mem_len(validation.mem);
+			cp = pgp_mem_data(validation.mem);
+			for (i = 0 ; i < (int)len ; i += cc) {
+				cc = (int)write(outfd, &cp[i], (unsigned)(len - i));
+				if (cc < 0) {
+					(void) fprintf(io->errs,
+						"netpgp: short write\n");
+					ret = 0;
+					break;
+				}
+			}
+			if (strcmp(outfile, "-") != 0) {
+				(void) close(outfd);
+			}
+		}
+	}
+	pgp_memory_free(validation.mem);
+	return ret;
+}
+#endif //////
+
+/**
+   \ingroup HighLevel_Verify
+   \brief Verifies the signatures in a pgp_memory_t struct
+   \param result Where to put the result
+   \param mem Memory to be validated
+   \param user_says_armoured Treat data as armoured, if set
+   \param keyring Keyring to use
+   \param detachmem detached memory (free done in this call if provided)
+   \return 1 if signature validates successfully; 0 if not
+   \note After verification, result holds the details of all keys which
+   have passed, failed and not been recognised.
+   \note It is the caller's responsiblity to call
+   	pgp_validate_result_free(result) after use.
+*/
+static inline unsigned
+_pgp_validate_mem(pgp_io_t *io,
+			pgp_validation_t *result,
+			pgp_memory_t *mem,
+			pgp_memory_t **cat,
+			const int user_says_armoured,
+			const pgp_keyring_t *keyring,
+			pgp_memory_t *detachmem)
+{
+	validate_data_cb_t	 validation;
+	pgp_stream_t		*stream = NULL;
+	const int		 printerrors = 1;
+	int			 realarmour;
+
+	pgp_setup_memory_read(io, &stream, mem, &validation, validate_data_cb, 1);
+
+	/* Set verification reader and handling options */
+	(void) memset(&validation, 0x0, sizeof(validation));
+	validation.result = result;
+	validation.keyring = keyring;
+	if (detachmem) {
+        validation.mem = detachmem;
+    }else{
+        validation.mem = pgp_memory_new();
+        pgp_memory_init(validation.mem, 128);
+    }
+
+	if ((realarmour = user_says_armoured) != 0 ||
+	    strncmp(pgp_mem_data(mem),
+	    		"-----BEGIN PGP MESSAGE-----", 27) == 0) {
+		realarmour = 1;
+	}
+	if (realarmour) {
+		pgp_reader_push_dearmour(stream);
+	}
+
+	/* Do the verification */
+	pgp_parse(stream, !printerrors);
+
+	/* Tidy up */
+	if (realarmour) {
+		pgp_reader_pop_dearmour(stream);
+	}
+	pgp_teardown_memory_read(stream, mem);
+
+	/* this is triggered only for --cat output */
+	if (cat) {
+		/* need to send validated output somewhere */
+		*cat = validation.mem;
+	} else {
+        pgp_memory_free(validation.mem);
+	}
+
+	return validate_result_status(io->errs, NULL, result);
+}
+
+unsigned
+pgp_validate_mem(pgp_io_t *io,
+			pgp_validation_t *result,
+			pgp_memory_t *mem,
+			pgp_memory_t **cat,
+			const int user_says_armoured,
+			const pgp_keyring_t *keyring)
+{
+    return _pgp_validate_mem(io,
+			result,
+			mem,
+			cat,
+			user_says_armoured,
+			keyring,
+	        NULL);
+}
+
+unsigned
+pgp_validate_mem_detached(pgp_io_t *io,
+			pgp_validation_t *result,
+			pgp_memory_t *mem,
+			pgp_memory_t **cat,
+			const int user_says_armoured,
+			const pgp_keyring_t *keyring,
+			pgp_memory_t *detachmem)
+{
+    return _pgp_validate_mem(io,
+			result,
+			mem,
+			cat,
+			user_says_armoured,
+			keyring,
+	        detachmem);
+}
+
diff --git a/netpgp/writer.c b/netpgp/writer.c
new file mode 100644
index 0000000000000000000000000000000000000000..69e04f107f71703bb4f70f1405bcc4fb147409c0
--- /dev/null
+++ b/netpgp/writer.c
@@ -0,0 +1,1895 @@
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alistair Crooks (agc@NetBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
+ * All rights reserved.
+ * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
+ * their moral rights under the UK Copyright Design and Patents Act 1988 to
+ * be recorded as the authors of this copyright work.
+ *
+ * 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.
+ */
+
+/** \file
+ * This file contains the base functions used by the writers.
+ */
+#include "netpgp/config-netpgp.h"
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if defined(__NetBSD__)
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$NetBSD$");
+#endif
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_OPENSSL_CAST_H
+#include <openssl/cast.h>
+#endif
+
+#include "netpgp/create.h"
+#include "netpgp/writer.h"
+#include "netpgp/keyring.h"
+#include "netpgp/signature.h"
+#include "netpgp/packet.h"
+#include "netpgp/packet-parse.h"
+#include "netpgp/readerwriter.h"
+#include "netpgp/memory.h"
+#include "netpgp/netpgpdefs.h"
+#include "netpgp/version.h"
+#include "netpgp/netpgpdigest.h"
+
+
+/*
+ * return 1 if OK, otherwise 0
+ */
+static unsigned
+base_write(pgp_output_t *out, const void *src, unsigned len)
+{
+	return out->writer.writer(src, len, &out->errors, &out->writer);
+}
+
+/**
+ * \ingroup Core_WritePackets
+ *
+ * \param src
+ * \param len
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write(pgp_output_t *output, const void *src, unsigned len)
+{
+	return base_write(output, src, len);
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \param n
+ * \param len
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_scalar(pgp_output_t *output, unsigned n, unsigned len)
+{
+	uint8_t   c;
+
+	while (len-- > 0) {
+		c = n >> (len * 8);
+		if (!base_write(output, &c, 1)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \param bn
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_mpi(pgp_output_t *output, const BIGNUM *bn)
+{
+	unsigned	bits;
+	uint8_t		buf[NETPGP_BUFSIZ];
+
+	bits = (unsigned)BN_num_bits(bn);
+	if (bits > 65535) {
+		(void) fprintf(stderr, "pgp_write_mpi: too large %u\n", bits);
+		return 0;
+	}
+	BN_bn2bin(bn, buf);
+	return pgp_write_scalar(output, bits, 2) &&
+		pgp_write(output, buf, (bits + 7) / 8);
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \param tag
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_ptag(pgp_output_t *output, pgp_content_enum tag)
+{
+	uint8_t   c;
+
+	c = tag | PGP_PTAG_ALWAYS_SET | PGP_PTAG_NEW_FORMAT;
+	return base_write(output, &c, 1);
+}
+
+/**
+ * \ingroup Core_WritePackets
+ * \param len
+ * \param output
+ * \return 1 if OK, otherwise 0
+ */
+
+unsigned
+pgp_write_length(pgp_output_t *output, unsigned len)
+{
+	uint8_t   c[2];
+
+	if (len < 192) {
+		c[0] = len;
+		return base_write(output, c, 1);
+	}
+	if (len < 8192 + 192) {
+		c[0] = ((len - 192) >> 8) + 192;
+		c[1] = (len - 192) % 256;
+		return base_write(output, c, 2);
+	}
+	return pgp_write_scalar(output, 0xff, 1) &&
+		pgp_write_scalar(output, len, 4);
+}
+
+/*
+ * Note that we finalise from the top down, so we don't use writers below
+ * that have already been finalised
+ */
+unsigned
+pgp_writer_info_finalise(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	unsigned   ret = 1;
+
+	if (writer->finaliser) {
+		ret = writer->finaliser(errors, writer);
+		writer->finaliser = NULL;
+	}
+	if (writer->next && !pgp_writer_info_finalise(errors, writer->next)) {
+		writer->finaliser = NULL;
+		return 0;
+	}
+	return ret;
+}
+
+void
+pgp_writer_info_delete(pgp_writer_t *writer)
+{
+	/* we should have finalised before deleting */
+	if (writer->finaliser) {
+		(void) fprintf(stderr, "pgp_writer_info_delete: not done\n");
+		return;
+	}
+	if (writer->next) {
+		pgp_writer_info_delete(writer->next);
+		free(writer->next);
+		writer->next = NULL;
+	}
+	if (writer->destroyer) {
+		writer->destroyer(writer);
+		writer->destroyer = NULL;
+	}
+	writer->writer = NULL;
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Set a writer in output. There should not be another writer set.
+ *
+ * \param output The output structure
+ * \param writer
+ * \param finaliser
+ * \param destroyer
+ * \param arg The argument for the writer and destroyer
+ */
+void
+pgp_writer_set(pgp_output_t *output,
+	       pgp_writer_func_t *writer,
+	       pgp_writer_finaliser_t *finaliser,
+	       pgp_writer_destroyer_t *destroyer,
+	       void *arg)
+{
+	if (output->writer.writer) {
+		(void) fprintf(stderr, "pgp_writer_set: already set\n");
+	} else {
+		output->writer.writer = writer;
+		output->writer.finaliser = finaliser;
+		output->writer.destroyer = destroyer;
+		output->writer.arg = arg;
+	}
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Push a writer in output. There must already be another writer set.
+ *
+ * \param output The output structure
+ * \param writer
+ * \param finaliser
+ * \param destroyer
+ * \param arg The argument for the writer and destroyer
+ */
+void
+pgp_writer_push(pgp_output_t *output,
+		pgp_writer_func_t *writer,
+		pgp_writer_finaliser_t *finaliser,
+		pgp_writer_destroyer_t *destroyer,
+		void *arg)
+{
+	pgp_writer_t *copy;
+
+    if (output->writer.writer == NULL) {
+		(void) fprintf(stderr, "pgp_writer_push: no orig writer\n");
+	} else {
+        if ((copy = calloc(1, sizeof(*copy))) == NULL) {
+            (void) fprintf(stderr, "pgp_writer_push: bad alloc\n");
+            return; /* TODO return error */
+        }
+		*copy = output->writer;
+		output->writer.next = copy;
+
+		output->writer.writer = writer;
+		output->writer.finaliser = finaliser;
+		output->writer.destroyer = destroyer;
+		output->writer.arg = arg;
+	}
+}
+
+void
+pgp_writer_pop(pgp_output_t *output)
+{
+	pgp_writer_t *next;
+
+	/* Make sure the finaliser has been called. */
+	if (output->writer.finaliser) {
+		(void) fprintf(stderr,
+			"pgp_writer_pop: finaliser not called\n");
+	} else if (output->writer.next == NULL) {
+		(void) fprintf(stderr,
+			"pgp_writer_pop: not a stacked writer\n");
+	} else {
+		if (output->writer.destroyer) {
+			output->writer.destroyer(&output->writer);
+		}
+		next = output->writer.next;
+		output->writer = *next;
+		free(next);
+	}
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Close the writer currently set in output.
+ *
+ * \param output The output structure
+ */
+unsigned
+pgp_writer_close(pgp_output_t *output)
+{
+	unsigned   ret;
+
+	ret = pgp_writer_info_finalise(&output->errors, &output->writer);
+	pgp_writer_info_delete(&output->writer);
+	return ret;
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Get the arg supplied to pgp_createinfo_set_writer().
+ *
+ * \param writer The writer_info structure
+ * \return The arg
+ */
+void           *
+pgp_writer_get_arg(pgp_writer_t *writer)
+{
+	return writer->arg;
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Write to the next writer down in the stack.
+ *
+ * \param src The data to write.
+ * \param len The length of src.
+ * \param errors A place to store errors.
+ * \param writer The writer_info structure.
+ * \return Success - if 0, then errors should contain the error.
+ */
+static unsigned
+stacked_write(pgp_writer_t *writer, const void *src, unsigned len,
+		  pgp_error_t ** errors)
+{
+	return writer->next->writer(src, len, errors, writer->next);
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * Free the arg. Many writers just have a calloc()ed lump of storage, this
+ * function releases it.
+ *
+ * \param writer the info structure.
+ */
+static void
+generic_destroyer(pgp_writer_t *writer)
+{
+	free(pgp_writer_get_arg(writer));
+}
+
+/**
+ * \ingroup Core_Writers
+ *
+ * A writer that just writes to the next one down. Useful for when you
+ * want to insert just a finaliser into the stack.
+ */
+unsigned
+pgp_writer_passthrough(const uint8_t *src,
+		       unsigned len,
+		       pgp_error_t **errors,
+		       pgp_writer_t *writer)
+{
+	return stacked_write(writer, src, len, errors);
+}
+
+/**************************************************************************/
+
+/**
+ * \struct dashesc_t
+ */
+typedef struct {
+	unsigned   		 seen_nl:1;
+	unsigned		 seen_cr:1;
+	pgp_create_sig_t	*sig;
+	pgp_memory_t		*trailing;
+} dashesc_t;
+
+static unsigned
+dash_esc_writer(const uint8_t *src,
+		    unsigned len,
+		    pgp_error_t **errors,
+		    pgp_writer_t *writer)
+{
+	dashesc_t	*dash = pgp_writer_get_arg(writer);
+	unsigned        n;
+
+	if (pgp_get_debug_level(__FILE__)) {
+		unsigned    i = 0;
+
+		(void) fprintf(stderr, "dash_esc_writer writing %u:\n", len);
+		for (i = 0; i < len; i++) {
+			fprintf(stderr, "0x%02x ", src[i]);
+			if (((i + 1) % 16) == 0) {
+				(void) fprintf(stderr, "\n");
+			} else if (((i + 1) % 8) == 0) {
+				(void) fprintf(stderr, "  ");
+			}
+		}
+		(void) fprintf(stderr, "\n");
+	}
+	/* XXX: make this efficient */
+	for (n = 0; n < len; ++n) {
+		unsigned        l;
+
+		if (dash->seen_nl) {
+			if (src[n] == '-' &&
+			    !stacked_write(writer, "- ", 2, errors)) {
+				return 0;
+			}
+			dash->seen_nl = 0;
+		}
+		dash->seen_nl = src[n] == '\n';
+
+		if (dash->seen_nl && !dash->seen_cr) {
+			if (!stacked_write(writer, "\r", 1, errors)) {
+				return 0;
+			}
+			pgp_sig_add_data(dash->sig, "\r", 1);
+		}
+		dash->seen_cr = src[n] == '\r';
+
+		if (!stacked_write(writer, &src[n], 1, errors)) {
+			return 0;
+		}
+
+		/* trailing whitespace isn't included in the signature */
+		if (src[n] == ' ' || src[n] == '\t') {
+			pgp_memory_add(dash->trailing, &src[n], 1);
+		} else {
+			if ((l = (unsigned)pgp_mem_len(dash->trailing)) != 0) {
+				if (!dash->seen_nl && !dash->seen_cr) {
+					pgp_sig_add_data(dash->sig,
+					pgp_mem_data(dash->trailing), l);
+				}
+				pgp_memory_clear(dash->trailing);
+			}
+			pgp_sig_add_data(dash->sig, &src[n], 1);
+		}
+	}
+	return 1;
+}
+
+/**
+ * \param writer
+ */
+static void
+dash_escaped_destroyer(pgp_writer_t *writer)
+{
+	dashesc_t	*dash;
+
+	dash = pgp_writer_get_arg(writer);
+	pgp_memory_free(dash->trailing);
+	free(dash);
+}
+
+/**
+ * \ingroup Core_WritersNext
+ * \brief Push Clearsigned Writer onto stack
+ * \param output
+ * \param sig
+ */
+unsigned
+pgp_writer_push_clearsigned(pgp_output_t *output, pgp_create_sig_t *sig)
+{
+	static const char     header[] =
+		"-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: ";
+	const char     *hash;
+	dashesc_t      *dash;
+	unsigned	ret;
+
+	hash = pgp_text_from_hash(pgp_sig_get_hash(sig));
+	if ((dash = calloc(1, sizeof(*dash))) == NULL) {
+		PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Bad alloc");
+		return 0;
+	}
+	ret = (pgp_write(output, header, (unsigned)(sizeof(header) - 1)) &&
+		pgp_write(output, hash, (unsigned)strlen(hash)) &&
+		pgp_write(output, "\r\n\r\n", 4));
+
+	if (ret == 0) {
+		PGP_ERROR_1(&output->errors, PGP_E_W, "%s",
+			"Error pushing clearsigned header");
+		free(dash);
+		return ret;
+	}
+	dash->seen_nl = 1;
+	dash->sig = sig;
+	dash->trailing = pgp_memory_new();
+	pgp_writer_push(output, dash_esc_writer, NULL,
+			dash_escaped_destroyer, dash);
+	return ret;
+}
+
+
+/**
+ * \struct base64_t
+ */
+typedef struct {
+	unsigned	pos;
+	uint8_t		t;
+	unsigned	checksum;
+} base64_t;
+
+static const char     b64map[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static unsigned
+base64_writer(const uint8_t *src,
+	      unsigned len,
+	      pgp_error_t **errors,
+	      pgp_writer_t *writer)
+{
+	base64_t	*base64;
+	unsigned         n;
+
+	base64 = pgp_writer_get_arg(writer);
+	for (n = 0; n < len;) {
+		base64->checksum = pgp_crc24(base64->checksum, src[n]);
+		if (base64->pos == 0) {
+			/* XXXXXX00 00000000 00000000 */
+			if (!stacked_write(writer,
+					&b64map[(unsigned)src[n] >> 2],
+					1, errors)) {
+				return 0;
+			}
+
+			/* 000000XX xxxx0000 00000000 */
+			base64->t = (src[n++] & 3) << 4;
+			base64->pos = 1;
+		} else if (base64->pos == 1) {
+			/* 000000xx XXXX0000 00000000 */
+			base64->t += (unsigned)src[n] >> 4;
+			if (!stacked_write(writer, &b64map[base64->t], 1,
+					errors)) {
+				return 0;
+			}
+
+			/* 00000000 0000XXXX xx000000 */
+			base64->t = (src[n++] & 0xf) << 2;
+			base64->pos = 2;
+		} else if (base64->pos == 2) {
+			/* 00000000 0000xxxx XX000000 */
+			base64->t += (unsigned)src[n] >> 6;
+			if (!stacked_write(writer, &b64map[base64->t], 1,
+					errors)) {
+				return 0;
+			}
+
+			/* 00000000 00000000 00XXXXXX */
+			if (!stacked_write(writer,
+					&b64map[src[n++] & 0x3f], 1, errors)) {
+				return 0;
+			}
+
+			base64->pos = 0;
+		}
+	}
+
+	return 1;
+}
+
+static unsigned
+sig_finaliser(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	static const char	trail[] = "\r\n-----END PGP SIGNATURE-----\r\n";
+	base64_t		*base64;
+	uint8_t			c[3];
+
+	base64 = pgp_writer_get_arg(writer);
+	if (base64->pos) {
+		if (!stacked_write(writer, &b64map[base64->t], 1, errors)) {
+			return 0;
+		}
+		if (base64->pos == 1 &&
+		    !stacked_write(writer, "==", 2, errors)) {
+			return 0;
+		}
+		if (base64->pos == 2 &&
+		    !stacked_write(writer, "=", 1, errors)) {
+			return 0;
+		}
+	}
+	/* Ready for the checksum */
+	if (!stacked_write(writer, "\r\n=", 3, errors)) {
+		return 0;
+	}
+
+	base64->pos = 0;		/* get ready to write the checksum */
+
+	c[0] = base64->checksum >> 16;
+	c[1] = base64->checksum >> 8;
+	c[2] = base64->checksum;
+	/* push the checksum through our own writer */
+	if (!base64_writer(c, 3, errors, writer)) {
+		return 0;
+	}
+
+	return stacked_write(writer, trail, (unsigned)(sizeof(trail) - 1), errors);
+}
+
+/**
+ * \struct linebreak_t
+ */
+typedef struct {
+	unsigned        pos;
+} linebreak_t;
+
+#define BREAKPOS	76
+
+static unsigned
+linebreak_writer(const uint8_t *src,
+		 unsigned len,
+		 pgp_error_t **errors,
+		 pgp_writer_t *writer)
+{
+	linebreak_t	*linebreak;
+	unsigned         n;
+
+	linebreak = pgp_writer_get_arg(writer);
+	for (n = 0; n < len; ++n, ++linebreak->pos) {
+		if (src[n] == '\r' || src[n] == '\n') {
+			linebreak->pos = 0;
+		}
+		if (linebreak->pos == BREAKPOS) {
+			if (!stacked_write(writer, "\r\n", 2, errors)) {
+				return 0;
+			}
+			linebreak->pos = 0;
+		}
+		if (!stacked_write(writer, &src[n], 1, errors)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/**
+ * \ingroup Core_WritersNext
+ * \brief Push armoured signature on stack
+ * \param output
+ */
+unsigned
+pgp_writer_use_armored_sig(pgp_output_t *output)
+{
+	static const char     header[] =
+			"\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: "
+			NETPGP_VERSION_STRING
+			"\r\n\r\n";
+	linebreak_t	*linebreak;
+	base64_t   	*base64;
+
+	pgp_writer_pop(output);
+	if (pgp_write(output, header, (unsigned)(sizeof(header) - 1)) == 0) {
+		PGP_ERROR_1(&output->errors, PGP_E_W, "%s",
+			"Error switching to armoured signature");
+		return 0;
+	}
+	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
+		PGP_ERROR_1(&output->errors, PGP_E_W, "%s",
+			"pgp_writer_use_armored_sig: Bad alloc");
+		return 0;
+	}
+	pgp_writer_push(output, linebreak_writer, NULL,
+			generic_destroyer,
+			linebreak);
+	base64 = calloc(1, sizeof(*base64));
+	if (!base64) {
+		PGP_MEMORY_ERROR(&output->errors);
+		return 0;
+	}
+	base64->checksum = CRC24_INIT;
+	pgp_writer_push(output, base64_writer, sig_finaliser,
+			generic_destroyer, base64);
+	return 1;
+}
+
+static unsigned
+armoured_message_finaliser(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	/* TODO: This is same as sig_finaliser apart from trailer. */
+	static const char	 trailer[] =
+			"\r\n-----END PGP MESSAGE-----\r\n";
+	base64_t		*base64;
+	uint8_t			 c[3];
+
+	base64 = pgp_writer_get_arg(writer);
+	if (base64->pos) {
+		if (!stacked_write(writer, &b64map[base64->t], 1, errors)) {
+			return 0;
+		}
+		if (base64->pos == 1 &&
+		    !stacked_write(writer, "==", 2, errors)) {
+			return 0;
+		}
+		if (base64->pos == 2 &&
+		    !stacked_write(writer, "=", 1, errors)) {
+			return 0;
+		}
+	}
+	/* Ready for the checksum */
+	if (!stacked_write(writer, "\r\n=", 3, errors)) {
+		return 0;
+	}
+
+	base64->pos = 0;		/* get ready to write the checksum */
+
+	c[0] = base64->checksum >> 16;
+	c[1] = base64->checksum >> 8;
+	c[2] = base64->checksum;
+	/* push the checksum through our own writer */
+	if (!base64_writer(c, 3, errors, writer)) {
+		return 0;
+	}
+
+	return stacked_write(writer, trailer, (unsigned)strlen(trailer), errors);
+}
+
+/**
+ \ingroup Core_WritersNext
+ \brief Write a PGP MESSAGE
+ \todo replace with generic function
+*/
+void
+pgp_writer_push_armor_msg(pgp_output_t *output)
+{
+	static const char	 header[] = "-----BEGIN PGP MESSAGE-----\r\n";
+	linebreak_t		*linebreak;
+	base64_t		*base64;
+
+	pgp_write(output, header, (unsigned)(sizeof(header) - 1));
+	pgp_write(output, "\r\n", 2);
+	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_writer_push_armor_msg: bad lb alloc\n");
+		return;
+	}
+	pgp_writer_push(output, linebreak_writer, NULL,
+		generic_destroyer,
+		linebreak);
+	if ((base64 = calloc(1, sizeof(*base64))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_writer_push_armor_msg: bad alloc\n");
+		return;
+	}
+	base64->checksum = CRC24_INIT;
+	pgp_writer_push(output, base64_writer,
+		armoured_message_finaliser, generic_destroyer,
+		base64);
+}
+
+#if 0 //////
+static unsigned
+armoured_finaliser(pgp_armor_type_t type,
+			pgp_error_t **errors,
+			pgp_writer_t *writer)
+{
+	static const char     tail_pubkey[] =
+			"\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n";
+	static const char     tail_private_key[] =
+			"\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n";
+	const char		*tail = NULL;
+	unsigned		 tailsize = 0;
+	base64_t		*base64;
+	uint8_t		 	 c[3];
+
+	switch (type) {
+	case PGP_PGP_PUBLIC_KEY_BLOCK:
+		tail = tail_pubkey;
+		tailsize = sizeof(tail_pubkey) - 1;
+		break;
+
+	case PGP_PGP_PRIVATE_KEY_BLOCK:
+		tail = tail_private_key;
+		tailsize = sizeof(tail_private_key) - 1;
+		break;
+
+	default:
+		(void) fprintf(stderr, "armoured_finaliser: unusual type\n");
+		return 0;
+	}
+	base64 = pgp_writer_get_arg(writer);
+	if (base64->pos) {
+		if (!stacked_write(writer, &b64map[base64->t], 1,
+					errors)) {
+			return 0;
+		}
+		if (base64->pos == 1 && !stacked_write(writer, "==", 2,
+				errors)) {
+			return 0;
+		}
+		if (base64->pos == 2 && !stacked_write(writer, "=", 1,
+				errors)) {
+			return 0;
+		}
+	}
+	/* Ready for the checksum */
+	if (!stacked_write(writer, "\r\n=", 3, errors)) {
+		return 0;
+	}
+	base64->pos = 0;		/* get ready to write the checksum */
+	c[0] = base64->checksum >> 16;
+	c[1] = base64->checksum >> 8;
+	c[2] = base64->checksum;
+	/* push the checksum through our own writer */
+	if (!base64_writer(c, 3, errors, writer)) {
+		return 0;
+	}
+	return stacked_write(writer, tail, tailsize, errors);
+}
+#endif //////
+
+#if 0 //////
+static unsigned
+armored_pubkey_fini(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	return armoured_finaliser(PGP_PGP_PUBLIC_KEY_BLOCK, errors, writer);
+}
+#endif //////
+
+#if 0 //////
+static unsigned
+armored_privkey_fini(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	return armoured_finaliser(PGP_PGP_PRIVATE_KEY_BLOCK, errors, writer);
+}
+#endif //////
+
+/* \todo use this for other armoured types */
+/**
+ \ingroup Core_WritersNext
+ \brief Push Armoured Writer on stack (generic)
+*/
+#if 0 //////
+void
+pgp_writer_push_armoured(pgp_output_t *output, pgp_armor_type_t type)
+{
+	static char     hdr_pubkey[] =
+			"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: "
+			NETPGP_VERSION_STRING
+			"\r\n\r\n";
+	static char     hdr_private_key[] =
+			"-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: "
+			NETPGP_VERSION_STRING
+			"\r\n\r\n";
+	unsigned    	 hdrsize = 0;
+	unsigned	(*finaliser) (pgp_error_t **, pgp_writer_t *);
+	base64_t	*base64;
+	linebreak_t	*linebreak;
+	char           *header = NULL;
+
+	finaliser = NULL;
+	switch (type) {
+	case PGP_PGP_PUBLIC_KEY_BLOCK:
+		header = hdr_pubkey;
+		hdrsize = sizeof(hdr_pubkey) - 1;
+		finaliser = armored_pubkey_fini;
+		break;
+
+	case PGP_PGP_PRIVATE_KEY_BLOCK:
+		header = hdr_private_key;
+		hdrsize = sizeof(hdr_private_key) - 1;
+		finaliser = armored_privkey_fini;
+		break;
+
+	default:
+		(void) fprintf(stderr,
+			"pgp_writer_push_armoured: unusual type\n");
+		return;
+	}
+	if ((linebreak = calloc(1, sizeof(*linebreak))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_writer_push_armoured: bad alloc\n");
+		return;
+	}
+	pgp_write(output, header, hdrsize);
+	pgp_writer_push(output, linebreak_writer, NULL,
+			generic_destroyer,
+			linebreak);
+	if ((base64 = calloc(1, sizeof(*base64))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_writer_push_armoured: bad alloc\n");
+        free(linebreak);
+		return;
+	}
+	base64->checksum = CRC24_INIT;
+	pgp_writer_push(output, base64_writer, finaliser,
+			generic_destroyer, base64);
+}
+#endif //////
+
+/**************************************************************************/
+
+typedef struct {
+	pgp_crypt_t    *crypt;
+	int             free_crypt;
+} crypt_t;
+
+/*
+ * This writer simply takes plaintext as input,
+ * encrypts it with the given key
+ * and outputs the resulting encrypted text
+ */
+static unsigned
+encrypt_writer(const uint8_t *src,
+	       unsigned len,
+	       pgp_error_t **errors,
+	       pgp_writer_t *writer)
+{
+#define BUFSZ 1024		/* arbitrary number */
+	uint8_t		encbuf[BUFSZ];
+	unsigned        remaining;
+	unsigned        done = 0;
+	crypt_t		*pgp_encrypt;
+
+	remaining = len;
+	pgp_encrypt = (crypt_t *) pgp_writer_get_arg(writer);
+	if (!pgp_is_sa_supported(pgp_encrypt->crypt->alg)) {
+		(void) fprintf(stderr, "encrypt_writer: not supported\n");
+		return 0;
+	}
+	while (remaining > 0) {
+		unsigned        size = (remaining < BUFSZ) ? remaining : BUFSZ;
+
+		/* memcpy(buf,src,size); // \todo copy needed here? */
+		pgp_encrypt->crypt->cfb_encrypt(pgp_encrypt->crypt, encbuf,
+					src + done, size);
+
+		if (pgp_get_debug_level(__FILE__)) {
+			hexdump(stderr, "unencrypted", &src[done], 16);
+			hexdump(stderr, "encrypted", encbuf, 16);
+		}
+		if (!stacked_write(writer, encbuf, size, errors)) {
+			if (pgp_get_debug_level(__FILE__)) {
+				fprintf(stderr,
+					"encrypted_writer: stacked write\n");
+			}
+			return 0;
+		}
+		remaining -= size;
+		done += size;
+	}
+
+	return 1;
+}
+
+static void
+encrypt_destroyer(pgp_writer_t *writer)
+{
+	crypt_t    *pgp_encrypt;
+
+	pgp_encrypt = (crypt_t *) pgp_writer_get_arg(writer);
+	if (pgp_encrypt->free_crypt) {
+		free(pgp_encrypt->crypt);
+	}
+	free(pgp_encrypt);
+}
+
+/**
+\ingroup Core_WritersNext
+\brief Push Encrypted Writer onto stack (create SE packets)
+*/
+void
+pgp_push_enc_crypt(pgp_output_t *output, pgp_crypt_t *pgp_crypt)
+{
+	/* Create encrypt to be used with this writer */
+	/* Remember to free this in the destroyer */
+	crypt_t    *pgp_encrypt;
+
+	if ((pgp_encrypt = calloc(1, sizeof(*pgp_encrypt))) == NULL) {
+		(void) fprintf(stderr, "pgp_push_enc_crypt: bad alloc\n");
+	} else {
+		/* Setup the encrypt */
+		pgp_encrypt->crypt = pgp_crypt;
+		pgp_encrypt->free_crypt = 0;
+		/* And push writer on stack */
+		pgp_writer_push(output, encrypt_writer, NULL,
+			encrypt_destroyer, pgp_encrypt);
+	}
+}
+
+/**************************************************************************/
+
+typedef struct {
+	pgp_crypt_t    *crypt;
+    unsigned  raw;
+} encrypt_se_ip_t;
+
+static unsigned	encrypt_se_ip_writer(const uint8_t *,
+		     unsigned,
+		     pgp_error_t **,
+		     pgp_writer_t *);
+static void     encrypt_se_ip_destroyer(pgp_writer_t *);
+
+/* */
+
+/**
+\ingroup Core_WritersNext
+\brief Push Encrypted SE IP Writer onto stack
+*/
+int
+pgp_push_enc_se_ip(pgp_output_t *output, const pgp_keyring_t *pubkeys, const char *cipher, unsigned raw)
+{
+	pgp_pk_sesskey_t *initial_sesskey = NULL;
+	pgp_pk_sesskey_t *encrypted_pk_sesskey;
+	encrypt_se_ip_t *se_ip;
+	pgp_crypt_t	*encrypted;
+	uint8_t		*iv;
+	unsigned	n;
+
+	if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
+		(void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n");
+		return 0;
+	}
+
+	for (n = 0; n < pubkeys->keyc; ++n) {
+        /* Create and write encrypted PK session key */
+        if ((encrypted_pk_sesskey =
+                 pgp_create_pk_sesskey(&pubkeys->keys[n],
+                     cipher, initial_sesskey)) == NULL) {
+            (void) fprintf(stderr, "pgp_push_enc_se_ip: null pk sesskey\n");
+            free(se_ip);
+            return 0;
+        }
+
+        if (initial_sesskey == NULL) {
+            initial_sesskey = encrypted_pk_sesskey;
+        }
+
+	    pgp_write_pk_sesskey(output, encrypted_pk_sesskey);
+
+        if(encrypted_pk_sesskey != initial_sesskey){
+    	    free(encrypted_pk_sesskey);
+        }
+    }
+
+    if (initial_sesskey == NULL) {
+        (void) fprintf(stderr, "pgp_push_enc_se_ip: no sesskey\n");
+        free(se_ip);
+        return 0;
+    }
+
+	/* Setup the se_ip */
+	if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
+		free(se_ip);
+		(void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n");
+		return 0;
+	}
+	pgp_crypt_any(encrypted, initial_sesskey->symm_alg);
+	if ((iv = calloc(1, encrypted->blocksize)) == NULL) {
+		free(se_ip);
+		free(encrypted);
+		(void) fprintf(stderr, "pgp_push_enc_se_ip: bad alloc\n");
+		return 0;
+	}
+	encrypted->set_iv(encrypted, iv);
+	encrypted->set_crypt_key(encrypted, &initial_sesskey->key[0]);
+	pgp_encrypt_init(encrypted);
+
+	se_ip->crypt = encrypted;
+    se_ip->raw = raw;
+
+	/* And push writer on stack */
+	pgp_writer_push(output, encrypt_se_ip_writer, NULL,
+			encrypt_se_ip_destroyer, se_ip);
+	/* tidy up */
+	free(initial_sesskey);
+	free(iv);
+	return 1;
+}
+
+static unsigned
+encrypt_se_ip_writer(const uint8_t *src,
+		     unsigned len,
+		     pgp_error_t **errors,
+		     pgp_writer_t *writer)
+{
+	const unsigned	 bufsz = 128;
+	encrypt_se_ip_t	*se_ip = pgp_writer_get_arg(writer);
+	pgp_output_t	*litoutput;
+	pgp_output_t	*zoutput;
+	pgp_output_t	*output;
+	pgp_memory_t	*litmem;
+	pgp_memory_t	*zmem;
+	pgp_memory_t	*localmem;
+	unsigned	 ret = 1;
+
+    const uint8_t *zsrc;
+    unsigned zsrclen;
+
+	pgp_setup_memory_write(&litoutput, &litmem, bufsz);
+	pgp_setup_memory_write(&zoutput, &zmem, bufsz);
+	pgp_setup_memory_write(&output, &localmem, bufsz);
+
+    if (!se_ip->raw) {
+        /* create literal data packet from source data */
+        pgp_write_litdata(litoutput, src, (const int)len, PGP_LDT_BINARY);
+        if (pgp_mem_len(litmem) <= len) {
+            (void) fprintf(stderr, "encrypt_se_ip_writer: bad len\n");
+            return 0;
+        }
+        zsrc = pgp_mem_data(litmem);
+        zsrclen = (unsigned)pgp_mem_len(litmem);
+    }else{
+        zsrc = src;
+        zsrclen = len;
+    }
+
+	/* create compressed packet from literal data packet */
+	pgp_writez(zoutput, zsrc, zsrclen);
+
+	/* create SE IP packet set from this compressed literal data */
+	pgp_write_se_ip_pktset(output, pgp_mem_data(zmem),
+			       (unsigned)pgp_mem_len(zmem),
+			       se_ip->crypt);
+	if (pgp_mem_len(localmem) <= pgp_mem_len(zmem)) {
+		(void) fprintf(stderr,
+				"encrypt_se_ip_writer: bad comp len\n");
+		return 0;
+	}
+
+	/* now write memory to next writer */
+	ret = stacked_write(writer, pgp_mem_data(localmem),
+				(unsigned)pgp_mem_len(localmem), errors);
+
+	pgp_memory_free(localmem);
+	pgp_memory_free(zmem);
+	pgp_memory_free(litmem);
+
+	return ret;
+}
+
+static void
+encrypt_se_ip_destroyer(pgp_writer_t *writer)
+{
+	encrypt_se_ip_t	*se_ip;
+
+	se_ip = pgp_writer_get_arg(writer);
+	free(se_ip->crypt);
+	free(se_ip);
+}
+
+unsigned
+pgp_write_se_ip_pktset(pgp_output_t *output,
+			const uint8_t *data,
+			const unsigned len,
+			pgp_crypt_t *crypted)
+{
+	pgp_output_t	*mdcoutput;
+	pgp_memory_t	*mdc;
+	uint8_t		 hashed[PGP_SHA1_HASH_SIZE];
+	uint8_t		*preamble;
+	const size_t	 mdcsize = 1 + 1 + PGP_SHA1_HASH_SIZE;
+	size_t		 preamblesize;
+	size_t		 bufsize;
+
+	preamblesize = crypted->blocksize + 2;
+	if ((preamble = calloc(1, preamblesize)) == NULL) {
+		(void) fprintf(stderr, "pgp_write_se_ip_pktset: bad alloc\n");
+		return 0;
+	}
+	bufsize = preamblesize + len + mdcsize;
+
+	if (!pgp_write_ptag(output, PGP_PTAG_CT_SE_IP_DATA) ||
+	    !pgp_write_length(output, (unsigned)(1 + bufsize)) ||
+	    !pgp_write_scalar(output, PGP_SE_IP_DATA_VERSION, 1)) {
+		free(preamble);
+		return 0;
+	}
+	pgp_random(preamble, crypted->blocksize);
+	preamble[crypted->blocksize] = preamble[crypted->blocksize - 2];
+	preamble[crypted->blocksize + 1] = preamble[crypted->blocksize - 1];
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "preamble", preamble, preamblesize);
+	}
+
+	/* now construct MDC packet and add to the end of the buffer */
+	pgp_setup_memory_write(&mdcoutput, &mdc, mdcsize);
+	pgp_calc_mdc_hash(preamble, preamblesize, data, len, hashed);
+	pgp_write_mdc(mdcoutput, hashed);
+
+	if (pgp_get_debug_level(__FILE__)) {
+		hexdump(stderr, "plaintext", data, len);
+		hexdump(stderr, "mdc", pgp_mem_data(mdc), PGP_SHA1_HASH_SIZE + 1 + 1);
+	}
+
+	/* and write it out */
+	pgp_push_enc_crypt(output, crypted);
+	if (pgp_get_debug_level(__FILE__)) {
+		(void) fprintf(stderr,
+			"writing %" PRIsize "u + %u + %" PRIsize "u\n",
+			preamblesize, len, pgp_mem_len(mdc));
+	}
+	if (!pgp_write(output, preamble, (unsigned)preamblesize) ||
+	    !pgp_write(output, data, len) ||
+	    !pgp_write(output, pgp_mem_data(mdc), (unsigned)pgp_mem_len(mdc))) {
+		/* \todo fix cleanup here and in old code functions */
+		return 0;
+	}
+
+	pgp_writer_pop(output);
+
+	/* cleanup  */
+	pgp_teardown_memory_write(mdcoutput, mdc);
+	free(preamble);
+
+	return 1;
+}
+
+typedef struct {
+	int             fd;
+} writer_fd_t;
+
+static unsigned
+fd_writer(const uint8_t *src, unsigned len,
+	  pgp_error_t **errors,
+	  pgp_writer_t *writer)
+{
+	writer_fd_t	*writerfd;
+	int              n;
+
+	writerfd = pgp_writer_get_arg(writer);
+	n = (int)write(writerfd->fd, src, len);
+	if (n == -1) {
+		PGP_SYSTEM_ERROR_1(errors, PGP_E_W_WRITE_FAILED, "write",
+				   "file descriptor %d", writerfd->fd);
+		return 0;
+	}
+	if ((unsigned) n != len) {
+		PGP_ERROR_1(errors, PGP_E_W_WRITE_TOO_SHORT,
+			    "file descriptor %d", writerfd->fd);
+		return 0;
+	}
+	return 1;
+}
+
+static void
+writer_fd_destroyer(pgp_writer_t *writer)
+{
+	free(pgp_writer_get_arg(writer));
+}
+
+/**
+ * \ingroup Core_WritersFirst
+ * \brief Write to a File
+ *
+ * Set the writer in output to be a stock writer that writes to a file
+ * descriptor. If another writer has already been set, then that is
+ * first destroyed.
+ *
+ * \param output The output structure
+ * \param fd The file descriptor
+ *
+ */
+
+void
+pgp_writer_set_fd(pgp_output_t *output, int fd)
+{
+	writer_fd_t	*writer;
+
+	if ((writer = calloc(1, sizeof(*writer))) == NULL) {
+		(void) fprintf(stderr, "pgp_writer_set_fd: bad alloc\n");
+	} else {
+		writer->fd = fd;
+		pgp_writer_set(output, fd_writer, NULL, writer_fd_destroyer, writer);
+	}
+}
+
+static unsigned
+memory_writer(const uint8_t *src,
+		unsigned len,
+		pgp_error_t **errors,
+		pgp_writer_t *writer)
+{
+	pgp_memory_t   *mem;
+
+	__PGP_USED(errors);
+	mem = pgp_writer_get_arg(writer);
+	pgp_memory_add(mem, src, len);
+	return 1;
+}
+
+/**
+ * \ingroup Core_WritersFirst
+ * \brief Write to memory
+ *
+ * Set a memory writer.
+ *
+ * \param output The output structure
+ * \param mem The memory structure
+ * \note It is the caller's responsiblity to call pgp_memory_free(mem)
+ * \sa pgp_memory_free()
+ */
+
+void
+pgp_writer_set_memory(pgp_output_t *output, pgp_memory_t *mem)
+{
+	pgp_writer_set(output, memory_writer, NULL, NULL, mem);
+}
+
+/**************************************************************************/
+
+typedef struct {
+	pgp_hash_alg_t	 hash_alg;
+	pgp_hash_t		 hash;
+	uint8_t			*hashed;
+} skey_checksum_t;
+
+static unsigned
+skey_checksum_writer(const uint8_t *src,
+	const unsigned len,
+	pgp_error_t **errors,
+	pgp_writer_t *writer)
+{
+	skey_checksum_t	*sum;
+	unsigned	 ret = 1;
+
+	sum = pgp_writer_get_arg(writer);
+	/* add contents to hash */
+	sum->hash.add(&sum->hash, src, len);
+	/* write to next stacked writer */
+	ret = stacked_write(writer, src, len, errors);
+	/* tidy up and return */
+	return ret;
+}
+
+static unsigned
+skey_checksum_finaliser(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	skey_checksum_t *sum;
+
+	sum = pgp_writer_get_arg(writer);
+	if (errors && *errors) {
+		printf("errors in skey_checksum_finaliser\n");
+	}
+	(*sum->hash.finish)(&sum->hash, sum->hashed);
+	return 1;
+}
+
+static void
+skey_checksum_destroyer(pgp_writer_t *writer)
+{
+	skey_checksum_t *sum;
+
+	sum = pgp_writer_get_arg(writer);
+	free(sum);
+}
+
+/**
+\ingroup Core_WritersNext
+\param output
+\param seckey
+*/
+void
+pgp_push_checksum_writer(pgp_output_t *output, pgp_seckey_t *seckey)
+{
+	/* XXX: push a SHA-1 checksum writer (and change s2k to 254). */
+	skey_checksum_t *sum;
+	unsigned	 hashsize;
+
+	if ((sum = calloc(1, sizeof(*sum))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_push_checksum_writer: bad alloc\n");
+	} else {
+		/* configure the arg */
+		/* Hardcoded SHA1 for just now */
+		sum->hash_alg = PGP_HASH_SHA1;
+		hashsize = pgp_hash_size(sum->hash_alg);
+		if ((sum->hashed = seckey->checkhash) == NULL) {
+			sum->hashed = seckey->checkhash = calloc(1, hashsize);
+		}
+		/* init the hash */
+		pgp_hash_any(&sum->hash, sum->hash_alg);
+		if (!sum->hash.init(&sum->hash)) {
+			(void) fprintf(stderr,
+				"pgp_push_checksum_writer: bad hash init\n");
+			/* just continue and die */
+			/* XXX - agc - no way to return failure */
+		}
+		pgp_writer_push(output, skey_checksum_writer,
+			skey_checksum_finaliser, skey_checksum_destroyer, sum);
+	}
+}
+
+/**************************************************************************/
+
+typedef struct {
+    uint16_t  sum;
+} sum16_t;
+
+static unsigned
+sum16_writer(const uint8_t *src,
+                     const unsigned len,
+                     pgp_error_t **errors,
+                     pgp_writer_t *writer)
+{
+    sum16_t	*arg;
+    unsigned	 ret = 1;
+    int		 n;
+
+    arg = pgp_writer_get_arg(writer);
+
+    for (n = 0; n < len; ++n) {
+        arg->sum = (arg->sum + src[n]) & 0xffff;
+    }
+
+    /* write to next stacked writer */
+    ret = stacked_write(writer, src, len, errors);
+    /* tidy up and return */
+    return ret;
+}
+
+void
+pgp_push_sum16_writer(pgp_output_t *output)
+{
+    sum16_t *sum;
+
+    if ((sum = calloc(1, sizeof(*sum))) == NULL) {
+        (void) fprintf(stderr,
+                       "pgp_push_sum16_writer: bad alloc\n");
+    } else {
+        pgp_writer_push(output, sum16_writer,
+                        NULL, NULL, sum);
+    }
+}
+
+uint16_t
+pgp_pop_sum16_writer(pgp_output_t *output)
+{
+    uint16_t	 sum;
+    sum16_t		*arg;
+    arg = pgp_writer_get_arg(&output->writer);
+    sum = arg->sum;
+    pgp_writer_pop(output);
+    free(arg);
+    return sum;
+}
+
+/**************************************************************************/
+
+#define MAX_PARTIAL_DATA_LENGTH 1073741824
+
+typedef struct {
+	pgp_crypt_t	*crypt;
+	pgp_memory_t	*mem_data;
+	pgp_memory_t	*litmem;
+	pgp_output_t	*litoutput;
+	pgp_memory_t	*se_ip_mem;
+	pgp_output_t	*se_ip_out;
+	pgp_hash_t	 hash;
+} str_enc_se_ip_t;
+
+
+static unsigned
+str_enc_se_ip_writer(const uint8_t *src,
+			    unsigned len,
+			    pgp_error_t **errors,
+			    pgp_writer_t *writer);
+
+static unsigned
+str_enc_se_ip_finaliser(pgp_error_t **errors,
+			       pgp_writer_t * writer);
+
+static void     str_enc_se_ip_destroyer(pgp_writer_t *writer);
+
+/* */
+
+/**
+\ingroup Core_WritersNext
+\param output
+\param pubkey
+*/
+void
+pgp_push_stream_enc_se_ip(pgp_output_t *output, pgp_key_t *pubkey, const char *cipher)
+{
+	pgp_pk_sesskey_t	*encrypted_pk_sesskey;
+	str_enc_se_ip_t		*se_ip;
+	const unsigned	 	 bufsz = 1024;
+	pgp_crypt_t		*encrypted;
+	uint8_t			*iv;
+
+	if ((se_ip = calloc(1, sizeof(*se_ip))) == NULL) {
+		(void) fprintf(stderr,
+			"pgp_push_stream_enc_se_ip: bad alloc\n");
+		return;
+	}
+	encrypted_pk_sesskey = pgp_create_pk_sesskey(pubkey, cipher, NULL);
+	pgp_write_pk_sesskey(output, encrypted_pk_sesskey);
+
+	/* Setup the se_ip */
+	if ((encrypted = calloc(1, sizeof(*encrypted))) == NULL) {
+		free(se_ip);
+		(void) fprintf(stderr,
+			"pgp_push_stream_enc_se_ip: bad alloc\n");
+		return;
+	}
+	pgp_crypt_any(encrypted, encrypted_pk_sesskey->symm_alg);
+	if ((iv = calloc(1, encrypted->blocksize)) == NULL) {
+		free(encrypted);
+		free(se_ip);
+		(void) fprintf(stderr,
+			"pgp_push_stream_enc_se_ip: bad alloc\n");
+		return;
+	}
+	encrypted->set_iv(encrypted, iv);
+	encrypted->set_crypt_key(encrypted, &encrypted_pk_sesskey->key[0]);
+	pgp_encrypt_init(encrypted);
+
+	se_ip->crypt = encrypted;
+
+	se_ip->mem_data = pgp_memory_new();
+	pgp_memory_init(se_ip->mem_data, bufsz);
+
+	se_ip->litmem = NULL;
+	se_ip->litoutput = NULL;
+
+	pgp_setup_memory_write(&se_ip->se_ip_out, &se_ip->se_ip_mem, bufsz);
+
+	/* And push writer on stack */
+	pgp_writer_push(output,
+			str_enc_se_ip_writer,
+			str_enc_se_ip_finaliser,
+			str_enc_se_ip_destroyer, se_ip);
+	/* tidy up */
+	free(encrypted_pk_sesskey);
+	free(iv);
+}
+
+
+/* calculate the partial data length */
+static unsigned
+partial_data_len(unsigned len)
+{
+	unsigned	mask;
+	int		i;
+
+	if (len == 0) {
+		(void) fprintf(stderr, "partial_data_len: 0 len\n");
+		return 0;
+	}
+	if (len > MAX_PARTIAL_DATA_LENGTH) {
+		return MAX_PARTIAL_DATA_LENGTH;
+	}
+	mask = MAX_PARTIAL_DATA_LENGTH;
+	for (i = 0; i <= 30; i++) {
+		if (mask & len) {
+			break;
+		}
+		mask >>= 1;
+	}
+	return mask;
+}
+
+static unsigned
+write_partial_len(pgp_output_t *output, unsigned len)
+{
+	/* len must be a power of 2 from 0 to 30 */
+	uint8_t	c;
+	int	i;
+
+	for (i = 0; i <= 30; i++) {
+		if ((len >> i) & 1) {
+			break;
+		}
+	}
+	c = 224 + i;
+	return pgp_write(output, &c, 1);
+}
+
+static unsigned
+stream_write_litdata(pgp_output_t *output,
+			const uint8_t *data,
+			unsigned len)
+{
+	size_t          pdlen;
+
+	while (len > 0) {
+		pdlen = partial_data_len(len);
+		write_partial_len(output, (unsigned)pdlen);
+		pgp_write(output, data, (unsigned)pdlen);
+		data += pdlen;
+		len -= (unsigned)pdlen;
+	}
+	return 1;
+}
+
+static unsigned
+stream_write_litdata_first(pgp_output_t *output,
+				const uint8_t *data,
+				unsigned len,
+				const pgp_litdata_enum type)
+{
+	/* \todo add filename  */
+	/* \todo add date */
+	/* \todo do we need to check text data for <cr><lf> line endings ? */
+
+	unsigned	sz_towrite;
+	size_t		sz_pd;
+
+	sz_towrite = 1 + 1 + 4 + len;
+	sz_pd = (size_t)partial_data_len(sz_towrite);
+	if (sz_pd < 512) {
+		(void) fprintf(stderr,
+			"stream_write_litdata_first: bad sz_pd\n");
+		return 0;
+	}
+	pgp_write_ptag(output, PGP_PTAG_CT_LITDATA);
+	write_partial_len(output, (unsigned)sz_pd);
+	pgp_write_scalar(output, (unsigned)type, 1);
+	pgp_write_scalar(output, 0, 1);
+	pgp_write_scalar(output, 0, 4);
+	pgp_write(output, data, (unsigned)(sz_pd - 6));
+
+	data += (sz_pd - 6);
+	sz_towrite -= (unsigned)sz_pd;
+
+	return stream_write_litdata(output, data, (unsigned)sz_towrite);
+}
+
+static unsigned
+stream_write_litdata_last(pgp_output_t *output,
+				const uint8_t *data,
+				unsigned len)
+{
+	pgp_write_length(output, len);
+	return pgp_write(output, data, len);
+}
+
+static unsigned
+stream_write_se_ip(pgp_output_t *output,
+			const uint8_t *data,
+			unsigned len,
+			str_enc_se_ip_t *se_ip)
+{
+	size_t          pdlen;
+
+	while (len > 0) {
+		pdlen = partial_data_len(len);
+		write_partial_len(output, (unsigned)pdlen);
+
+		pgp_push_enc_crypt(output, se_ip->crypt);
+		pgp_write(output, data, (unsigned)pdlen);
+		pgp_writer_pop(output);
+
+		se_ip->hash.add(&se_ip->hash, data, (unsigned)pdlen);
+
+		data += pdlen;
+		len -= (unsigned)pdlen;
+	}
+	return 1;
+}
+
+static unsigned
+stream_write_se_ip_first(pgp_output_t *output,
+			const uint8_t *data,
+			unsigned len,
+			str_enc_se_ip_t *se_ip)
+{
+	uint8_t	*preamble;
+	size_t	blocksize;
+	size_t 	preamblesize;
+	size_t 	sz_towrite;
+	size_t 	sz_pd;
+
+	blocksize = se_ip->crypt->blocksize;
+	preamblesize = blocksize + 2;
+	sz_towrite = preamblesize + 1 + len;
+	if ((preamble = calloc(1, preamblesize)) == NULL) {
+		(void) fprintf(stderr,
+			"stream_write_se_ip_first: bad alloc\n");
+		return 0;
+	}
+	sz_pd = (size_t)partial_data_len((unsigned)sz_towrite);
+	if (sz_pd < 512) {
+		free(preamble);
+		(void) fprintf(stderr,
+			"stream_write_se_ip_first: bad sz_pd\n");
+		return 0;
+	}
+	pgp_write_ptag(output, PGP_PTAG_CT_SE_IP_DATA);
+	write_partial_len(output, (unsigned)sz_pd);
+	pgp_write_scalar(output, PGP_SE_IP_DATA_VERSION, 1);
+	pgp_push_enc_crypt(output, se_ip->crypt);
+
+	pgp_random(preamble, blocksize);
+	preamble[blocksize] = preamble[blocksize - 2];
+	preamble[blocksize + 1] = preamble[blocksize - 1];
+	pgp_hash_any(&se_ip->hash, PGP_HASH_SHA1);
+	if (!se_ip->hash.init(&se_ip->hash)) {
+		free(preamble);
+		(void) fprintf(stderr,
+			"stream_write_se_ip_first: bad hash init\n");
+		return 0;
+	}
+	pgp_write(output, preamble, (unsigned)preamblesize);
+	se_ip->hash.add(&se_ip->hash, preamble, (unsigned)preamblesize);
+	pgp_write(output, data, (unsigned)(sz_pd - preamblesize - 1));
+	se_ip->hash.add(&se_ip->hash, data, (unsigned)(sz_pd - preamblesize - 1));
+	data += (sz_pd - preamblesize - 1);
+	sz_towrite -= sz_pd;
+	pgp_writer_pop(output);
+	stream_write_se_ip(output, data, (unsigned)sz_towrite, se_ip);
+	free(preamble);
+	return 1;
+}
+
+static unsigned
+stream_write_se_ip_last(pgp_output_t *output,
+			const uint8_t *data,
+			unsigned len,
+			str_enc_se_ip_t *se_ip)
+{
+	pgp_output_t	*mdcoutput;
+	pgp_memory_t	*mdcmem;
+	const size_t	 mdcsize = 1 + 1 + PGP_SHA1_HASH_SIZE;
+	uint8_t		 c;
+	uint8_t		 hashed[PGP_SHA1_HASH_SIZE];
+	size_t		 bufsize = len + mdcsize;
+
+	se_ip->hash.add(&se_ip->hash, data, len);
+
+	/* MDC packet tag */
+	c = MDC_PKT_TAG;
+	se_ip->hash.add(&se_ip->hash, &c, 1);
+
+	/* MDC packet len */
+	c = PGP_SHA1_HASH_SIZE;
+	se_ip->hash.add(&se_ip->hash, &c, 1);
+
+	/* finish */
+	se_ip->hash.finish(&se_ip->hash, hashed);
+
+	pgp_setup_memory_write(&mdcoutput, &mdcmem, mdcsize);
+	pgp_write_mdc(mdcoutput, hashed);
+
+	/* write length of last se_ip chunk */
+	pgp_write_length(output, (unsigned)bufsize);
+
+	/* encode everting */
+	pgp_push_enc_crypt(output, se_ip->crypt);
+
+	pgp_write(output, data, len);
+	pgp_write(output, pgp_mem_data(mdcmem), (unsigned)pgp_mem_len(mdcmem));
+
+	pgp_writer_pop(output);
+
+	pgp_teardown_memory_write(mdcoutput, mdcmem);
+
+	return 1;
+}
+
+static unsigned
+str_enc_se_ip_writer(const uint8_t *src,
+			    unsigned len,
+			    pgp_error_t **errors,
+			    pgp_writer_t *writer)
+{
+	str_enc_se_ip_t	*se_ip;
+	unsigned	 ret;
+	size_t           datalength;
+
+	se_ip = pgp_writer_get_arg(writer);
+	if (se_ip->litoutput == NULL) {
+		/* first literal data chunk is not yet written */
+
+		pgp_memory_add(se_ip->mem_data, src, len);
+		datalength = pgp_mem_len(se_ip->mem_data);
+
+		/* 4.2.2.4. Partial Body Lengths */
+		/* The first partial length MUST be at least 512 octets long. */
+		if (datalength < 512) {
+			return 1;	/* will wait for more data or
+						 * end of stream             */
+		}
+		pgp_setup_memory_write(&se_ip->litoutput,
+				&se_ip->litmem, datalength + 32);
+		stream_write_litdata_first(se_ip->litoutput,
+				pgp_mem_data(se_ip->mem_data),
+				(unsigned)datalength,
+				PGP_LDT_BINARY);
+
+		stream_write_se_ip_first(se_ip->se_ip_out,
+				pgp_mem_data(se_ip->litmem),
+				(unsigned)pgp_mem_len(se_ip->litmem), se_ip);
+	} else {
+		stream_write_litdata(se_ip->litoutput, src, len);
+		stream_write_se_ip(se_ip->se_ip_out,
+				pgp_mem_data(se_ip->litmem),
+				(unsigned)pgp_mem_len(se_ip->litmem), se_ip);
+	}
+
+	/* now write memory to next writer */
+	ret = stacked_write(writer, pgp_mem_data(se_ip->se_ip_mem),
+				(unsigned)pgp_mem_len(se_ip->se_ip_mem), errors);
+
+	pgp_memory_clear(se_ip->litmem);
+	pgp_memory_clear(se_ip->se_ip_mem);
+
+	return ret;
+}
+
+/* write last chunk of data */
+static unsigned
+str_enc_se_ip_finaliser(pgp_error_t **errors, pgp_writer_t *writer)
+{
+	str_enc_se_ip_t	*se_ip;
+
+	se_ip = pgp_writer_get_arg(writer);
+	if (se_ip->litoutput == NULL) {
+		/* first literal data chunk was not written */
+		/* so we know the total length of data, write a simple packet */
+
+		/* create literal data packet from buffered data */
+		pgp_setup_memory_write(&se_ip->litoutput, &se_ip->litmem,
+				 pgp_mem_len(se_ip->mem_data) + 32);
+
+		pgp_write_litdata(se_ip->litoutput,
+			pgp_mem_data(se_ip->mem_data),
+			(const int)pgp_mem_len(se_ip->mem_data),
+			PGP_LDT_BINARY);
+
+		/* create SE IP packet set from this literal data */
+		pgp_write_se_ip_pktset(se_ip->se_ip_out,
+				pgp_mem_data(se_ip->litmem),
+				(unsigned)pgp_mem_len(se_ip->litmem),
+				se_ip->crypt);
+
+	} else {
+		/* finish writing */
+		stream_write_litdata_last(se_ip->litoutput, NULL, 0);
+		stream_write_se_ip_last(se_ip->se_ip_out,
+				pgp_mem_data(se_ip->litmem),
+				(unsigned)pgp_mem_len(se_ip->litmem), se_ip);
+	}
+
+	/* now write memory to next writer */
+	return stacked_write(writer, pgp_mem_data(se_ip->se_ip_mem),
+				 (unsigned)pgp_mem_len(se_ip->se_ip_mem), errors);
+}
+
+static void
+str_enc_se_ip_destroyer(pgp_writer_t *writer)
+{
+	str_enc_se_ip_t *se_ip;
+
+	se_ip = pgp_writer_get_arg(writer);
+	pgp_memory_free(se_ip->mem_data);
+	pgp_teardown_memory_write(se_ip->litoutput, se_ip->litmem);
+	pgp_teardown_memory_write(se_ip->se_ip_out, se_ip->se_ip_mem);
+
+	se_ip->crypt->decrypt_finish(se_ip->crypt);
+
+	free(se_ip->crypt);
+	free(se_ip);
+}