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(®ion, 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(®ion, stream); + break; + + case PGP_PTAG_CT_PUBLIC_KEY: + case PGP_PTAG_CT_PUBLIC_SUBKEY: + ret = parse_pubkey((pgp_content_enum)pkt.u.ptag.type, ®ion, stream); + break; + + case PGP_PTAG_CT_TRUST: + ret = parse_trust(®ion, stream); + break; + + case PGP_PTAG_CT_USER_ID: + ret = parse_userid(®ion, stream); + break; + + case PGP_PTAG_CT_COMPRESSED: + ret = parse_compressed(®ion, stream); + break; + + case PGP_PTAG_CT_1_PASS_SIG: + ret = parse_one_pass(®ion, stream); + break; + + case PGP_PTAG_CT_LITDATA: + ret = parse_litdata(®ion, stream); + break; + + case PGP_PTAG_CT_USER_ATTR: + ret = parse_userattr(®ion, stream); + break; + + case PGP_PTAG_CT_SECRET_KEY: + case PGP_PTAG_CT_SECRET_SUBKEY: + ret = parse_seckey((pgp_content_enum)pkt.u.ptag.type, ®ion, stream); + break; + + case PGP_PTAG_CT_PK_SESSION_KEY: + ret = parse_pk_sesskey(®ion, stream); + break; + + case PGP_PTAG_CT_SE_DATA: + // SE_DATA CURRENTLY BROKEN + ret = 0; + //ret = parse_se_data(®ion, stream); + break; + + case PGP_PTAG_CT_SE_IP_DATA: + ret = parse_se_ip_data(®ion, stream); + break; + + case PGP_PTAG_CT_MDC: + ret = parse_mdc(®ion, 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(®ion, 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(®ion, 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); +}