diff --git a/README.txt b/README.txt new file mode 100644 index 000000000..1b95ef7c5 --- /dev/null +++ b/README.txt @@ -0,0 +1,42 @@ +To use this branch: + + chekcout the last version of openpgp SDK: + # svn co svn://openpgp.nominet.org.uk/openpgpsdk/tags/openpgpsdk-0.9 openpgpsdk + # cd openpgpsdk + # ./configure --without-idea + # make + + For the moment, the compilation is not workign on ubuntu + +Work to do +========== +Put a 'x' when done. 1,2,3 means started/ongoing/almost finished. + +Compilation + 00 [1] make sure the library compiles on linux + 01 [ ] make sure the library compiles on windows + +Project + 02 [1] determine what's missing in OpenPGP-SDK + 03 [3] make a separate layer in RS to handle PGP. AuthPGP is too close to libretroshare. + 04 [1] write the new AuthGPG class + 05 [ ] consider removing thread behaviour of AuthGPG + 06 [ ] remove callback system and services from AuthGPG, since it's not useful anymore + 07 [ ] make all RS use GPGIdType isntead of std::string. + +Notes +===== + Questions to answer: + - do we rely on updates from openPGP-sdk ? Probably not. This code seems frozen. + - do we need an abstract layer for PGP handling in RS ? + - what new functionalities do we need in RS ? + * pgp keyring sharing/import/export + * identity import/export + + Code struture + - replace current AuthGPG (virtual class) by a class named GPGHandler, + that is responsible for signing, checking signatures, encrypting etc. + - add a specific 8-bytes type for GPG Ids. Could be a uint64_t, or a + uchar[8] + + diff --git a/RetroShare.pro b/RetroShare.pro index 04e0b55a9..cea9ce545 100644 --- a/RetroShare.pro +++ b/RetroShare.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS += \ + openpgpsdk/src/src.pro \ libbitdht/src/libbitdht.pro \ libretroshare/src/libretroshare.pro \ retroshare-gui/src/RetroShare.pro \ diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index a073084ad..9ae55db4e 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -176,6 +176,9 @@ linux-* { INCLUDEPATH *= /usr/lib/x86_64-linux-gnu/glib-2.0/include/ INCLUDEPATH *= /usr/lib/i386-linux-gnu/glib-2.0/include/ + OPENPGPSDK_DIR = ../../openpgpsdk/src + INCLUDEPATH *= $${OPENPGPSDK_DIR} + DESTDIR = lib QMAKE_CXXFLAGS *= -Wall -D_FILE_OFFSET_BITS=64 QMAKE_CC = g++ @@ -293,6 +296,8 @@ win32 { ZLIB_DIR = ../../../zlib-1.2.3 SSL_DIR = ../../../../OpenSSL + OPENPGPSDK_DIR = ../../openpgpsdk/src + INCLUDEPATH *= $${OPENPGPSDK_DIR} INCLUDEPATH += . $${SSL_DIR}/include $${UPNPC_DIR} $${PTHREADS_DIR} $${ZLIB_DIR} $${GPGME_DIR}/src $${GPG_ERROR_DIR}/src } @@ -365,6 +370,8 @@ HEADERS += ft/ftchunkmap.h \ HEADERS += pqi/authssl.h \ pqi/authgpg.h \ + pgp/pgphandler.h \ + pgp/pgpkeyutil.h \ pqi/cleanupxpgp.h \ pqi/p3cfgmgr.h \ pqi/p3peermgr.h \ @@ -483,7 +490,6 @@ HEADERS += util/folderiterator.h \ util/rsrandom.h \ util/pugiconfig.h \ util/radix64.h \ - util/pgpkey.h \ util/pugixml.h SOURCES += dbase/cachestrapper.cc \ @@ -507,6 +513,8 @@ SOURCES += ft/ftchunkmap.cc \ SOURCES += pqi/authgpg.cc \ pqi/authssl.cc \ + pgp/pgphandler.cc \ + pgp/pgpkeyutil.cc \ pqi/cleanupxpgp.cc \ pqi/p3cfgmgr.cc \ pqi/p3peermgr.cc \ @@ -626,7 +634,6 @@ SOURCES += util/folderiterator.cc \ util/rsversion.cc \ util/rswin.cc \ util/rsrandom.cc \ - util/pgpkey.cc \ util/pugixml.cc zeroconf { diff --git a/libretroshare/src/pgp/pgphandler.cc b/libretroshare/src/pgp/pgphandler.cc new file mode 100644 index 000000000..43ade6ecf --- /dev/null +++ b/libretroshare/src/pgp/pgphandler.cc @@ -0,0 +1,1329 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef WINDOWS_SYS +#include "util/rsstring.h" +#endif + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +} +#include "pgphandler.h" +#include "retroshare/rsiface.h" // For rsicontrol. +#include "util/rsdir.h" +#include "pgp/pgpkeyutil.h" + +//#define DEBUG_PGPHANDLER 1 + +PassphraseCallback PGPHandler::_passphrase_callback = NULL ; + +ops_keyring_t *PGPHandler::allocateOPSKeyring() +{ + ops_keyring_t *kr = (ops_keyring_t*)malloc(sizeof(ops_keyring_t)) ; + kr->nkeys = 0 ; + kr->nkeys_allocated = 0 ; + kr->keys = 0 ; + + return kr ; +} + +ops_parse_cb_return_t cb_get_passphrase(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo)// __attribute__((unused))) +{ + const ops_parser_content_union_t *content=&content_->content; + bool prev_was_bad = false ; + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD: prev_was_bad = true ; + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + { + std::string passwd; + std::string uid_hint = std::string((const char *)cbinfo->cryptinfo.keydata->uids[0].user_id) ; + uid_hint += "(" + PGPIdType(cbinfo->cryptinfo.keydata->key_id).toStdString()+")" ; + + passwd = PGPHandler::passphraseCallback()(NULL,uid_hint.c_str(),NULL,prev_was_bad) ; + *(content->secret_key_passphrase.passphrase)= (char *)ops_mallocz(passwd.length()+1) ; + memcpy(*(content->secret_key_passphrase.passphrase),passwd.c_str(),passwd.length()) ; + return OPS_KEEP_MEMORY; + } + break; + + default: + break; + } + + return OPS_RELEASE_MEMORY; +} +void PGPHandler::setPassphraseCallback(PassphraseCallback cb) +{ + _passphrase_callback = cb ; +} + +PGPHandler::PGPHandler(const std::string& pubring, const std::string& secring,const std::string& trustdb,const std::string& pgp_lock_filename) + : pgphandlerMtx(std::string("PGPHandler")), _pubring_path(pubring),_secring_path(secring),_trustdb_path(trustdb),_pgp_lock_filename(pgp_lock_filename) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + _pubring_changed = false ; + _trustdb_changed = false ; + + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + if(_passphrase_callback == NULL) + { + std::cerr << "WARNING: before created a PGPHandler, you need to init the passphrase callback using PGPHandler::setPassphraseCallback()" << std::endl; + exit(-1) ; + } + + // Allocate public and secret keyrings. + // + _pubring = allocateOPSKeyring() ; + _secring = allocateOPSKeyring() ; + + // Check that the file exists. If not, create a void keyring. + + FILE *ftest ; + ftest = fopen(pubring.c_str(),"rb") ; + bool pubring_exist = (ftest != NULL) ; + if(ftest != NULL) + fclose(ftest) ; + ftest = fopen(secring.c_str(),"rb") ; + bool secring_exist = (ftest != NULL) ; + if(ftest != NULL) + fclose(ftest) ; + + // Read public and secret keyrings from supplied files. + // + if(pubring_exist) + { + if(ops_false == ops_keyring_read_from_file(_pubring, false, pubring.c_str())) + throw std::runtime_error("PGPHandler::readKeyRing(): cannot read pubring. File corrupted.") ; + } + else + std::cerr << "pubring file \"" << pubring << "\" not found. Creating a void keyring." << std::endl; + + const ops_keydata_t *keydata ; + int i=0 ; + while( (keydata = ops_keyring_get_key_by_index(_pubring,i)) != NULL ) + { + PGPCertificateInfo& cert(_public_keyring_map[ PGPIdType(keydata->key_id).toStdString() ]) ; + + // Init all certificates. + + initCertificateInfo(cert,keydata,i) ; + + // Validate signatures. + + validateAndUpdateSignatures(cert,keydata) ; + + ++i ; + } + _pubring_last_update_time = time(NULL) ; + std::cerr << "Pubring read successfully." << std::endl; + + if(secring_exist) + { + if(ops_false == ops_keyring_read_from_file(_secring, false, secring.c_str())) + throw std::runtime_error("PGPHandler::readKeyRing(): cannot read secring. File corrupted.") ; + } + else + std::cerr << "secring file \"" << secring << "\" not found. Creating a void keyring." << std::endl; + + i=0 ; + while( (keydata = ops_keyring_get_key_by_index(_secring,i)) != NULL ) + { + initCertificateInfo(_secret_keyring_map[ PGPIdType(keydata->key_id).toStdString() ],keydata,i) ; + ++i ; + } + _secring_last_update_time = time(NULL) ; + + std::cerr << "Secring read successfully." << std::endl; + + locked_readPrivateTrustDatabase() ; + _trustdb_last_update_time = time(NULL) ; +} + +void PGPHandler::initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t index) +{ + // Parse certificate name + // + + if(keydata->uids != NULL) + { + std::string namestring( (char *)keydata->uids[0].user_id ) ; + + cert._name = "" ; + uint32_t i=0; + while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { cert._name += namestring[i] ; ++i ;} + + std::string& next = (namestring[i] == '(')?cert._comment:cert._email ; + ++i ; + next = "" ; + while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next += namestring[i] ; ++i ;} + + while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { next += namestring[i] ; ++i ;} + + if(i< namestring.length()) + { + std::string& next2 = (namestring[i] == '(')?cert._comment:cert._email ; + ++i ; + next2 = "" ; + while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next2 += namestring[i] ; ++i ;} + } + } + + cert._trustLvl = 1 ; // to be setup accordingly + cert._validLvl = 1 ; // to be setup accordingly + cert._key_index = index ; + cert._flags = 0 ; + + ops_fingerprint_t f ; + ops_fingerprint(&f,&keydata->key.pkey) ; + + cert._fpr = PGPFingerprintType(f.fingerprint) ; + + if(keydata->key.pkey.algorithm != OPS_PKA_RSA) + cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM ; +} + +bool PGPHandler::validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) +{ + ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + ops_boolean_t res = ops_validate_key_signatures(result,keydata,_pubring,cb_get_passphrase) ; + + if(res == ops_false) + std::cerr << "(EE) Error in PGPHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." << std::endl; + + bool ret = false ; + + // Parse signers. + // + + if(result != NULL) + for(size_t i=0;ivalid_count;++i) + { + std::string signer_str = PGPIdType(result->valid_sigs[i].signer_id).toStdString() ; + + if(cert.signers.find(signer_str) == cert.signers.end()) + { + cert.signers.insert(signer_str) ; + ret = true ; + } + } + + ops_validate_result_free(result) ; + + return ret ; +} + +PGPHandler::~PGPHandler() +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. +#ifdef DEBUG_PGPHANDLER + std::cerr << "Freeing PGPHandler. Deleting keyrings." << std::endl; +#endif + + // no need to free the the _map_ elements. They will be freed by the following calls: + // + ops_keyring_free(_pubring) ; + ops_keyring_free(_secring) ; + + free(_pubring) ; + free(_secring) ; +} + +bool PGPHandler::printKeys() const +{ +#ifdef DEBUG_PGPHANDLER + std::cerr << "Printing details of all " << std::dec << _public_keyring_map.size() << " keys: " << std::endl; +#endif + + for(std::map::const_iterator it(_public_keyring_map.begin()); it != _public_keyring_map.end(); it++) + { + std::cerr << "PGP Key: " << it->first << std::endl; + + std::cerr << "\tName : " << it->second._name << std::endl; + std::cerr << "\tEmail : " << it->second._email << std::endl; + std::cerr << "\tOwnSign : " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE) << std::endl; + std::cerr << "\tAccept Connect: " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION) << std::endl; + std::cerr << "\ttrustLvl : " << it->second._trustLvl << std::endl; + std::cerr << "\tvalidLvl : " << it->second._validLvl << std::endl; + std::cerr << "\tfingerprint : " << it->second._fpr.toStdString() << std::endl; + std::cerr << "\tSigners : " << it->second.signers.size() << std::endl; + + std::set::const_iterator sit; + for(sit = it->second.signers.begin(); sit != it->second.signers.end(); sit++) + { + std::cerr << "\t\tSigner ID:" << *sit << ", Name: " ; + const PGPCertificateInfo *info = PGPHandler::getCertificateInfo(PGPIdType(*sit)) ; + + if(info != NULL) + std::cerr << info->_name ; + + std::cerr << std::endl ; + } + } + std::cerr << "Public keyring list from OPS:" << std::endl; + ops_keyring_list(_pubring) ; + + return true ; +} + +bool PGPHandler::haveSecretKey(const PGPIdType& id) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + return getSecretKey(id) != NULL ; +} + +const PGPCertificateInfo *PGPHandler::getCertificateInfo(const PGPIdType& id) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + std::map::const_iterator it( _public_keyring_map.find(id.toStdString()) ) ; + + if(it != _public_keyring_map.end()) + return &it->second; + else + return NULL ; +} + +bool PGPHandler::availableGPGCertificatesWithPrivateKeys(std::list& ids) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + // go through secret keyring, and check that we have the pubkey as well. + // + + const ops_keydata_t *keydata = NULL ; + int i=0 ; + + while( (keydata = ops_keyring_get_key_by_index(_secring,i++)) != NULL ) + if(ops_keyring_find_key_by_id(_pubring,keydata->key_id) != NULL) // check that the key is in the pubring as well + { + if(keydata->key.pkey.algorithm == OPS_PKA_RSA) + ids.push_back(PGPIdType(keydata->key_id)) ; +#ifdef DEBUG_PGPHANDLER + else + std::cerr << "Skipping keypair " << PGPIdType(keydata->key_id).toStdString() << ", unsupported algorithm: " << keydata->key.pkey.algorithm << std::endl; +#endif + } + + return true ; +} + +bool PGPHandler::GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passphrase, PGPIdType& pgpId, std::string& errString) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + static const int KEY_NUMBITS = 2048 ; + + // 1 - generate keypair - RSA-2048 + // + ops_user_id_t uid ; + char *s = strdup((name + " (Generated by RetroShare) <" + email + ">" ).c_str()) ; + uid.user_id = (unsigned char *)s ; + unsigned long int e = 65537 ; // some prime number + + ops_keydata_t *key = ops_rsa_create_selfsigned_keypair(KEY_NUMBITS,e,&uid) ; + + free(s) ; + + if(!key) + return false ; + + // 2 - save the private key encrypted to a temporary memory buffer, so as to read an encrypted key to memory + + ops_create_info_t *cinfo = NULL ; + ops_memory_t *buf = NULL ; + ops_setup_memory_write(&cinfo, &buf, 0); + + if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) + { + errString = std::string("(EE) Cannot encode secret key to memory!!") ; + return false ; + } + + // 3 - read the memory chunk into an encrypted keyring + + ops_keyring_t *tmp_secring = allocateOPSKeyring() ; + + if(! ops_keyring_read_from_mem(tmp_secring, ops_false, buf)) + { + errString = std::string("(EE) Cannot re-read key from memory!!") ; + return false ; + } + ops_teardown_memory_write(cinfo,buf); // cleanup memory + + // 4 - copy the encrypted private key to the private keyring + + pgpId = PGPIdType(tmp_secring->keys[0].key_id) ; + addNewKeyToOPSKeyring(_secring,tmp_secring->keys[0]) ; + initCertificateInfo(_secret_keyring_map[ pgpId.toStdString() ],&tmp_secring->keys[0],_secring->nkeys-1) ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Added new secret key with id " << pgpId.toStdString() << " to secret keyring." << std::endl; +#endif + ops_keyring_free(tmp_secring) ; + free(tmp_secring) ; + + // 5 - add key to secret keyring on disk. + + cinfo = NULL ; + int fd=ops_setup_file_append(&cinfo, _secring_path.c_str()); + + if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) + { + errString= std::string("Cannot encode secret key to disk!! Disk full? Out of disk quota?") ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + + // 6 - copy the public key to the public keyring on disk + + cinfo = NULL ; + fd=ops_setup_file_append(&cinfo, _pubring_path.c_str()); + + if(!ops_write_transferable_public_key(key, ops_false, cinfo)) + { + errString=std::string("Cannot encode secret key to memory!!") ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + + // 7 - clean + ops_keydata_free(key) ; + + // 8 - re-read the key from the public keyring, and add it to memory. + + _pubring_last_update_time = 0 ; // force update pubring from disk. + locked_syncPublicKeyring() ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Added new public key with id " << pgpId.toStdString() << " to public keyring." << std::endl; +#endif + + // 9 - Update some flags. + + privateTrustCertificate(pgpId,PGPCertificateInfo::PGP_CERTIFICATE_TRUST_ULTIMATE) ; + + return true ; +} + +std::string PGPHandler::makeRadixEncodedPGPKey(const ops_keydata_t *key) +{ + ops_boolean_t armoured=ops_true; + ops_create_info_t* cinfo; + + ops_memory_t *buf = NULL ; + ops_setup_memory_write(&cinfo, &buf, 0); + + if(key->type == OPS_PTAG_CT_PUBLIC_KEY) + { + if(ops_write_transferable_public_key_from_packet_data(key,armoured,cinfo) != ops_true) + return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; + } + else if(key->type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + { + if(ops_write_transferable_secret_key_from_packet_data(key,armoured,cinfo) != ops_true) + return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; + } + else + { + std::cerr << "Unhandled key type " << key->type << std::endl; + return "ERROR: Cannot write key. Unhandled key type. " ; + } + + ops_writer_close(cinfo) ; + + std::string akey((char *)ops_memory_get_data(buf),ops_memory_get_length(buf)) ; + + ops_teardown_memory_write(cinfo,buf); + + return akey ; +} + +const ops_keydata_t *PGPHandler::getSecretKey(const PGPIdType& id) const +{ + std::map::const_iterator res = _secret_keyring_map.find(id.toStdString()) ; + + if(res == _secret_keyring_map.end()) + return NULL ; + else + return ops_keyring_get_key_by_index(_secring,res->second._key_index) ; +} +const ops_keydata_t *PGPHandler::getPublicKey(const PGPIdType& id) const +{ + std::map::const_iterator res = _public_keyring_map.find(id.toStdString()) ; + + if(res == _public_keyring_map.end()) + return NULL ; + else + return ops_keyring_get_key_by_index(_pubring,res->second._key_index) ; +} + +std::string PGPHandler::SaveCertificateToString(const PGPIdType& id,bool) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + const ops_keydata_t *key = getPublicKey(id) ; + + if(key == NULL) + { + std::cerr << "Cannot output key " << id.toStdString() << ": not found in keyring." << std::endl; + return "" ; + } + + return makeRadixEncodedPGPKey(key) ; +} + +bool PGPHandler::exportGPGKeyPair(const std::string& filename,const PGPIdType& exported_key_id) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + const ops_keydata_t *pubkey = getPublicKey(exported_key_id) ; + + if(pubkey == NULL) + { + std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": not found in public keyring." << std::endl; + return false ; + } + const ops_keydata_t *seckey = getSecretKey(exported_key_id) ; + + if(seckey == NULL) + { + std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": not found in secret keyring." << std::endl; + return false ; + } + + FILE *f = fopen(filename.c_str(),"w") ; + if(f == NULL) + { + std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": file " << filename << " cannot be written. Please check for permissions, quotas, disk space." << std::endl; + return false ; + } + + fprintf(f,"%s\n", makeRadixEncodedPGPKey(pubkey).c_str()) ; + fprintf(f,"%s\n", makeRadixEncodedPGPKey(seckey).c_str()) ; + + fclose(f) ; + return true ; +} + +bool PGPHandler::importGPGKeyPair(const std::string& filename,PGPIdType& imported_key_id,std::string& import_error) +{ + import_error = "" ; + + // 1 - Test for file existance + // + FILE *ftest = fopen(filename.c_str(),"r") ; + + if(ftest == NULL) + { + import_error = "Cannot open file " + filename + " for read. Please check access permissions." ; + return false ; + } + + fclose(ftest) ; + + // 2 - Read keyring from supplied file. + // + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + + if(ops_false == ops_keyring_read_from_file(tmp_keyring, ops_true, filename.c_str())) + { + import_error = "PGPHandler::readKeyRing(): cannot read key file. File corrupted?" ; + return false ; + } + if(tmp_keyring->nkeys != 2) + { + import_error = "PGPHandler::importKeyPair(): file does not contain a valid keypair." ; + if(tmp_keyring->nkeys > 2) + import_error += "\nMake sure that your key is a RSA key (DSA is not yet supported) and does not contain subkeys (not supported yet)."; + return false ; + } + + // 3 - Test that keyring contains a valid keypair. + // + const ops_keydata_t *pubkey = NULL ; + const ops_keydata_t *seckey = NULL ; + + if(tmp_keyring->keys[0].type == OPS_PTAG_CT_PUBLIC_KEY) + pubkey = &tmp_keyring->keys[0] ; + else if(tmp_keyring->keys[0].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + seckey = &tmp_keyring->keys[0] ; + else + { + import_error = "Unrecognised key type in key file for key #0. Giving up." ; + std::cerr << "Unrecognised key type " << tmp_keyring->keys[0].type << " in key file for key #0. Giving up." << std::endl; + return false ; + } + if(tmp_keyring->keys[1].type == OPS_PTAG_CT_PUBLIC_KEY) + pubkey = &tmp_keyring->keys[1] ; + else if(tmp_keyring->keys[1].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + seckey = &tmp_keyring->keys[1] ; + else + { + import_error = "Unrecognised key type in key file for key #1. Giving up." ; + std::cerr << "Unrecognised key type " << tmp_keyring->keys[1].type << " in key file for key #1. Giving up." << std::endl; + return false ; + } + + if(pubkey == NULL || seckey == NULL || pubkey == seckey) + { + import_error = "File does not contain a public and a private key. Sorry." ; + return false ; + } + if(memcmp(pubkey->fingerprint.fingerprint,seckey->fingerprint.fingerprint,KEY_FINGERPRINT_SIZE) != 0) + { + import_error = "Public and private keys do nt have the same fingerprint. Sorry!" ; + return false ; + } + if(pubkey->key.pkey.version != 4) + { + import_error = "Public key is not version 4. Rejected!" ; + return false ; + } + + // 4 - now check self-signature for this keypair. For this we build a dummy keyring containing only the key. + // + ops_validate_result_t *result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + + ops_keyring_t dummy_keyring ; + dummy_keyring.nkeys=1 ; + dummy_keyring.nkeys_allocated=1 ; + dummy_keyring.keys=const_cast(pubkey) ; + + if( (!ops_validate_key_signatures(result, const_cast(pubkey), &dummy_keyring, cb_get_passphrase)) || result->valid_count != 1 || result->invalid_count > 0) + { + import_error = "Cannot validate self signature for the imported key. Sorry." ; + return false ; + } + ops_validate_result_free(result); + + // 5 - All test passed. Adding key to keyring. + // + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + imported_key_id = PGPIdType(pubkey->key_id) ; + + if(getSecretKey(imported_key_id) == NULL) + { + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + ops_create_info_t *cinfo = NULL ; + int fd=ops_setup_file_append(&cinfo, _secring_path.c_str()); + + if(!ops_write_transferable_secret_key_from_packet_data(seckey,ops_false,cinfo)) + { + import_error = "(EE) Cannot encode secret key to disk!! Disk full? Out of disk quota?" ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + } + else + import_error = "Private key already exists! Not importing it again." ; + + if(addOrMergeKey(_pubring,_public_keyring_map,pubkey)) + _pubring_changed = true ; + + // 6 - clean + // + ops_keyring_free(tmp_keyring) ; + + return true ; +} + +void PGPHandler::addNewKeyToOPSKeyring(ops_keyring_t *kr,const ops_keydata_t& key) +{ + kr->keys = (ops_keydata_t*)realloc(kr->keys,(kr->nkeys+1)*sizeof(ops_keydata_t)) ; + memset(&kr->keys[kr->nkeys],0,sizeof(ops_keydata_t)) ; + ops_keydata_copy(&kr->keys[kr->nkeys],&key) ; + kr->nkeys++ ; +} + +bool PGPHandler::LoadCertificateFromString(const std::string& pgp_cert,PGPIdType& id,std::string& error_string) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. +#ifdef DEBUG_PGPHANDLER + std::cerr << "Reading new key from string: " << std::endl; +#endif + + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + ops_memory_t *mem = ops_memory_new() ; + ops_memory_add(mem,(unsigned char *)pgp_cert.c_str(),pgp_cert.length()) ; + + if(!ops_keyring_read_from_mem(tmp_keyring,ops_true,mem)) + { + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + ops_memory_release(mem) ; + free(mem) ; + + std::cerr << "Could not read key. Format error?" << std::endl; + error_string = std::string("Could not read key. Format error?") ; + return false ; + } + ops_memory_release(mem) ; + free(mem) ; + error_string.clear() ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << " Key read correctly: " << std::endl; +#endif + ops_keyring_list(tmp_keyring) ; + + const ops_keydata_t *keydata = NULL ; + int i=0 ; + + while( (keydata = ops_keyring_get_key_by_index(tmp_keyring,i++)) != NULL ) + if(addOrMergeKey(_pubring,_public_keyring_map,keydata)) + { + _pubring_changed = true ; +#ifdef DEBUG_PGPHANDLER + std::cerr << " Added the key in the main public keyring." << std::endl; +#endif + } + else + std::cerr << "Key already in public keyring." << std::endl; + + id = PGPIdType(tmp_keyring->keys[0].key_id) ; + + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + + _pubring_changed = true ; + + return true ; +} + +bool PGPHandler::addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) +{ + bool ret = false ; + PGPIdType id(keydata->key_id) ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << "AddOrMergeKey():" << std::endl; + std::cerr << " id: " << id.toStdString() << std::endl; +#endif + + // See if the key is already in the keyring + const ops_keydata_t *existing_key = NULL; + std::map::const_iterator res = kmap.find(id.toStdString()) ; + + // Checks that + // - the key is referenced by keyid + // - the map is initialized + // - the fingerprint matches! + // + if(res == kmap.end() || (existing_key = ops_keyring_get_key_by_index(keyring,res->second._key_index)) == NULL) + { +#ifdef DEBUG_PGPHANDLER + std::cerr << " Key is new. Adding it to keyring" << std::endl; +#endif + addNewKeyToOPSKeyring(keyring,*keydata) ; // the key is new. + initCertificateInfo(kmap[id.toStdString()],keydata,keyring->nkeys-1) ; + existing_key = &(keyring->keys[keyring->nkeys-1]) ; + ret = true ; + } + else + { + if(memcmp(existing_key->fingerprint.fingerprint, keydata->fingerprint.fingerprint,KEY_FINGERPRINT_SIZE)) + { + std::cerr << "(EE) attempt to merge key with identical id, but different fingerprint!" << std::endl; + return false ; + } + +#ifdef DEBUG_PGPHANDLER + std::cerr << " Key exists. Merging signatures." << std::endl; +#endif + ret = mergeKeySignatures(const_cast(existing_key),keydata) ; + + if(ret) + initCertificateInfo(kmap[id.toStdString()],existing_key,res->second._key_index) ; + } + + if(ret) + validateAndUpdateSignatures(kmap[id.toStdString()],existing_key) ; + + return ret ; +} + +bool PGPHandler::encryptTextToFile(const PGPIdType& key_id,const std::string& text,const std::string& outfile) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + ops_create_info_t *info; + int fd = ops_setup_file_write(&info, outfile.c_str(), ops_true); + + const ops_keydata_t *public_key = getPublicKey(key_id) ; + + if(public_key == NULL) + { + std::cerr << "Cannot get public key of id " << key_id.toStdString() << std::endl; + return false ; + } + + if(public_key->type != OPS_PTAG_CT_PUBLIC_KEY) + { + std::cerr << "PGPHandler::encryptTextToFile(): ERROR: supplied id did not return a public key!" << outfile << std::endl; + return false ; + } + + if (fd < 0) + { + std::cerr << "PGPHandler::encryptTextToFile(): ERROR: Cannot write to " << outfile << std::endl; + return false ; + } + ops_encrypt_stream(info, public_key, NULL, ops_false, ops_true); + ops_write(text.c_str(), text.length(), info); + ops_writer_close(info); + ops_create_info_delete(info); + + return true ; +} + +bool PGPHandler::decryptTextFromFile(const PGPIdType&,std::string& text,const std::string& inputfile) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + unsigned char *out_buf = NULL ; + std::string buf ; + + FILE *f = fopen(inputfile.c_str(),"rb") ; + + if (f == NULL) + { + return false; + } + + char c ; + while( (c = getc(f))!= EOF) + buf += c; + + fclose(f) ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << "PGPHandler::decryptTextFromFile: read a file of length " << std::dec << buf.length() << std::endl; + std::cerr << "buf=\"" << buf << "\"" << std::endl; +#endif + + int out_length ; + ops_boolean_t res = ops_decrypt_memory((const unsigned char *)buf.c_str(),buf.length(),&out_buf,&out_length,_secring,ops_true,cb_get_passphrase) ; + + text = std::string((char *)out_buf,out_length) ; + free (out_buf); + return (bool)res ; +} + +bool PGPHandler::SignDataBin(const PGPIdType& id,const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + // need to find the key and to decrypt it. + + const ops_keydata_t *key = getSecretKey(id) ; + + if(!key) + { + std::cerr << "Cannot sign: no secret key with id " << id.toStdString() << std::endl; + return false ; + } + + std::string passphrase = _passphrase_callback(NULL,PGPIdType(key->key_id).toStdString().c_str(),"Please enter passwd for encrypting your key : ",false) ; + + ops_secret_key_t *secret_key = ops_decrypt_secret_key_from_data(key,passphrase.c_str()) ; + + if(!secret_key) + { + std::cerr << "Key decryption went wrong. Wrong passwd?" << std::endl; + return false ; + } + + // then do the signature. + + ops_memory_t *memres = ops_sign_buf(data,len,(ops_sig_type_t)0x00,secret_key,ops_false,ops_false) ; + + if(!memres) + return false ; + + uint32_t tlen = std::min(*signlen,(uint32_t)ops_memory_get_length(memres)) ; + + memcpy(sign,ops_memory_get_data(memres),tlen) ; + *signlen = tlen ; + + ops_memory_release(memres) ; + free(memres) ; + ops_secret_key_free(secret_key) ; + free(secret_key) ; + + return true ; +} + +bool PGPHandler::privateSignCertificate(const PGPIdType& ownId,const PGPIdType& id_of_key_to_sign) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + ops_keydata_t *key_to_sign = const_cast(getPublicKey(id_of_key_to_sign)) ; + + if(key_to_sign == NULL) + { + std::cerr << "Cannot sign: no public key with id " << id_of_key_to_sign.toStdString() << std::endl; + return false ; + } + + // 1 - get decrypted secret key + // + const ops_keydata_t *skey = getSecretKey(ownId) ; + + if(!skey) + { + std::cerr << "Cannot sign: no secret key with id " << ownId.toStdString() << std::endl; + return false ; + } + const ops_keydata_t *pkey = getPublicKey(ownId) ; + + if(!pkey) + { + std::cerr << "Cannot sign: no public key with id " << ownId.toStdString() << std::endl; + return false ; + } + + std::string passphrase = _passphrase_callback(NULL,PGPIdType(skey->key_id).toStdString().c_str(),"Please enter passwd for encrypting your key : ",false) ; + + ops_secret_key_t *secret_key = ops_decrypt_secret_key_from_data(skey,passphrase.c_str()) ; + + if(!secret_key) + { + std::cerr << "Key decryption went wrong. Wrong passwd?" << std::endl; + return false ; + } + + // 2 - then do the signature. + + if(!ops_sign_key(key_to_sign,pkey->key_id,secret_key)) + { + std::cerr << "Key signature went wrong. Wrong passwd?" << std::endl; + return false ; + } + + // 3 - free memory + // + ops_secret_key_free(secret_key) ; + free(secret_key) ; + + _pubring_changed = true ; + + // 4 - update signatures. + // + PGPCertificateInfo& cert(_public_keyring_map[ id_of_key_to_sign.toStdString() ]) ; + validateAndUpdateSignatures(cert,key_to_sign) ; + cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; + + return true ; +} + +void PGPHandler::updateOwnSignatureFlag(const PGPIdType& own_id) +{ + std::string own_id_str = own_id.toStdString(); + + for(std::map::iterator it=_public_keyring_map.begin();it!=_public_keyring_map.end();++it) + if(it->second.signers.find(own_id_str) != it->second.signers.end()) + it->second._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; + else + it->second._flags &= ~PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; +} + +bool PGPHandler::getKeyFingerprint(const PGPIdType& id,PGPFingerprintType& fp) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + const ops_keydata_t *key = getPublicKey(id) ; + + if(key == NULL) + return false ; + + ops_fingerprint_t f ; + ops_fingerprint(&f,&key->key.pkey) ; + + fp = PGPFingerprintType(f.fingerprint) ; + + return true ; +} + +bool PGPHandler::VerifySignBin(const void *literal_data, uint32_t literal_data_length, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& key_fingerprint) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + PGPIdType id = PGPIdType(key_fingerprint.toByteArray() + PGPFingerprintType::SIZE_IN_BYTES - PGPIdType::SIZE_IN_BYTES) ; + const ops_keydata_t *key = getPublicKey(id) ; + + if(key == NULL) + { + std::cerr << "No key returned by fingerprint " << key_fingerprint.toStdString() << ", and ID " << id.toStdString() << ", signature verification failed!" << std::endl; + return false ; + } + + // Check that fingerprint is the same. + const ops_public_key_t *pkey = &key->key.pkey ; + ops_fingerprint_t fp ; + ops_fingerprint(&fp,pkey) ; + + if(key_fingerprint != PGPFingerprintType(fp.fingerprint)) + { + std::cerr << "Key fingerprint does not match " << key_fingerprint.toStdString() << ", for ID " << id.toStdString() << ", signature verification failed!" << std::endl; + return false ; + } + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Verifying signature from fingerprint " << key_fingerprint.toStdString() << ", length " << std::dec << sign_len << ", literal data length = " << literal_data_length << std::endl; + std::cerr << "Signature body: " << std::endl; + hexdump( sign,sign_len) ; + std::cerr << std::endl; +#endif + + return ops_validate_detached_signature(literal_data,literal_data_length,sign,sign_len,key) ; +} + +void PGPHandler::setAcceptConnexion(const PGPIdType& id,bool b) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + std::map::iterator res = _public_keyring_map.find(id.toStdString()) ; + + if(res != _public_keyring_map.end()) + { + if(b) + res->second._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION ; + else + res->second._flags &= ~PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION ; + } +} + +bool PGPHandler::getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&)) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP directory. + list.clear() ; + + for(std::map::const_iterator it(_public_keyring_map.begin());it!=_public_keyring_map.end();++it) + if( filter == NULL || (*filter)(it->second) ) + list.push_back(PGPIdType(it->first)) ; + + return true ; +} + +bool PGPHandler::isGPGId(const std::string &id) +{ + return _public_keyring_map.find(id) != _public_keyring_map.end() ; +} + +bool PGPHandler::isGPGSigned(const std::string &id) +{ + std::map::const_iterator res = _public_keyring_map.find(id) ; + return res != _public_keyring_map.end() && (res->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE) ; +} + +bool PGPHandler::isGPGAccepted(const std::string &id) +{ + std::map::const_iterator res = _public_keyring_map.find(id) ; + return (res != _public_keyring_map.end()) && (res->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION) ; +} + +// Lexicographic order on signature packets +// +bool operator<(const ops_packet_t& p1,const ops_packet_t& p2) +{ + if(p1.length < p2.length) + return true ; + if(p1.length > p2.length) + return false ; + + for(uint32_t i=0;i p2.raw[i]) + return false ; + } + return false ; +} + +bool PGPHandler::mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) +{ + // First sort all signatures into lists to see which is new, which is not new + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Merging signatures for key " << PGPIdType(dst->key_id).toStdString() << std::endl; +#endif + std::set dst_packets ; + + for(uint32_t i=0;inpackets;++i) dst_packets.insert(dst->packets[i]) ; + + std::set to_add ; + + for(uint32_t i=0;inpackets;++i) + if(dst_packets.find(src->packets[i]) == dst_packets.end()) + { + uint8_t tag ; + uint32_t length ; + + PGPKeyParser::read_packetHeader(src->packets[i].raw,tag,length) ; + + if(tag == PGPKeyParser::PGP_PACKET_TAG_SIGNATURE) + to_add.insert(src->packets[i]) ; +#ifdef DEBUG_PGPHANDLER + else + std::cerr << " Packet with tag 0x" << std::hex << (int)(src->packets[i].raw[0]) << std::dec << " not merged, because it is not a signature." << std::endl; +#endif + } + + for(std::set::const_iterator it(to_add.begin());it!=to_add.end();++it) + { +#ifdef DEBUG_PGPHANDLER + std::cerr << " Adding packet with tag 0x" << std::hex << (int)(*it).raw[0] << std::dec << std::endl; +#endif + ops_add_packet_to_keydata(dst,&*it) ; + } + return to_add.size() > 0 ; +} + +bool PGPHandler::privateTrustCertificate(const PGPIdType& id,int trustlvl) +{ + if(trustlvl < 0 || trustlvl >= 6 || trustlvl == 1) + { + std::cerr << "Invalid trust level " << trustlvl << " passed to privateTrustCertificate." << std::endl; + return false ; + } + + std::map::iterator it = _public_keyring_map.find(id.toStdString()); + + if(it == _public_keyring_map.end()) + { + std::cerr << "(EE) Key id " << id.toStdString() << " not in the keyring. Can't setup trust level." << std::endl; + return false ; + } + + if( (int)it->second._trustLvl != trustlvl ) + _trustdb_changed = true ; + + it->second._trustLvl = trustlvl ; + + return true ; +} + +struct PrivateTrustPacket +{ + unsigned char user_id[KEY_ID_SIZE] ; // pgp id in unsigned char format. + uint8_t trust_level ; // trust level. From 0 to 6. + uint32_t flags ; // not used yet, but who knows? +}; + +void PGPHandler::locked_readPrivateTrustDatabase() +{ + FILE *fdb = fopen(_trustdb_path.c_str(),"rb") ; +#ifdef DEBUG_PGPHANDLER + std::cerr << "PGPHandler: Reading private trust database." << std::endl; +#endif + + if(fdb == NULL) + { + std::cerr << " private trust database not found. No trust info loaded." << std::endl ; + return ; + } + std::map::iterator it ; + PrivateTrustPacket trustpacket; + int n_packets = 0 ; + + while(fread((void*)&trustpacket,sizeof(PrivateTrustPacket),1,fdb) == 1) + { + it = _public_keyring_map.find(PGPIdType(trustpacket.user_id).toStdString()) ; + + if(it == _public_keyring_map.end()) + { + std::cerr << " (WW) Trust packet found for unknown key id " << PGPIdType(trustpacket.user_id).toStdString() << std::endl; + continue ; + } + if(trustpacket.trust_level > 6) + { + std::cerr << " (WW) Trust packet found with unexpected trust level " << trustpacket.trust_level << std::endl; + continue ; + } + + ++n_packets ; + it->second._trustLvl = trustpacket.trust_level ; + } + + fclose(fdb) ; + + std::cerr << "PGPHandler: Successfully read " << n_packets << " trust packets." << std::endl; +} + +bool PGPHandler::locked_writePrivateTrustDatabase() +{ + FILE *fdb = fopen((_trustdb_path+".tmp").c_str(),"wb") ; +#ifdef DEBUG_PGPHANDLER + std::cerr << "PGPHandler: Reading private trust database." << std::endl; +#endif + + if(fdb == NULL) + { + std::cerr << " (EE) Can't open private trust database file " << _trustdb_path << " for write. Giving up!" << std::endl ; + return false; + } + PrivateTrustPacket trustpacket ; + + for(std::map::iterator it = _public_keyring_map.begin();it!=_public_keyring_map.end() ;++it) + { + memcpy(&trustpacket.user_id,PGPIdType(it->first).toByteArray(),KEY_ID_SIZE) ; + trustpacket.trust_level = it->second._trustLvl ; + + if(fwrite((void*)&trustpacket,sizeof(PrivateTrustPacket),1,fdb) != 1) + { + std::cerr << " (EE) Cannot write to trust database " << _trustdb_path << ". Disc full, or quota exceeded ? Leaving database untouched." << std::endl; + fclose(fdb) ; + return false; + } + } + + fclose(fdb) ; + + if(!RsDirUtil::renameFile(_trustdb_path+".tmp",_trustdb_path)) + { + std::cerr << " (EE) Cannot move temp file " << _trustdb_path+".tmp" << ". Bad write permissions?" << std::endl; + return false ; + } + else + return true ; +} + +bool PGPHandler::syncDatabase() +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Sync-ing keyrings." << std::endl; +#endif + locked_syncPublicKeyring() ; + //locked_syncSecretKeyring() ; + + // Now sync the trust database as well. + // + locked_syncTrustDatabase() ; + +#ifdef DEBUG_PGPHANDLER + std::cerr << "Done. " << std::endl; +#endif + return true ; +} + +bool PGPHandler::locked_syncPublicKeyring() +{ + struct stat64 buf ; +#ifdef WINDOWS_SYS + std::wstring wfullname; + librs::util::ConvertUtf8ToUtf16(_pubring_path, wfullname); + if(-1 == _wstati64(wfullname.c_str(), &buf)) +#else + if(-1 == stat64(_pubring_path.c_str(), &buf)) +#endif + std::cerr << "PGPHandler::syncDatabase(): can't stat file " << _pubring_path << ". Can't sync public keyring." << std::endl; + + if(_pubring_last_update_time < buf.st_mtime) + { + std::cerr << "Detected change on disk of public keyring. Merging!" << std::endl ; + + mergeKeyringFromDisk(_pubring,_public_keyring_map,_pubring_path) ; + _pubring_last_update_time = buf.st_mtime ; + } + + // Now check if the pubring was locally modified, which needs saving it again + if(_pubring_changed) + { + std::cerr << "Local changes in public keyring. Writing to disk..." << std::endl; + if(!ops_write_keyring_to_file(_pubring,ops_false,_pubring_path.c_str(),ops_true)) + std::cerr << "Cannot write public keyring. Disk full? Disk quota exceeded?" << std::endl; + else + { + std::cerr << "Done." << std::endl; + _pubring_last_update_time = time(NULL) ; // should we get this value from the disk instead?? + _pubring_changed = false ; + } + } + return true ; +} + +bool PGPHandler::locked_syncTrustDatabase() +{ + struct stat64 buf ; + std::wstring wfullname; +#ifdef WINDOWS_SYS + librs::util::ConvertUtf8ToUtf16(_trustdb_path, wfullname); + if(-1 == _wstati64(wfullname.c_str(), &buf)) +#else + if(-1 == stat64(_trustdb_path.c_str(), &buf)) +#endif + { + std::cerr << "PGPHandler::syncDatabase(): can't stat file " << _trustdb_path << ". Will force write it." << std::endl; + _trustdb_changed = true ; // we force write of trust database if it does not exist. + } + + if(_trustdb_last_update_time < buf.st_mtime) + { + std::cerr << "Detected change on disk of trust database. " << std::endl ; + + locked_readPrivateTrustDatabase(); + _trustdb_last_update_time = time(NULL) ; + } + + if(_trustdb_changed) + { + std::cerr << "Local changes in trust database. Writing to disk..." << std::endl; + if(!locked_writePrivateTrustDatabase()) + std::cerr << "Cannot write trust database. Disk full? Disk quota exceeded?" << std::endl; + else + { + std::cerr << "Done." << std::endl; + _trustdb_last_update_time = time(NULL) ; + _trustdb_changed = false ; + } + } + return true ; +} +void PGPHandler::mergeKeyringFromDisk( ops_keyring_t *keyring, + std::map& kmap, + const std::string& keyring_file) +{ +#ifdef DEBUG_PGPHANDLER + std::cerr << "Merging keyring " << keyring_file << " from disk to memory." << std::endl; +#endif + + // 1 - load keyring into a temporary keyring list. + ops_keyring_t *tmp_keyring = PGPHandler::allocateOPSKeyring() ; + + if(ops_false == ops_keyring_read_from_file(tmp_keyring, false, keyring_file.c_str())) + { + std::cerr << "PGPHandler::mergeKeyringFromDisk(): cannot read keyring. File corrupted?" ; + ops_keyring_free(tmp_keyring) ; + return ; + } + + // 2 - load new keys and merge existing key signatures + + for(int i=0;inkeys;++i) + addOrMergeKey(keyring,kmap,&tmp_keyring->keys[i]) ;// we dont' account for the return value. This is disk merging, not local changes. + + // 4 - clean + ops_keyring_free(tmp_keyring) ; +} + + diff --git a/libretroshare/src/pgp/pgphandler.h b/libretroshare/src/pgp/pgphandler.h new file mode 100644 index 000000000..2b2937053 --- /dev/null +++ b/libretroshare/src/pgp/pgphandler.h @@ -0,0 +1,174 @@ +#pragma once + +// This class implements an abstract pgp handler to be used in RetroShare. +// +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +} + +static const int KEY_ID_SIZE = 8 ; +static const int KEY_FINGERPRINT_SIZE = 20 ; + +typedef std::string (*PassphraseCallback)(void *data, const char *uid_hint, const char *passphrase_info, int prev_was_bad) ; + +typedef t_RsGenericIdType PGPIdType; +typedef t_RsGenericIdType PGPFingerprintType ; + +class PGPCertificateInfo +{ + public: + PGPCertificateInfo() {} + + std::string _name; + std::string _email; + std::string _comment; + + std::set signers; + + uint32_t _trustLvl; + uint32_t _validLvl; + uint32_t _flags ; + + PGPFingerprintType _fpr; /* fingerprint */ + PGPIdType _key_id ; + + uint32_t _key_index ; // index to array of keys in the public keyring + + static const uint32_t PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION = 0x0001 ; + static const uint32_t PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE = 0x0002 ; + static const uint32_t PGP_CERTIFICATE_FLAG_HAS_SIGNED_ME = 0x0004 ; + static const uint32_t PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM = 0x0008 ; // set when the key is not RSA, so that RS avoids to use it. + + static const uint8_t PGP_CERTIFICATE_TRUST_UNDEFINED = 0x00 ; + static const uint8_t PGP_CERTIFICATE_TRUST_NEVER = 0x02 ; + static const uint8_t PGP_CERTIFICATE_TRUST_MARGINALLY = 0x03 ; + static const uint8_t PGP_CERTIFICATE_TRUST_FULLY = 0x04 ; + static const uint8_t PGP_CERTIFICATE_TRUST_ULTIMATE = 0x05 ; +}; + +class PGPHandler +{ + public: + PGPHandler( const std::string& path_to_public_keyring, + const std::string& path_to_secret_keyring, + const std::string& path_to_trust_database, + const std::string& pgp_lock_file) ; + + virtual ~PGPHandler() ; + + /** + * @param ids list of gpg certificate ids (note, not the actual certificates) + */ + bool getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&) = NULL) const ; + bool haveSecretKey(const PGPIdType& id) const ; + + bool importGPGKeyPair(const std::string& filename,PGPIdType& imported_id,std::string& import_error) ; + bool exportGPGKeyPair(const std::string& filename,const PGPIdType& exported_id) const ; + + bool availableGPGCertificatesWithPrivateKeys(std::list& ids); + bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, PGPIdType& pgpId, std::string& errString) ; + + bool LoadCertificateFromString(const std::string& pem, PGPIdType& gpg_id, std::string& error_string); + std::string SaveCertificateToString(const PGPIdType& id,bool include_signatures) ; + + bool SignDataBin(const PGPIdType& id,const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen) ; + bool VerifySignBin(const void *data, uint32_t data_len, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& withfingerprint) ; + bool privateSignCertificate(const PGPIdType& own_id,const PGPIdType& id_of_key_to_sign) ; + + bool encryptTextToFile(const PGPIdType& key_id,const std::string& text,const std::string& outfile) ; + bool decryptTextFromFile(const PGPIdType& key_id,std::string& text,const std::string& inputfile) ; + + bool getKeyFingerprint(const PGPIdType& id,PGPFingerprintType& fp) const ; + void setAcceptConnexion(const PGPIdType&,bool) ; + void updateOwnSignatureFlag(const PGPIdType& ownId) ; + + //bool isKeySupported(const PGPIdType& id) const ; + + bool privateTrustCertificate(const PGPIdType& id,int valid_level) ; + + // Write keyring + + //bool writeSecretKeyring() ; + //bool writePublicKeyring() ; + + const PGPCertificateInfo *getCertificateInfo(const PGPIdType& id) const ; + + bool isGPGId(const std::string &id); + bool isGPGSigned(const std::string &id); + bool isGPGAccepted(const std::string &id); + + static void setPassphraseCallback(PassphraseCallback cb) ; + static PassphraseCallback passphraseCallback() { return _passphrase_callback ; } + + // Debug stuff. + virtual bool printKeys() const ; + + // Syncs the keyrings and trust database between memory and disk. The algorithm is: + // 1 - lock the keyrings + // 2 - compare file modification dates with last writing date + // - if file is modified, load it, and merge with memory + // 3 - look into memory modification flags + // - if flag says keyring has changed, write to disk + // + bool syncDatabase() ; + + private: + void initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t i) ; + + // Returns true if the signatures have been updated + // + bool validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) ; + + const ops_keydata_t *getPublicKey(const PGPIdType&) const ; + const ops_keydata_t *getSecretKey(const PGPIdType&) const ; + + void locked_readPrivateTrustDatabase() ; + bool locked_writePrivateTrustDatabase() ; + + bool locked_syncPublicKeyring() ; + bool locked_syncTrustDatabase() ; + + void mergeKeyringFromDisk(ops_keyring_t *keyring, std::map& kmap, const std::string& keyring_file) ; + bool addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) ; + + // Members. + // + mutable RsMutex pgphandlerMtx ; + + ops_keyring_t *_pubring ; + ops_keyring_t *_secring ; + + std::map _public_keyring_map ; // used for fast access to keys. Gives the index in the keyring. + std::map _secret_keyring_map ; + + const std::string _pubring_path ; + const std::string _secring_path ; + const std::string _trustdb_path ; + const std::string _pgp_lock_filename ; + + bool _pubring_changed ; + bool _trustdb_changed ; + + time_t _pubring_last_update_time ; + time_t _secring_last_update_time ; + time_t _trustdb_last_update_time ; + + // Helper functions. + // + static std::string makeRadixEncodedPGPKey(const ops_keydata_t *key) ; + static ops_keyring_t *allocateOPSKeyring() ; + static void addNewKeyToOPSKeyring(ops_keyring_t*, const ops_keydata_t&) ; + static PassphraseCallback _passphrase_callback ; + static bool mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) ; // returns true if signature lists are different +}; + diff --git a/libretroshare/src/util/pgpkey.cc b/libretroshare/src/pgp/pgpkeyutil.cc similarity index 79% rename from libretroshare/src/util/pgpkey.cc rename to libretroshare/src/pgp/pgpkeyutil.cc index ea37ec8ec..42b088aa4 100644 --- a/libretroshare/src/util/pgpkey.cc +++ b/libretroshare/src/pgp/pgpkeyutil.cc @@ -1,6 +1,6 @@ #include #include -#include "pgpkey.h" +#include "pgpkeyutil.h" #include #include @@ -9,10 +9,6 @@ /* #define DEBUG_PGPUTIL 1 */ /****************************/ -#define PGP_PACKET_TAG_PUBLIC_KEY 6 -#define PGP_PACKET_TAG_USER_ID 13 -#define PGP_PACKET_TAG_SIGNATURE 2 - #define PGP_CRC24_INIT 0xB704CEL #define PGP_CRC24_POLY 0x1864CFBL @@ -85,11 +81,11 @@ bool PGPKeyManagement::createMinimalKey(const std::string& pgp_certificate,std:: data += packet_length ; - if(packet_tag == PGP_PACKET_TAG_PUBLIC_KEY) + if(packet_tag == PGPKeyParser::PGP_PACKET_TAG_PUBLIC_KEY) public_key = true ; - if(packet_tag == PGP_PACKET_TAG_USER_ID) + if(packet_tag == PGPKeyParser::PGP_PACKET_TAG_USER_ID) user_id = true ; - if(packet_tag == PGP_PACKET_TAG_SIGNATURE) + if(packet_tag == PGPKeyParser::PGP_PACKET_TAG_SIGNATURE) own_signature = true ; if(public_key && own_signature && user_id) @@ -99,28 +95,7 @@ bool PGPKeyManagement::createMinimalKey(const std::string& pgp_certificate,std:: break ; } - std::string outstring ; - Radix64::encode(keydata,(uint64_t)data - (uint64_t)keydata,outstring) ; - - uint32_t crc = compute24bitsCRC((unsigned char *)keydata,(uint64_t)data - (uint64_t)keydata) ; - - unsigned char tmp[3] = { (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff } ; - std::string crc_string ; - Radix64::encode((const char *)tmp,3,crc_string) ; - -#ifdef DEBUG_PGPUTIL - std::cerr << "After signature pruning: " << std::endl; - std::cerr << outstring << std::endl; -#endif - - cleaned_certificate = std::string(PGP_CERTIFICATE_START_STRING) + "\n" + version_string + "\n\n" ; - - for(uint32_t i=0;i> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff } ; + std::string crc_string ; + Radix64::encode((const char *)tmp,3,crc_string) ; + +#ifdef DEBUG_PGPUTIL + std::cerr << "After signature pruning: " << std::endl; + std::cerr << outstring << std::endl; +#endif + + std::string certificate = std::string(PGP_CERTIFICATE_START_STRING) + "\n" + version_string + "\n\n" ; + + for(uint32_t i=0;i -#include +#include #include /* for (u)sleep() */ #include +#include +#include #include #include "serialiser/rsconfigitems.h" #include "cleanupxpgp.h" +#define LIMIT_CERTIFICATE_SIZE 1 +#define MAX_CERTIFICATE_SIZE 10000 + const time_t STORE_KEY_TIMEOUT = 1 * 60 * 60; //store key is call around every hour +AuthGPG *AuthGPG::_instance = NULL ; + void cleanupZombies(int numkill); // function to cleanup zombies under OSX. //#define GPG_DEBUG 1 -static AuthGPG *instance_gpg = NULL; +/* Function to sign X509_REQ via GPGme. */ -void AuthGPGInit() +bool AuthGPG::decryptTextFromFile(std::string& text,const std::string& inputfile) { - instance_gpg = new AuthGPGimpl(); + return PGPHandler::decryptTextFromFile(mOwnGpgId,text,inputfile) ; } - - -void AuthGPGExit() +bool AuthGPG::encryptTextToFile(const std::string& text,const std::string& outfile) { - if (instance_gpg) { - instance_gpg->join(); - delete(instance_gpg); - instance_gpg = NULL; - } + return PGPHandler::encryptTextToFile(mOwnGpgId,text,outfile) ; } -AuthGPG *AuthGPG::getAuthGPG() +std::string pgp_pwd_callback(void * /*hook*/, const char *uid_hint, const char * /*passphrase_info*/, int prev_was_bad) { - return instance_gpg; -} - -/* Turn a set of parameters into a string */ -static std::string setKeyPairParams(bool useRsa, unsigned int blen, - std::string name, std::string comment, std::string email, - std::string inPassphrase); - -static gpg_error_t keySignCallback(void *, gpgme_status_code_t, \ - const char *, int); - -static gpg_error_t trustCallback(void *, gpgme_status_code_t, \ - const char *, int); - -static std::string ProcessPGPmeError(gpgme_error_t ERR); - -/* Function to sign X509_REQ via GPGme. - */ - -gpgcert::gpgcert() - :key(NULL), mHaveCachedCert(false) -{ - return; -} - -gpgcert::~gpgcert() -{ - if (key) - { - gpgme_key_unref(key); - } -} - #define GPG_DEBUG2 -gpg_error_t pgp_pwd_callback(void */*hook*/, const char *uid_hint, const char */*passphrase_info*/, int prev_was_bad, int fd) -{ #ifdef GPG_DEBUG2 fprintf(stderr, "pgp_pwd_callback() called.\n"); #endif std::string password; - if (rsicontrol->getNotify().askForPassword(uid_hint, prev_was_bad, password) == false) { - return GPG_ERR_CANCELED; - } + rsicontrol->getNotify().askForPassword(uid_hint, prev_was_bad, password) ; -#ifdef GPG_DEBUG2 - std::cerr << "pgp_pwd_callback() got GPG passwd from gui." << std::endl; -#endif + return password ; +} - if((void*)fd != NULL) +void AuthGPG::init(const std::string& path_to_public_keyring,const std::string& path_to_secret_keyring,const std::string& path_to_trustdb,const std::string& pgp_lock_file) +{ + if(_instance != NULL) { -#ifndef WINDOWS_SYS - write(fd, password.c_str(), password.size()); - write(fd, "\n", 1); /* needs a new line? */ -#else - DWORD written = 0; - HANDLE winFd = (HANDLE) fd; - WriteFile(winFd, password.c_str(), password.size(), &written, NULL); - WriteFile(winFd, "\n", 1, &written, NULL); -#endif + exit(); + std::cerr << "AuthGPG::init() called twice!" << std::endl ; } -#ifdef GPG_DEBUG2 - fprintf(stderr, "pgp_pwd_callback() password setted\n"); -#endif - - return 0; + PGPHandler::setPassphraseCallback(pgp_pwd_callback) ; + _instance = new AuthGPG(path_to_public_keyring,path_to_secret_keyring,path_to_trustdb,pgp_lock_file) ; } -AuthGPGimpl::AuthGPGimpl() - :p3Config(CONFIG_TYPE_AUTHGPG), gpgMtxEngine("AuthGPG-engine"), gpgMtxData("AuthGPG-data"),gpgmeInit(false),gpgmeKeySelected(false), gpgMtxService("AuthGPG-service") +void AuthGPG::exit() { - { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - CTX = NULL; - - setlocale(LC_ALL, ""); - gpgme_check_version(NULL); - gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); - - #ifdef LC_MESSAGES - gpgme_set_locale(NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); - #endif - - if (GPG_ERR_NO_ERROR != gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP)) - { - std::cerr << "Error check engine version" << std::endl; - return; - } - - if (GPG_ERR_NO_ERROR != gpgme_get_engine_info(&INFO)) - { - std::cerr << "Error getting engine info" << std::endl; - while (INFO && INFO->protocol != GPGME_PROTOCOL_OpenPGP) { - INFO = INFO->next; - } - if (!INFO) { - fprintf (stderr, "GPGME compiled without support for protocol %s", - gpgme_get_protocol_name (INFO->protocol)); - } else if (INFO->file_name && !INFO->version) { - fprintf (stderr, "Engine %s not installed properly", - INFO->file_name); - } else if (INFO->file_name && INFO->version && INFO->req_version) { - fprintf (stderr, "Engine %s version %s installed, " - "but at least version %s required", INFO->file_name, - INFO->version, INFO->req_version); - } else { - fprintf (stderr, "Unknown problem with engine for protocol %s", - gpgme_get_protocol_name (INFO->protocol)); - } - return; - } - - /* Create New Contexts */ - if (GPG_ERR_NO_ERROR != gpgme_new(&CTX)) - { - std::cerr << "Error creating GPGME Context" << std::endl; - return; - } - - gpgme_set_passphrase_cb(CTX, pgp_pwd_callback, (void *) NULL); - } + if(_instance != NULL) + { + _instance->join(); + delete _instance ; + _instance = NULL; + } } -/* Initialize */ -bool AuthGPGimpl::InitAuth () +AuthGPG::AuthGPG(const std::string& path_to_public_keyring,const std::string& path_to_secret_keyring,const std::string& path_to_trustdb,const std::string& pgp_lock_file) + :p3Config(CONFIG_TYPE_AUTHGPG), + PGPHandler(path_to_public_keyring,path_to_secret_keyring,path_to_trustdb,pgp_lock_file), + gpgMtxService("AuthGPG-service"), + gpgMtxEngine("AuthGPG-engine"), + gpgMtxData("AuthGPG-data"), + gpgKeySelected(false) { - std::string HomeDir; - - if (!CTX) { - std::cerr << "Error with gpg initialization. Is gpg missing ?" << std::endl; - return false; - } - -#ifdef WINDOWS_SYS - if (RsInit::isPortable ()) { - // set home dir of gpg to configdir\gnupg - HomeDir = RsInit::RsConfigDirectory() + "/" + "gnupg"; - - if (!RsDirUtil::checkCreateDirectory(HomeDir)) { - std::cerr << "Error creating gnupg directory" << std::endl; - return false; - } - } -#endif - - /* setup protocol and homedir */ - if (GPG_ERR_NO_ERROR != gpgme_ctx_set_engine_info(CTX, GPGME_PROTOCOL_OpenPGP, NULL, HomeDir.empty () ? NULL : HomeDir.c_str ())) - { - std::cerr << "Error creating Setting Protocol" << std::endl; - return false; - } - - gpgmeInit = true; - - storeAllKeys(); - #ifdef GPG_DEBUG - printAllKeys_locked(); - #endif - //updateTrustAllKeys_locked(); - - /* start thread */ - start(); - - return true; + _force_sync_database = false ; + start(); } /* This function is called when retroshare is first started @@ -240,60 +119,17 @@ bool AuthGPGimpl::InitAuth () * * returns false if GnuPG is not available. */ -bool AuthGPGimpl::availableGPGCertificatesWithPrivateKeys(std::list &ids) +bool AuthGPG::availableGPGCertificatesWithPrivateKeys(std::list &ids) { - int i = 0; - gpgme_key_t KEY = NULL; - gpg_error_t ERR; + std::list pids ; - { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + PGPHandler::availableGPGCertificatesWithPrivateKeys(pids) ; - if (!gpgmeInit) - { - return false; - } - } /******* UNLOCKED ******/ - - /* XXX should check that CTX is valid */ - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - /* Initiates a key listing */ - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_start (CTX, "", 1)) - { - std::cerr << "Error iterating through KeyList" << std::endl; - return false; - - } - - /* Loop until end of key */ - for(i = 0;(GPG_ERR_NO_ERROR == (ERR = gpgme_op_keylist_next (CTX, &KEY))); i++) - { - if (KEY->subkeys) - { - ids.push_back(KEY->subkeys->keyid); -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::availablePGPCertificates() Added: " << KEY->subkeys->keyid << std::endl; -#endif - } - else - { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::availablePGPCertificates() Missing subkey" << std::endl; -#endif - } - } - - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_end(CTX)) - { - std::cerr << "Error ending KeyList" << std::endl; - return false; - } - - std::cerr << "AuthGPGimpl::availablePGPCertificates() Secret Key Count: " << i << std::endl; + for(std::list::const_iterator it(pids.begin());it!=pids.end();++it) + ids.push_back( (*it).toStdString() ) ; /* return false if there are no private keys */ - return (i > 0); + return !ids.empty(); } /* You can initialise Retroshare with @@ -303,67 +139,26 @@ bool AuthGPGimpl::availableGPGCertificatesWithPrivateKeys(std::list * This function must be called successfully (return == 1) * before anything else can be done. (except above fn). */ -int AuthGPGimpl::GPGInit(const std::string &ownId) +int AuthGPG::GPGInit(const std::string &ownId) { + std::cerr << "AuthGPG::GPGInit() called with own gpg id : " << ownId << std::endl; - std::cerr << "AuthGPGimpl::GPGInit() called with own gpg id : " << ownId << std::endl; + mOwnGpgId = PGPIdType(ownId); - { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + //force the validity of the private key. When set to unknown, it caused signature and text encryptions bugs + privateTrustCertificate(ownId, 5); + updateOwnSignatureFlag(mOwnGpgId) ; - if (!gpgmeInit) { - return 0; - } + std::cerr << "AuthGPG::GPGInit finished." << std::endl; - mOwnGpgId = ownId; - - // clear old cert - gpgme_key_unref(mOwnGpgCert.key); - mOwnGpgCert = gpgcert(); - - // search own key - certmap::iterator it = mKeyList.find(mOwnGpgId); - if (it != mKeyList.end()) { - it->second.ownsign = true; - - // store own key, grab a reference, so the key remains - gpgme_key_ref(it->second.key); - - mOwnGpgCert = it->second; - } - } -// already stored in AuthGPGimpl::InitAuth -// storeAllKeys(); - - int lvl = 0; - - { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - if (mOwnGpgCert.id != mOwnGpgId) { - std::cerr << "AuthGPGimpl::GPGInit() failed to find your id." << std::endl; - return 0; - } - - gpgmeKeySelected = true; - - lvl = mOwnGpgCert.validLvl; - } - - //force the validity of the private key. When set to unknown, it caused signature and text encryptions bugs - privateTrustCertificate(mOwnGpgId, 5); - - //printAllKeys_locked(); - - std::cerr << "AuthGPGimpl::GPGInit finished." << std::endl; - - return 1; + return 1; } - AuthGPGimpl::~AuthGPGimpl() + AuthGPG::~AuthGPG() { } -void AuthGPGimpl::run() +void AuthGPG::run() { int count = 0; @@ -378,15 +173,24 @@ void AuthGPGimpl::run() /* every 100 milliseconds */ processServices(); - /* every minute */ - if (++count >= 600) { - storeAllKeys_tick(); - count = 0; + /* every ten seconds */ + if (++count >= 100 || _force_sync_database) + { + RsStackMutex stack(gpgMtxService); /******* LOCKED ******/ + + // The call does multiple things at once: + // - checks whether the keyring has changed in memory + // - checks whether the keyring has changed on disk. + // - merges/updates according to status. + // + PGPHandler::syncDatabase() ; + count = 0; + _force_sync_database = false ; } } } -void AuthGPGimpl::processServices() +void AuthGPG::processServices() { AuthGPGOperation *operation = NULL; AuthGPGService *service = NULL; @@ -416,98 +220,61 @@ void AuthGPGimpl::processServices() } AuthGPGOperationLoadOrSave *loadOrSave = dynamic_cast(operation); - if (loadOrSave) { - if (loadOrSave->m_load) { - /* process load operation */ + if (loadOrSave) + { + if (loadOrSave->m_load) + { + /* process load operation */ - /* load the certificate */ + /* load the certificate */ - /* don't bother loading - if we already have the certificate */ - if (isGPGId(loadOrSave->m_certGpgId)) - { + /* don't bother loading - if we already have the certificate */ + if (isGPGId(loadOrSave->m_certGpgId)) + { #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::processServices() Skipping load - already have it" << std::endl; + std::cerr << "AuthGPGimpl::processServices() Skipping load - already have it" << std::endl; #endif - } - else - { + } + else + { #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::processServices() Process load operation" << std::endl; + std::cerr << "AuthGPGimpl::processServices() Process load operation" << std::endl; #endif - std::string error_string ; - LoadCertificateFromString(loadOrSave->m_certGpg, loadOrSave->m_certGpgId,error_string); - } + std::string error_string ; + LoadCertificateFromString(loadOrSave->m_certGpg, loadOrSave->m_certGpgId,error_string); + } - } else { - /* process save operation */ + } else { + /* process save operation */ #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::processServices() Process save operation" << std::endl; + std::cerr << "AuthGPGimpl::processServices() Process save operation" << std::endl; #endif - /* save the certificate to string */ -/***** - * #define DISABLE_CERTIFICATE_SEND 1 - ****/ + /* save the certificate to string */ + /***** + * #define DISABLE_CERTIFICATE_SEND 1 + ****/ -#define LIMIT_CERTIFICATE_SIZE 1 -#define MAX_CERTIFICATE_SIZE 10000 - - if (!getCachedGPGCertificate(loadOrSave->m_certGpgId, loadOrSave->m_certGpg)) - { - - -#ifdef DISABLE_CERTIFICATE_SEND - std::cerr << "AuthGPGimpl::processServices() Certificates Disabled" << std::endl; - loadOrSave->m_certGpg = ""; -#else - loadOrSave->m_certGpg = SaveCertificateToString(loadOrSave->m_certGpgId,true); - std::cerr << "AuthGPGimpl::processServices() Cert for: " << loadOrSave->m_certGpgId; - std::cerr << " is " << loadOrSave->m_certGpg.size() << " bytes"; - std::cerr << std::endl; - - #ifdef LIMIT_CERTIFICATE_SIZE - if (loadOrSave->m_certGpg.size() > MAX_CERTIFICATE_SIZE) - { - std::cerr << "AuthGPGimpl::processServices() Cert for: " << loadOrSave->m_certGpgId; - std::cerr << " is over size limit - switching to a minimal certificate"; - std::cerr << std::endl; - - std::string cleaned_key ; - if(PGPKeyManagement::createMinimalKey(loadOrSave->m_certGpg,cleaned_key)) - { - loadOrSave->m_certGpg = cleaned_key; - std::cerr << "AuthGPGimpl::processServices() Minimal Cert Generation, size"; - std::cerr << " is " << loadOrSave->m_certGpg.size() << " bytes"; - std::cerr << std::endl; - } - else - { - std::cerr << "AuthGPGimpl::processServices() Minimal Cert Generation Failed! removing cert"; - std::cerr << std::endl; - loadOrSave->m_certGpg = ""; - } - } - #endif - cacheGPGCertificate(loadOrSave->m_certGpgId, loadOrSave->m_certGpg); - } -#endif + loadOrSave->m_certGpg = SaveCertificateToString(loadOrSave->m_certGpgId,true); #ifdef GPG_DEBUG - std::cerr << "Certificate for: " << loadOrSave->m_certGpgId << " is: "; - std::cerr << std::endl; - std::cerr << loadOrSave->m_certGpg; - std::cerr << std::endl; + std::cerr << "Certificate for: " << loadOrSave->m_certGpgId << " is: "; + std::cerr << std::endl; + std::cerr << loadOrSave->m_certGpg; + std::cerr << std::endl; #endif - } + } - service->setGPGOperation(loadOrSave); - } else { + service->setGPGOperation(loadOrSave); + } + else + { #ifdef GPG_DEBUG std::cerr << "AuthGPGimpl::processServices() Unknown operation" << std::endl; #endif @@ -516,960 +283,226 @@ void AuthGPGimpl::processServices() delete operation; } -bool AuthGPGimpl::storeAllKeys_tick() { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::storeAllKeys_tick() called." << std::endl; -#endif - time_t timeSinceStore = 0; - { - RsStackMutex stack(gpgMtxData); - timeSinceStore = time(NULL) - mStoreKeyTime; - } - - if (timeSinceStore > STORE_KEY_TIMEOUT) - { - storeAllKeys(); - } - return true ; -} - -// store all keys in map mKeyList to avoid callin gpgme exe repeatedly -bool AuthGPGimpl::storeAllKeys() +bool AuthGPG::DoOwnSignature(const void *data, unsigned int datalen, void *buf_sigout, unsigned int *outl) { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::storeAllKeys()" << std::endl; -#endif - - std::string ownGpgId; - - /* store member variables locally */ - { - RsStackMutex stack(gpgMtxData); - - if (!gpgmeInit) - { - std::cerr << "AuthGPGimpl::storeAllKeys() Error since GPG is not initialised" << std::endl; - return false; - } - - mStoreKeyTime = time(NULL); - ownGpgId = mOwnGpgId; - } - - /* read keys from gpg to local list */ - std::list keyList; - - { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::storeAllKeys() clearing existing ones" << std::endl; -#endif - - gpg_error_t ERR; - - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - /* enable SIG mode */ - gpgme_keylist_mode_t origmode = gpgme_get_keylist_mode(CTX); - gpgme_keylist_mode_t mode = origmode | GPGME_KEYLIST_MODE_SIGS; - - gpgme_set_keylist_mode(CTX, mode); - - /* store keys */ - gpgme_key_t KEY = NULL; - - /* Initiates a key listing 0 = All Keys */ - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_start (CTX, "", 0)) - { - std::cerr << "AuthGPGimpl::storeAllKeys() Error iterating through KeyList" << std::endl; - // if (rsicontrol != NULL) { - // rsicontrol->getNotify().notifyErrorMsg(0,0,"Error reading gpg keyring, cannot acess key list."); - // } - gpgme_set_keylist_mode(CTX, origmode); - return false; - } - - /* Loop until end of key */ - ERR = gpgme_op_keylist_next (CTX, &KEY); - if (GPG_ERR_NO_ERROR != ERR) { - std::cerr << "AuthGPGimpl::storeAllKeys() didn't find any gpg key in the keyring" << std::endl; - // if (rsicontrol != NULL) { - // rsicontrol->getNotify().notifyErrorMsg(0,0,"Error reading gpg keyring, cannot find any key in the list."); - // } - gpgme_set_keylist_mode(CTX, origmode); - return false; - } - - for(int i = 0;GPG_ERR_NO_ERROR == ERR; i++) - { - /* store in pqiAuthDetails */ - gpgcert nu; - - /* NB subkeys is a linked list and can contain multiple keys. - * first key is primary. - */ - - if ((!KEY->subkeys) || (!KEY->uids)) - { - std::cerr << "AuthGPGimpl::storeAllKeys() Invalid Key in List... skipping" << std::endl; - continue; - } - - /* In general MainSubKey is used to sign all others! - * Don't really need to worry about other ids either. - */ - gpgme_subkey_t mainsubkey = KEY->subkeys; - nu.id = mainsubkey->keyid; - nu.fpr = mainsubkey->fpr; - -#ifdef GPG_DEBUG - std::cerr << "MAIN KEYID: " << nu.id << " FPR: " << nu.fpr << std::endl; - - gpgme_subkey_t subkeylist = KEY->subkeys; - while(subkeylist != NULL) - { - std::cerr << "\tKEYID: " << subkeylist->keyid << " FPR: " << subkeylist->fpr << std::endl; - - subkeylist = subkeylist->next; - } -#endif - - - /* NB uids is a linked list and can contain multiple ids. - * first id is primary. - */ - gpgme_user_id_t mainuid = KEY->uids; - nu.name = mainuid->name; - nu.email = mainuid->email; - gpgme_key_sig_t mainsiglist = mainuid->signatures; - - nu.ownsign = false; - while(mainsiglist != NULL) - { - if (mainsiglist->status == GPG_ERR_NO_ERROR) - { - /* add as a signature ... even if the - * we haven't go the peer yet. - * (might be yet to come). - */ - std::string keyid = mainsiglist->keyid; - if (nu.signers.end() == std::find( - nu.signers.begin(), - nu.signers.end(),keyid)) - { - nu.signers.push_back(keyid); - } - if (keyid == ownGpgId) { - nu.ownsign = true; - } - } - mainsiglist = mainsiglist->next; - } - -#ifdef GPG_DEBUG - gpgme_user_id_t uidlist = KEY->uids; - while(uidlist != NULL) - { - std::cerr << "\tUID: " << uidlist->uid; - std::cerr << " NAME: " << uidlist->name; - std::cerr << " EMAIL: " << uidlist->email; - std::cerr << " VALIDITY: " << uidlist->validity; - std::cerr << std::endl; - gpgme_key_sig_t usiglist = uidlist->signatures; - while(usiglist != NULL) - { - std::cerr << "\t\tSIG KEYID: " << usiglist->keyid; - std::cerr << " UID: " << usiglist->uid; - std::cerr << " NAME: " << usiglist->name; - std::cerr << " EMAIL: " << usiglist->email; - std::cerr << " VALIDITY: " << (usiglist->status == GPG_ERR_NO_ERROR); - std::cerr << std::endl; - - usiglist = usiglist->next; - } - - uidlist = uidlist->next; - } -#endif - - /* signatures are attached to uids... but only supplied - * if GPGME_KEYLIST_MODE_SIGS is on. - * signature notation supplied is GPGME_KEYLIST_MODE_SIG_NOTATION is on - */ - nu.trustLvl = KEY->owner_trust; - nu.validLvl = mainuid->validity; - - /* grab a reference, so the key remains */ - gpgme_key_ref(KEY); - nu.key = KEY; - - /* store in map */ - keyList.push_back(nu); -#ifdef GPG_DEBUG - std::cerr << "nu.name" << nu.name << std::endl; - std::cerr << "nu.trustLvl" << nu.trustLvl << std::endl; - std::cerr << "nu.accept_connection" << nu.accept_connection << std::endl; -#endif - - ERR = gpgme_op_keylist_next (CTX, &KEY); - } - - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_end(CTX)) - { - std::cerr << "Error ending KeyList" << std::endl; - gpgme_set_keylist_mode(CTX, origmode); - return false; - } - - gpgme_set_keylist_mode(CTX, origmode); - } - - /* process read gpg keys and store it in member */ - std::list gpg_change_trust_list; - - { - RsStackMutex stack(gpgMtxData); - - //let's start a new list - mKeyList.clear(); - - for (std::list::iterator it = keyList.begin(); it != keyList.end(); it++) { - gpgcert &nu = *it; - - std::map::iterator itAccept; - if (mAcceptToConnectMap.end() != (itAccept = mAcceptToConnectMap.find(nu.id))) { - nu.accept_connection = itAccept->second; - } else { - nu.accept_connection = false; - mAcceptToConnectMap[nu.id] = false; - } - - if (nu.trustLvl < 2 && nu.accept_connection) { - //add it to the list of key that we will force the trust to 2 - gpg_change_trust_list.push_back(nu.id); - } - - /* grab a reference, so the key remains */ - gpgme_key_ref(nu.key); - - mKeyList[nu.id] = nu; - - //store own key - if (nu.id == mOwnGpgId) { - /* grab a reference, so the key remains */ - gpgme_key_ref(nu.key); - - gpgme_key_unref(mOwnGpgCert.key); - mOwnGpgCert = nu; - } - } - } - - std::list::iterator it; - for(it = gpg_change_trust_list.begin(); it != gpg_change_trust_list.end(); it++) - { - privateTrustCertificate(*it, 3); - } - - return true; - -} - -// update trust on all available keys. Not used anymore -//bool AuthGPGimpl::updateTrustAllKeys_locked() -//{ -// gpg_error_t ERR; -// if (!gpgmeInit) -// { -// std::cerr << "Error since GPG is not initialised" << std::endl; -// return false; -// } -// -// -// /* have to do this the hard way! */ -// std::map::iterator it; -// -// for(it = mKeyList.begin(); it != mKeyList.end(); it++) -// { -// /* check for trust items associated with key */ -// std::string peerid = it->second.email; -//#ifdef GPG_DEBUG -// std::cerr << "Searching GPGme for TrustInfo on: " << peerid << std::endl; -//#endif -// -// /* Initiates a key listing. NB: maxlevel is ignored!*/ -// if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_trustlist_start (CTX, peerid.c_str(), 0))) -// { -// std::cerr << "Error Starting Trust List" << std::endl; -// ProcessPGPmeError(ERR); -// continue; -// } -// -// -// /* Loop until end of key */ -//#ifdef GPG_DEBUG -// gpgme_trust_item_t ti = NULL; -// -// for(int i = 0;(GPG_ERR_NO_ERROR == (ERR = gpgme_op_trustlist_next (CTX, &ti))); i++) -// { -// std::string keyid = ti->keyid; -// int type = ti->type; -// int level = ti->level; -// -// /* identify the peers, and add trust level */ -// std::cerr << "GPGme Trust Item for: " << keyid; -// std::cerr << std::endl; -// -// std::cerr << "\t Type: " << type; -// std::cerr << " Level: " << level; -// std::cerr << std::endl; -// -// std::cerr << "\t Owner Trust: " << ti->owner_trust; -// std::cerr << " Validity: " << ti->validity; -// std::cerr << " Name: " << ti->name; -// std::cerr << std::endl; -// } -// std::cerr << "End of TrustList Iteration." << std::endl; -//#endif -// ProcessPGPmeError(ERR); -// -// if (GPG_ERR_NO_ERROR != gpgme_op_trustlist_end(CTX)) -// { -// std::cerr << "Error ending TrustList" << std::endl; -// -// ProcessPGPmeError(ERR); -// } -// } -// -// return true; -// -//} - -bool AuthGPGimpl::printAllKeys_locked() -{ - certmap::const_iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - std::cerr << "PGP Key: " << it->second.id << std::endl; - - std::cerr << "\tName: " << it->second.name << std::endl; - std::cerr << "\tEmail: " << it->second.email << std::endl; - - std::cerr << "\townsign: " << it->second.ownsign << std::endl; - std::cerr << "\ttrustLvl: " << it->second.trustLvl << std::endl; - std::cerr << "\tvalidLvl: " << it->second.validLvl << std::endl; - std::cerr << "\tEmail: " << it->second.email << std::endl; - - std::list::const_iterator sit; - for(sit = it->second.signers.begin(); - sit != it->second.signers.end(); sit++) - { - std::cerr << "\t\tSigner ID:" << *sit; - - /* do a naughty second search.. should be ok - * as we aren't modifying list. - */ - certmap::const_iterator kit = mKeyList.find(*sit); - if (kit != mKeyList.end()) - { - std::cerr << " Name:" << kit->second.name << std::endl; - } - } - } - return true; -} - -bool AuthGPGimpl::printOwnKeys_locked() -{ - certmap::iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - if (it->second.ownsign) - { - std::cerr << "Own PGP Key: " << it->second.id << std::endl; - - std::cerr << "\tName: " << it->second.name << std::endl; - std::cerr << "\tEmail: " << it->second.email << std::endl; - } - } - return true; -} - -bool AuthGPGimpl::printKeys() -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - printAllKeys_locked(); - return printOwnKeys_locked(); -} - -std::string ProcessPGPmeError(gpgme_error_t ERR) -{ - gpgme_err_code_t code = gpgme_err_code(ERR); - gpgme_err_source_t src = gpgme_err_source(ERR); - - std::string s ; - - if(code > 0) - { - rs_sprintf(s, "GPGme ERROR: Code: %d Source: %d\n", code, src); - rs_sprintf_append(s, "GPGme ERROR: %s\n", gpgme_strerror(ERR)); - } - else - return std::string("Unknown error") ; - - return s ; -} - -void print_pgpme_verify_summary(unsigned int summary) -{ - std::cerr << "\tFLAGS:"; - if (summary & GPGME_SIGSUM_VALID) - std::cerr << " VALID "; - if (summary & GPGME_SIGSUM_GREEN) - std::cerr << " GREEN "; - if (summary & GPGME_SIGSUM_RED) - std::cerr << " RED "; - if (summary & GPGME_SIGSUM_KEY_REVOKED) - std::cerr << " KEY_REVOKED "; - if (summary & GPGME_SIGSUM_KEY_EXPIRED) - std::cerr << " KEY_EXPIRED "; - if (summary & GPGME_SIGSUM_SIG_EXPIRED) - std::cerr << " SIG_EXPIRED "; - if (summary & GPGME_SIGSUM_KEY_MISSING) - std::cerr << " KEY_MISSING "; - if (summary & GPGME_SIGSUM_CRL_MISSING) - std::cerr << " CRL_MISSING "; - if (summary & GPGME_SIGSUM_CRL_TOO_OLD) - std::cerr << " CRL_TOO_OLD "; - if (summary & GPGME_SIGSUM_BAD_POLICY) - std::cerr << " BAD_POLICY "; - if (summary & GPGME_SIGSUM_SYS_ERROR) - std::cerr << " SYS_ERROR "; - std::cerr << std::endl; -} - - -bool AuthGPGimpl::DoOwnSignature(const void *data, unsigned int datalen, void *buf_sigout, unsigned int *outl) -{ - gpgcert ownGpgCert; - { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - - /* grab a reference, so the key remains */ - gpgme_key_ref(mOwnGpgCert.key); - - ownGpgCert = mOwnGpgCert; - } /******* UNLOCKED ******/ - - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - /* setup signers */ - gpgme_signers_clear(CTX); - if (GPG_ERR_NO_ERROR != gpgme_signers_add(CTX, ownGpgCert.key)) - { - std::cerr << "AuthGPGimpl::DoOwnSignature() Error Adding Signer" << std::endl; - } - - gpgme_data_t gpgmeData; - gpgme_data_t gpgmeSig; - if (GPG_ERR_NO_ERROR != gpgme_data_new_from_mem(&gpgmeData, (const char *) data, datalen, 1)) - { - std::cerr << "Error create Data" << std::endl; - } - - if (GPG_ERR_NO_ERROR != gpgme_data_new(&gpgmeSig)) - { - std::cerr << "Error create Sig" << std::endl; - } - - cleanupZombies(2); // cleanup zombies under OSX. (Called before gpgme operation) - - /* move string data to gpgmeData */ - gpgme_set_armor (CTX, 0); - - gpgme_sig_mode_t mode = GPGME_SIG_MODE_DETACH; - gpg_error_t ERR; - if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_sign(CTX,gpgmeData, gpgmeSig, mode))) - { - std::cerr << "AuthGPGimpl::Sign FAILED ERR: " << ERR << std::endl; - std::cerr << ProcessPGPmeError(ERR) << std::endl; - - gpgme_data_release(gpgmeSig); - gpgme_data_release(gpgmeData); - gpgme_signers_clear(CTX); - return false; - } - - gpgme_sign_result_t res = gpgme_op_sign_result(CTX); - - if (res) - { -#ifdef GPG_DEBUG - fprintf(stderr, "Sign Got Result\n"); -#endif - } - else - { - fprintf(stderr, "Sign Failed to get Result\n"); - } - -#ifdef GPG_DEBUG - gpgme_invalid_key_t ik = res->invalid_signers; - gpgme_new_signature_t sg = res->signatures; - - while(ik != NULL) - { - fprintf(stderr, "AuthGPGimpl::Sign, Invalid by: %s\n", ik->fpr); - ik = ik->next; - } - - while(sg != NULL) - { - fprintf(stderr, "AuthGPGimpl::Signed by: %s\n", sg->fpr); - sg = sg->next; - } -#endif - - /* now extract the data from gpgmeSig */ - size_t len = 0; -// gpgme_data_write (gpgmeSig, "", 1); // to be able to convert it into a string - char *export_sig = gpgme_data_release_and_get_mem(gpgmeSig, &len); -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::Signature len: " << len << std::endl; -#endif - - if (len < *outl) // -1 because we added a 0 at the end. - { - std::cerr << "Length is smaller. Reducing to " << len << std::endl; - *outl = len; - } - memmove(buf_sigout, export_sig, *outl); - gpgme_free(export_sig); - gpgme_data_release (gpgmeData); - gpgme_signers_clear(CTX); - - /* extract id(s)! */ - return true; + return PGPHandler::SignDataBin(mOwnGpgId,data,datalen,(unsigned char *)buf_sigout,outl) ; } /* import to GnuPG and other Certificates */ -bool AuthGPGimpl::VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const std::string &withfingerprint) +bool AuthGPG::VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const std::string &withfingerprint) { - gpgme_data_t gpgmeSig; - gpgme_data_t gpgmeData; - -#ifdef GPG_DEBUG - std::cerr << "VerifySignature: datalen: " << datalen << " siglen: " << siglen << std::endl; -#endif - - if (!active()) + if(withfingerprint.length() != 40) { + std::cerr << "AuthGPG::VerifySignature(): no (or dammaged) fingerprint. Nor verifying signature. This is likely to be an unknown peer. fingerprint=\"" << withfingerprint << "\"." << std::endl; return false ; } + return PGPHandler::VerifySignBin((unsigned char*)data,datalen,(unsigned char*)sig,siglen,PGPFingerprintType(withfingerprint)) ; +} + +bool AuthGPG::exportProfile(const std::string& fname,const std::string& exported_id) +{ + return PGPHandler::exportGPGKeyPair(fname,PGPIdType(exported_id)) ; +} + +bool AuthGPG::importProfile(const std::string& fname,std::string& imported_id,std::string& import_error) +{ + PGPIdType id ; + + if(PGPHandler::importGPGKeyPair(fname,id,import_error)) + { + imported_id = id.toStdString() ; + return true ; + } + else + return false ; +} + + +bool AuthGPG::active() +{ + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + + return gpgKeySelected; +} + +bool AuthGPG::GeneratePGPCertificate(const std::string& name, + const std::string& email, const std::string& passwd, std::string &pgpId, std::string& errString) +{ RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ + + PGPIdType id ; - if (GPG_ERR_NO_ERROR != gpgme_data_new_from_mem(&gpgmeData, (const char *) data, datalen, 1)) + bool res = PGPHandler::GeneratePGPCertificate(name, email, passwd, id, errString) ; + + pgpId = id.toStdString() ; + return res ; +} + +/**** These Two are common */ +std::string AuthGPG::getGPGName(const std::string &id,bool *success) +{ + if(id.length() != 16) { - std::cerr << "Error create Data" << std::endl; + std::cerr << "Wrong string passed to getGPGDetails: \"" << id << "\"" << std::endl; + return std::string() ; } + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - if (siglen != 0 && GPG_ERR_NO_ERROR != gpgme_data_new_from_mem(&gpgmeSig, (const char *) sig, siglen, 1)) + const PGPCertificateInfo *info = PGPHandler::getCertificateInfo(PGPIdType(id)) ; + + if(info != NULL) { - std::cerr << "Error create Sig" << std::endl; - } - - cleanupZombies(2); // cleanup zombies under OSX. (Called before gpgme operation) - - gpgme_set_armor (CTX, 0); - - gpgme_error_t ERR; - if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_verify(CTX,gpgmeSig, gpgmeData, NULL))) - { - std::cerr << "AuthGPGimpl::Verify FAILED" << std::endl; - std::cerr << ProcessPGPmeError(ERR) << std::endl; - } - - gpgme_verify_result_t res = gpgme_op_verify_result(CTX); - - if (res) - { -#ifdef GPG_DEBUG - fprintf(stderr, "VerifySignature Got Result\n"); -#endif + if(success != NULL) *success = true ; + return info->_name ; } else { -#ifdef GPG_DEBUG - fprintf(stderr, "VerifySignature Failed to get Result\n"); -#endif - gpgme_data_release(gpgmeData); - gpgme_data_release(gpgmeSig); - return false ; + if(success != NULL) *success = false ; + return "[Unknown PGP Cert name]" ; } - - gpgme_signature_t sg = res->signatures; - bool valid = false; - - while(sg != NULL) - { -#ifdef GPG_DEBUG - fprintf(stderr, "AuthGPGimpl::Verify Sig by: %s, status: %d\n", sg->fpr, sg->status); - print_pgpme_verify_summary(sg->summary); -#endif - - if (sg->status == GPG_ERR_NO_ERROR) - { -#ifdef GPG_DEBUG - fprintf(stderr, "AuthGPGimpl::VerifySignature() OK\n"); -#endif - if (withfingerprint.empty() == false && withfingerprint == sg->fpr) { -#ifdef GPG_DEBUG - fprintf(stderr, "AuthGPGimpl::VerifySignature() for the fingerprint key : "); - std::cerr << withfingerprint; - fprintf(stderr, "\n"); -#endif - valid = true; - break; - } - } - - sg = sg->next; - } - - gpgme_data_release (gpgmeData); - gpgme_data_release (gpgmeSig); - - /* extract id(s)! */ - if (!valid) - { - fprintf(stderr, "AuthGPGimpl::VerifySignature() FAILED\n"); - } - - return valid; -} - - - - -bool AuthGPGimpl::active() -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - - return ((gpgmeInit) && (gpgmeKeySelected)); -} - -bool AuthGPGimpl::GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &/*errString*/) -{ - { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - gpgme_key_t newKey; - gpgme_genkey_result_t result; - gpg_error_t ERR; - - cleanupZombies(2); // cleanup zombies under OSX. (Called before gpgme operation) - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_op_genkey(CTX, setKeyPairParams(true, 2048, name, "generated by Retroshare", email, \ - passwd).c_str(), NULL, NULL))) { - std::cerr << "Error generating the key" << std::endl; - std::cerr << ProcessPGPmeError(ERR) << std::endl; - return 0; - } - - if((result = gpgme_op_genkey_result(CTX)) == NULL) - return 0; - - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_get_key(CTX, result->fpr, &newKey, 1))) { - std::cerr << "Error reading own key" << std::endl; - return 0; - } - pgpId = newKey->subkeys->keyid; - } - - // reload keys - so yours is now there. - storeAllKeys(); - return true; -} - -bool AuthGPGimpl::CloseAuth() -{ - return true; } /**** These Two are common */ -std::string AuthGPGimpl::getGPGName(const std::string &id) +std::string AuthGPG::getGPGEmail(const std::string &id,bool *success) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + const PGPCertificateInfo *info = PGPHandler::getCertificateInfo(PGPIdType(id)) ; - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - return it->second.name; - - return std::string(); -} - -/**** These Two are common */ -std::string AuthGPGimpl::getGPGEmail(const std::string &id) -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - return it->second.email; - - return std::string(); + if(info != NULL) + { + if(success != NULL) *success = true ; + return info->_email ; + } + else + { + if(success != NULL) *success = false ; + return "[Unknown PGP Cert email]" ; + } } /**** GPG versions ***/ -std::string AuthGPGimpl::getGPGOwnId() +std::string AuthGPG::getGPGOwnId() { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - return mOwnGpgId; + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + return mOwnGpgId.toStdString(); } -std::string AuthGPGimpl::getGPGOwnName() +std::string AuthGPG::getGPGOwnName() { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - return mOwnGpgCert.name; + return getGPGName(mOwnGpgId.toStdString()) ; } -bool AuthGPGimpl::getGPGAllList(std::list &ids) +bool AuthGPG::getGPGAllList(std::list &ids) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + + std::list list ; + PGPHandler::getGPGFilteredList(list) ; + + for(std::list::const_iterator it(list.begin());it!=list.end();++it) + ids.push_back( (*it).toStdString() ) ; - /* add an id for each pgp certificate */ - certmap::iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - ids.push_back(it->first); - } return true; } - -bool AuthGPGimpl::getGPGDetails(const std::string &id, RsPeerDetails &d) +bool AuthGPG::haveSecretKey(const std::string& id) const { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::getPGPDetails() called for : " << id << std::endl; -#endif + return PGPHandler::haveSecretKey(PGPIdType(id)) ; +} +bool AuthGPG::isKeySupported(const std::string& id) const +{ + const PGPCertificateInfo *pc = PGPHandler::getCertificateInfo(PGPIdType(id)) ; - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + if(pc == NULL) + return false ; - /* add an id for each pgp certificate */ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - { - gpgcert &cert = it->second; - - d.id = cert.id; //keep, it but can be bug gen - d.gpg_id = cert.id; - d.name = cert.name; - d.email = cert.email; - d.trustLvl = cert.trustLvl; - d.validLvl = cert.validLvl; - d.ownsign = cert.ownsign; - d.gpgSigners = cert.signers; - d.fpr = cert.fpr; - - d.accept_connection = cert.accept_connection; - - //did the peer signed me ? - d.hasSignedMe = false; - std::list::iterator signersIt; - for(signersIt = mOwnGpgCert.signers.begin(); signersIt != mOwnGpgCert.signers.end() ; ++signersIt) - if (*signersIt == d.id) - { - d.hasSignedMe = true; - break; - } - -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::getPGPDetails() Name : " << cert.name << std::endl; -#endif - return true; - } - return false; + return !(pc->_flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM) ; } -bool AuthGPGimpl::decryptText(gpgme_data_t CIPHER, gpgme_data_t PLAIN) +bool AuthGPG::getGPGDetails(const std::string& id, RsPeerDetails &d) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - gpgme_set_armor (CTX, 1); - gpg_error_t ERR; + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - cleanupZombies(2); // cleanup zombies under OSX. (Called before gpgme operation) - - if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_decrypt (CTX, CIPHER, PLAIN))) + if(id.length() != 16) { - std::cerr << "AuthGPGimpl::decryptText() Error decrypting text" << std::endl; - std::cerr << ProcessPGPmeError(ERR) << std::endl; - return false; + std::cerr << "Wrong string passed to getGPGDetails: \"" << id << "\"" << std::endl; + return false ; } + const PGPCertificateInfo *pc = PGPHandler::getCertificateInfo(PGPIdType(id)) ; + + if(pc == NULL) + return false ; + + const PGPCertificateInfo& cert(*pc) ; + + d.id = id ; + d.gpg_id = id ; + d.name = cert._name; + d.email = cert._email; + d.trustLvl = cert._trustLvl; + d.validLvl = cert._trustLvl; + d.ownsign = cert._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE; + d.gpgSigners.clear() ; + for(std::set::const_iterator it(cert.signers.begin());it!=cert.signers.end();++it) + d.gpgSigners.push_back( *it ) ; + + d.fpr = cert._fpr.toStdString(); + + d.accept_connection = cert._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION; + d.hasSignedMe = cert._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_SIGNED_ME; + return true; } -bool AuthGPGimpl::encryptText(gpgme_data_t PLAIN, gpgme_data_t CIPHER) +bool AuthGPG::getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&)) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - gpgme_encrypt_flags_t* flags = new gpgme_encrypt_flags_t(); - gpgme_key_t keys[2] = {mOwnGpgCert.key, NULL}; - gpgme_set_armor (CTX, 1); - gpg_error_t ERR; + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + std::list ids ; - cleanupZombies(2); // cleanup zombies under OSX. (Called before gpgme operation) + PGPHandler::getGPGFilteredList(ids,filter) ; - if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_encrypt(CTX, keys, *flags, PLAIN, CIPHER))) - { - std::cerr << "AuthGPGimpl::encryptText() Error encrypting text" << std::endl; - std::cerr << ProcessPGPmeError(ERR) << std::endl; - return false; - } + for(std::list::const_iterator it(ids.begin());it!=ids.end();++it) + list.push_back( (*it).toStdString() ) ; - return true; + return true ; } -bool AuthGPGimpl::getGPGValidList(std::list &ids) +static bool filter_Validity(const PGPCertificateInfo& info) { return true ; } //{ return info._validLvl >= PGPCertificateInfo::GPGME_VALIDITY_MARGINAL ; } +static bool filter_Accepted(const PGPCertificateInfo& info) { return info._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION ; } +static bool filter_OwnSigned(const PGPCertificateInfo& info) { return info._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; } + +bool AuthGPG::getGPGValidList(std::list &ids) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - /* add an id for each pgp certificate */ - certmap::iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - if (it->second.validLvl >= GPGME_VALIDITY_MARGINAL) { - ids.push_back(it->first); - } - } - return true; + return getGPGFilteredList(ids,&filter_Validity); } -bool AuthGPGimpl::getGPGAcceptedList(std::list &ids) +bool AuthGPG::getGPGAcceptedList(std::list &ids) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - if (it->second.accept_connection) - { - ids.push_back(it->first); - } - } - return true; + return getGPGFilteredList(ids,&filter_Accepted); } -bool AuthGPGimpl::getGPGSignedList(std::list &ids) +bool AuthGPG::getGPGSignedList(std::list &ids) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - if (it->second.ownsign) - { - ids.push_back(it->first); - } - } - return true; + return getGPGFilteredList(ids,&filter_OwnSigned); } -bool AuthGPGimpl::isGPGValid(const std::string &id) +bool AuthGPG::getCachedGPGCertificate(const std::string &id, std::string &certificate) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) { - return (it->second.validLvl >= GPGME_VALIDITY_MARGINAL); - } else { - return false; - } + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + certificate = PGPHandler::SaveCertificateToString(PGPIdType(id),false) ; -} - -bool AuthGPGimpl::isGPGId(const std::string &id) -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) { - return true; - } else { - return false; - } -} - - -bool AuthGPGimpl::isGPGSigned(const std::string &id) -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - { - return (it->second.ownsign); - } - return false; -} - -bool AuthGPGimpl::isGPGAccepted(const std::string &id) -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - { - return (it->second.accept_connection); - } - return false; -} - - -bool AuthGPGimpl::cacheGPGCertificate(const std::string &id, const std::string &certificate) -{ - - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - { - it->second.mCachedCert = certificate; - it->second.mHaveCachedCert = true; -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::cacheGPGCertificate() success for: " << id; - std::cerr << std::endl; +#ifdef LIMIT_CERTIFICATE_SIZE + std::string cleaned_key ; + if(PGPKeyManagement::createMinimalKey(certificate,cleaned_key)) + certificate = cleaned_key ; #endif - return true; - } - -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::cacheGPGCertificate() failed for: " << id; - std::cerr << std::endl; -#endif - return false; + return certificate.length() > 0 ; } - -bool AuthGPGimpl::getCachedGPGCertificate(const std::string &id, std::string &certificate) -{ - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(id))) - { - if (it->second.mHaveCachedCert) - { - certificate = it->second.mCachedCert; - -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::getCachedGPGCertificate() success for: " << id; - std::cerr << std::endl; -#endif - - return true; - } - } -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::getCachedGPGCertificate() failed for: " << id; - std::cerr << std::endl; -#endif - return false; -} - - /***************************************************************** * Loading and Saving Certificates - this has to * be able to handle both openpgp and X509 certificates. @@ -1480,60 +513,17 @@ bool AuthGPGimpl::getCachedGPGCertificate(const std::string &id, std::string &ce /* SKTAN : do not know how to use std::string id */ -std::string AuthGPGimpl::SaveCertificateToString(const std::string &id,bool include_signatures) +std::string AuthGPG::SaveCertificateToString(const std::string &id,bool include_signatures) { if (!isGPGId(id)) { - std::cerr << "AuthGPGimpl::SaveCertificateToString() unknown ID" << std::endl; + std::cerr << "AuthGPG::SaveCertificateToString() unknown ID" << std::endl; return ""; } RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - std::string tmp; - const char *pattern[] = { NULL, NULL }; - pattern[0] = id.c_str(); - gpgme_data_t gpgmeData; - -#ifdef GPGME_EXPORT_MODE_MINIMAL - gpgme_export_mode_t export_mode = include_signatures?0:GPGME_EXPORT_MODE_MINIMAL ; -#else - /* remove unused parameter warnings */ - (void) include_signatures; - - unsigned int export_mode = 0 ; -#endif - - if (GPG_ERR_NO_ERROR != gpgme_data_new (&gpgmeData)) - { - std::cerr << "Error create Data" << std::endl; - } - gpgme_set_armor (CTX, 1); - - if (GPG_ERR_NO_ERROR != gpgme_op_export_ext (CTX, pattern, export_mode, gpgmeData)) - { - std::cerr << "Error export Data" << std::endl; - } - - fflush (NULL); - //showData (gpgmeData); - - size_t len = 0; - char *export_txt = gpgme_data_release_and_get_mem(gpgmeData, &len); - - char *str = new char[len+1] ; - memcpy(str,export_txt,len) ; - str[len]=0 ; - - tmp = std::string(str); - - delete[] str ; - -#ifdef GPG_DEBUG - std::cerr << "Exported Certificate: " << std::endl << tmp << std::endl; -#endif - - gpgme_free(export_txt); + std::string tmp = PGPHandler::SaveCertificateToString(PGPIdType(id),include_signatures) ; // Try to remove signatures manually. // @@ -1546,93 +536,16 @@ std::string AuthGPGimpl::SaveCertificateToString(const std::string &id,bool incl } /* import to GnuPG and other Certificates */ -bool AuthGPGimpl::LoadCertificateFromString(const std::string &str, std::string &gpg_id,std::string& error_string) +bool AuthGPG::LoadCertificateFromString(const std::string &str, std::string &gpg_id,std::string& error_string) { - if (str == "") { - error_string = "Certificate is an empty string." ; - return false; - } - int imported = 0; - std::string fingerprint; + RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - //std::string cleancert = cleanUpCertificate(str); disable for p3disc message on windows system. Move the clean cert in p3peers - std::string cleancert = str; + PGPIdType id ; + bool res = PGPHandler::LoadCertificateFromString(str,id,error_string) ; - #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::LoadCertificateFromString() cleancert : " << cleancert; - #endif + gpg_id = id.toStdString(); - gpgme_data_t gpgmeData; - gpg_error_t ERR ; - - if (GPG_ERR_NO_ERROR != (ERR = gpgme_data_new_from_mem(&gpgmeData, cleancert.c_str(), cleancert.length(), 1))) - { - error_string = ProcessPGPmeError(ERR) ; - std::cerr << "Error create Data: " << error_string << std::endl; - return false; - } - - /* move string data to gpgmeData */ - - gpgme_set_armor (CTX, 1); - if (GPG_ERR_NO_ERROR != (ERR = gpgme_op_import (CTX,gpgmeData))) - { - std::cerr << "AuthGPGimpl::LoadCertificateFromString() Error Importing Certificate" << std::endl; - error_string = ProcessPGPmeError(ERR) ; - gpgme_data_release (gpgmeData); - return false ; - } - - - gpgme_import_result_t res = gpgme_op_import_result(CTX); - - if(res == NULL || res->imports == NULL) { - gpgme_data_release (gpgmeData); - error_string = "Certificate is corrupted." ; - return false ; - } - - fingerprint = std::string(res->imports->fpr); - #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::LoadCertificateFromString() Importing considered folowing fpr : " << fingerprint << std::endl; - #endif - imported = res->imported || res->new_signatures; - - #ifdef GPG_DEBUG - fprintf(stderr, "ImportCertificate(Considered: %d Imported: %d)\n", - res->considered, res->imported); - #endif - - /* do we need to delete res??? */ - gpgme_data_release (gpgmeData); - } - - /* extract id(s)! (only if we actually imported one) */ - if (imported) { - storeAllKeys(); - } - //retrieve the id of the key - certmap::iterator it; - gpg_id = "" ; - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - for(it = mKeyList.begin(); it != mKeyList.end(); it++) - { - if (it->second.fpr == fingerprint) - { - gpg_id = it->second.id; - break; - } - } -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::LoadCertificateFromString() returning with gpg_id : " << gpg_id << std::endl; -#endif - if (gpg_id == "") { - return false; - } else { - return true; - } + return res ; } /***************************************************************** @@ -1649,23 +562,16 @@ bool AuthGPGimpl::LoadCertificateFromString(const std::string &str, std::string /*************************************/ /* These take PGP Ids */ -bool AuthGPGimpl::AllowConnection(const std::string &gpg_id, bool accept) +bool AuthGPG::AllowConnection(const std::string &gpg_id, bool accept) { - #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::AllowConnection(" << gpg_id << ")" << std::endl; + std::cerr << "AuthGPG::AllowConnection(" << gpg_id << ")" << std::endl; #endif /* Was a "Reload Certificates" here -> be shouldn't be needed -> and very expensive, try without. */ { RsStackMutex stack(gpgMtxData); - certmap::iterator it; - if (mKeyList.end() == (it = mKeyList.find(gpg_id))) - { - return false; - } - it->second.accept_connection = accept; - mAcceptToConnectMap[gpg_id] = accept; + PGPHandler::setAcceptConnexion(PGPIdType(gpg_id),accept) ; } IndicateConfigChanged(); @@ -1676,895 +582,163 @@ bool AuthGPGimpl::AllowConnection(const std::string &gpg_id, bool accept) } /* These take PGP Ids */ -bool AuthGPGimpl::SignCertificateLevel0(const std::string &id) +bool AuthGPG::SignCertificateLevel0(const std::string &id) +{ +#ifdef GPG_DEBUG + std::cerr << "AuthGPG::SignCertificat(" << id << ")" << std::endl; +#endif + + return privateSignCertificate(id) ; +} + +bool AuthGPG::RevokeCertificate(const std::string &id) { /* remove unused parameter warnings */ (void) id; #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::SignCertificat(" << id << ")" << std::endl; -#endif - - if (1 != privateSignCertificate(id)) - { - storeAllKeys(); - return false; - } - - /* reload stuff now ... */ - storeAllKeys(); - return true; -} - -bool AuthGPGimpl::RevokeCertificate(const std::string &id) -{ - //RsStackMutex stack(gpgMtx); /******* LOCKED ******/ - - /* remove unused parameter warnings */ - (void) id; - -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::RevokeCertificate(" << id << ") not implemented yet" << std::endl; + std::cerr << "AuthGPG::RevokeCertificate(" << id << ") not implemented yet" << std::endl; #endif return false; } -bool AuthGPGimpl::TrustCertificate(const std::string &id, int trustlvl) +bool AuthGPG::TrustCertificate(const std::string &id, int trustlvl) { #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::TrustCertificate(" << id << ", " << trustlvl << ")" << std::endl; + std::cerr << "AuthGPG::TrustCertificate(" << id << ", " << trustlvl << ")" << std::endl; #endif - if (1 != privateTrustCertificate(id, trustlvl)) - { - storeAllKeys(); - return false; - } - - /* Keys are reloaded by privateTrustCertificate */ - return true; + return privateTrustCertificate(id, trustlvl) ; } -#if 0 -/* remove otherwise will cause bugs */ -bool AuthGPGimpl::SignData(std::string input, std::string &sign) +bool AuthGPG::SignDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) { - return false; + return DoOwnSignature(data, datalen, sign, signlen); } -bool AuthGPGimpl::SignData(const void *data, const uint32_t len, std::string &sign) +bool AuthGPG::VerifySignBin(const void *data, uint32_t datalen, unsigned char *sign, unsigned int signlen, const std::string &withfingerprint) { - return false; + return VerifySignature(data, datalen, sign, signlen, withfingerprint); } +/* Sign/Trust stuff */ -bool AuthGPGimpl::SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) +int AuthGPG::privateSignCertificate(const std::string &id) { - return false; -} -#endif + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ -bool AuthGPGimpl::SignDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) { - return DoOwnSignature(data, datalen, - sign, signlen); -} - -bool AuthGPGimpl::VerifySignBin(const void *data, uint32_t datalen, unsigned char *sign, unsigned int signlen, const std::string &withfingerprint) { - return VerifySignature(data, datalen, - sign, signlen, withfingerprint); -} - - - /* Sign/Trust stuff */ - -int AuthGPGimpl::privateSignCertificate(const std::string &id) -{ - /* The key should be in Others list and not in Peers list ?? - * Once the key is signed, it moves from Others to Peers list ??? - */ - - gpgcert signKey; - gpgcert ownKey; - - { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - certmap::iterator it; - - if (mKeyList.end() == (it = mKeyList.find(id))) - { - return false; - } - - /* grab a reference, so the key remains */ - gpgme_key_ref(it->second.key); - - signKey = it->second; - - /* grab a reference, so the key remains */ - gpgme_key_ref(mOwnGpgCert.key); - - ownKey = mOwnGpgCert; - } /******* UNLOCKED ******/ - - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - class SignParams sparams("0"); - class EditParams params(SIGN_START, &sparams); - gpgme_data_t out; - gpg_error_t ERR; - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_data_new(&out))) { - return 0; - } - - gpgme_signers_clear(CTX); - if(GPG_ERR_NO_ERROR != (ERR = gpgme_signers_add(CTX, ownKey.key))) { - gpgme_data_release(out); - return 0; - } - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_op_edit(CTX, signKey.key, keySignCallback, ¶ms, out))) { - gpgme_data_release(out); - gpgme_signers_clear(CTX); - return 0; - } - - gpgme_data_release(out); - gpgme_signers_clear(CTX); - - return 1; + int ret = PGPHandler::privateSignCertificate(mOwnGpgId,PGPIdType(id)) ; + _force_sync_database = true ; + return ret ; } /* revoke the signature on Certificate */ -int AuthGPGimpl::privateRevokeCertificate(const std::string &/*id*/) +int AuthGPG::privateRevokeCertificate(const std::string &/*id*/) { //RsStackMutex stack(gpgMtx); /******* LOCKED ******/ return 0; } -int AuthGPGimpl::privateTrustCertificate(const std::string &id, int trustlvl) +int AuthGPG::privateTrustCertificate(const std::string &id, int trustlvl) { + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + /* The certificate should be in Peers list ??? */ - if(!isGPGAccepted(id)) { + if(!isGPGAccepted(id)) + { std::cerr << "Invalid Certificate" << std::endl; return 0; } - - { - gpgcert trustCert; - { - RsStackMutex stack(gpgMtxData); - trustCert = mKeyList.find(id)->second; - } /******* UNLOCKED ******/ - - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ - - gpgme_key_t trustKey = trustCert.key; - std::string trustString; - rs_sprintf(trustString, "%d", trustlvl); - class TrustParams sparams(trustString); - class EditParams params(TRUST_START, &sparams); - gpgme_data_t out; - gpg_error_t ERR; - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_data_new(&out))) { - return 0; - } - - if(GPG_ERR_NO_ERROR != (ERR = gpgme_op_edit(CTX, trustKey, trustCallback, ¶ms, out))) { - gpgme_data_release(out); - return 0; - } - - gpgme_data_release(out); - - //the key ref has changed, we got to get rid of the old reference. - trustCert.key = NULL; - } - - storeAllKeys(); - - return 1; -} - -static std::string setKeyPairParams(bool useRsa, unsigned int blen, - std::string name, std::string comment, std::string email, - std::string inPassphrase) -{ - std::string params = "\n"; - if (useRsa) - { - params += "Key-Type: RSA\n"; - if (blen < 1024) - { -#ifdef GPG_DEBUG - std::cerr << "Weak Key... strengthing..."<< std::endl; -#endif - blen = 1024; - } - blen = ((blen / 512) * 512); /* make multiple of 512 */ - rs_sprintf_append(params, "Key-Length: %u\n", blen); - } - else - { - params += "Key-Type: DSA\n"; - params += "Key-Length: 1024\n"; - params += "Subkey-Type: ELG-E\n"; - params += "Subkey-Length: 1024\n"; - } - params += "Name-Real: " + name + "\n"; - params += "Name-Comment: " + comment + "\n"; - params += "Name-Email: " + email + "\n"; - params += "Expire-Date: 0\n"; - params += "Passphrase: " + inPassphrase + "\n"; - params += "\n"; - - return params; -} - - -/* Author: Shiva - * This function returns the key macthing the user parameters - * from the keyring - */ - -#ifdef UNUSED_CODE -static gpgme_key_t getKey(gpgme_ctx_t CTX, std::string name, std::string comment, std::string email) { - - gpgme_key_t key; - gpgme_user_id_t user; - - /* Initiates a key listing */ - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_start (CTX, "", 0)) - { - std::cerr << "Error iterating through KeyList"; - std::cerr << std::endl; - return false; - - } - - /* Loop until end of key */ - for(int i = 0;(GPG_ERR_NO_ERROR == gpgme_op_keylist_next (CTX, &key)); i++) - { - user = key->uids; - - while(user != NULL) { - if((name.size() && name == user->name) && (comment.size() && comment == user->comment) && \ - (email.size() && email == user->email)) - { - /* grab a reference to the key */ - gpgme_op_keylist_end(CTX); - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_end(CTX)) - { - std::cerr << "Error ending KeyList"; - std::cerr << std::endl; - } - gpgme_key_ref(key); - return key; - } - user = user->next; - } - } - - if (GPG_ERR_NO_ERROR != gpgme_op_keylist_end(CTX)) - { - std::cerr << "Error ending KeyList" << std::endl; - } - return NULL; -} -#endif - - -/* Callback function for key signing */ - -static gpg_error_t keySignCallback(void *opaque, gpgme_status_code_t status, \ - const char *args, int fd) { - - class EditParams *params = (class EditParams *)opaque; - class SignParams *sparams = (class SignParams *)params->oParams; - const char *result = NULL; -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback status: %d args: %s, params->state: %d\n", status, args, params->state); - - /* printf stuff out */ - if (status == GPGME_STATUS_EOF) - fprintf(stderr,"keySignCallback GPGME_STATUS_EOF\n"); - if (status == GPGME_STATUS_GOT_IT) - fprintf(stderr,"keySignCallback GPGME_STATUS_GOT_IT\n"); - if (status == GPGME_STATUS_USERID_HINT) - fprintf(stderr,"keySignCallback GPGME_STATUS_USERID_HINT\n"); - if (status == GPGME_STATUS_NEED_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_NEED_PASSPHRASE\n"); - if (status == GPGME_STATUS_GOOD_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_GOOD_PASSPHRASE\n"); - if (status == GPGME_STATUS_BAD_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_BAD_PASSPHRASE\n"); - if (status == GPGME_STATUS_GET_LINE) - fprintf(stderr,"keySignCallback GPGME_STATUS_GET_LINE\n"); - if (status == GPGME_STATUS_GET_BOOL) - fprintf(stderr,"keySignCallback GPGME_STATUS_GET_BOOL\n"); - if (status == GPGME_STATUS_ALREADY_SIGNED) - fprintf(stderr,"keySignCallback GPGME_STATUS_ALREADY_SIGNED\n"); - - /* printf stuff out */ - if (params->state == SIGN_START) - fprintf(stderr,"keySignCallback params->state SIGN_START\n"); - if (params->state == SIGN_COMMAND) - fprintf(stderr,"keySignCallback params->state SIGN_COMMAND\n"); - if (params->state == SIGN_UIDS) - fprintf(stderr,"keySignCallback params->state SIGN_UIDS\n"); - if (params->state == SIGN_SET_EXPIRE) - fprintf(stderr,"keySignCallback params->state SIGN_SET_EXPIRE\n"); - if (params->state == SIGN_SET_CHECK_LEVEL) - fprintf(stderr,"keySignCallback params->state SIGN_SET_CHECK_LEVEL\n"); - if (params->state == SIGN_CONFIRM) - fprintf(stderr,"keySignCallback params->state SIGN_CONFIRM\n"); - if (params->state == SIGN_QUIT) - fprintf(stderr,"keySignCallback params->state SIGN_QUIT\n"); - if (params->state == SIGN_ENTER_PASSPHRASE) - fprintf(stderr,"keySignCallback params->state SIGN_ENTER_PASSPHRASE\n"); - if (params->state == SIGN_ERROR) - fprintf(stderr,"keySignCallback params->state SIGN_ERROR"); -#endif - - - if(status == GPGME_STATUS_EOF || - status == GPGME_STATUS_GOT_IT || - status == GPGME_STATUS_USERID_HINT || - status == GPGME_STATUS_NEED_PASSPHRASE || - // status == GPGME_STATUS_GOOD_PASSPHRASE || - status == GPGME_STATUS_BAD_PASSPHRASE) { - - - fprintf(stderr,"keySignCallback Error status\n"); - std::cerr << ProcessPGPmeError(params->err) << std::endl; - - return params->err; - } - - switch (params->state) - { - case SIGN_START: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_START\n"); -#endif - - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) - { - params->state = SIGN_COMMAND; - result = "sign"; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_COMMAND: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_COMMAND\n"); -#endif - - if (status == GPGME_STATUS_GET_BOOL && - (!std::string("keyedit.sign_all.okay").compare(args))) - { - params->state = SIGN_UIDS; - result = "Y"; - } - else if (status == GPGME_STATUS_GET_BOOL && - (!std::string("sign_uid.okay").compare(args))) - { - params->state = SIGN_ENTER_PASSPHRASE; - result = "Y"; - } - else if (status == GPGME_STATUS_GET_LINE && - (!std::string("sign_uid.expire").compare(args))) - { - params->state = SIGN_SET_EXPIRE; - result = "Y"; - } - else if (status == GPGME_STATUS_GET_LINE && - (!std::string("sign_uid.class").compare(args))) - { - params->state = SIGN_SET_CHECK_LEVEL; - result = sparams->checkLvl.c_str(); - } - else if (status == GPGME_STATUS_ALREADY_SIGNED) - { - /* The key has already been signed with this key */ - params->state = SIGN_QUIT; - result = "quit"; - } - else if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) - { - /* Failed sign: expired key */ - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_UIDS: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_UIDS\n"); -#endif - - if (status == GPGME_STATUS_GET_LINE && - (!std::string("sign_uid.expire").compare(args))) - { - params->state = SIGN_SET_EXPIRE; - result = "Y"; - } - else if (status == GPGME_STATUS_GET_LINE && - (!std::string("sign_uid.class").compare(args))) - { - params->state = SIGN_SET_CHECK_LEVEL; - result = sparams->checkLvl.c_str(); - } - else if (status == GPGME_STATUS_GET_BOOL && - (!std::string("sign_uid.okay").compare(args))) - { - params->state = SIGN_ENTER_PASSPHRASE; - result = "Y"; - } - else if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) - { - /* Failed sign: expired key */ - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_SET_EXPIRE: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_SET_EXPIRE\n"); -#endif - - if (status == GPGME_STATUS_GET_LINE && - (!std::string("sign_uid.class").compare(args))) - { - params->state = SIGN_SET_CHECK_LEVEL; - result = sparams->checkLvl.c_str(); - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_SET_CHECK_LEVEL: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_SET_CHECK_LEVEL\n"); -#endif - - if (status == GPGME_STATUS_GET_BOOL && - (!std::string("sign_uid.okay").compare(args))) - { - params->state = SIGN_ENTER_PASSPHRASE; - result = "Y"; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_ENTER_PASSPHRASE: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_ENTER_PASSPHRASE\n"); -#endif - - if (status == GPGME_STATUS_GOOD_PASSPHRASE) - { - params->state = SIGN_CONFIRM; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_CONFIRM: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_CONFIRM\n"); -#endif - - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) - { - params->state = SIGN_QUIT; - result = "quit"; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_QUIT: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_QUIT\n"); -#endif - - if (status == GPGME_STATUS_GET_BOOL && - (!std::string("keyedit.save.okay").compare(args))) - { - params->state = SIGN_SAVE; - result = "Y"; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case SIGN_ERROR: -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback SIGN_ERROR\n"); -#endif - - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) - { - /* Go to quit operation state */ - params->state = SIGN_QUIT; - result = "quit"; - } - else - { - params->state = SIGN_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - default: - fprintf(stderr,"keySignCallback UNKNOWN state\n"); - break; - } - - if (result) - { -#ifdef GPG_DEBUG - fprintf(stderr,"keySignCallback result:%s\n", result); -#endif -#ifndef WINDOWS_SYS - if (*result) - { - write (fd, result, strlen (result)); - write (fd, "\n", 1); - } -#else - DWORD written = 0; - HANDLE winFd = (HANDLE) fd; - if (*result) - { - WriteFile(winFd, result, strlen(result), &written, NULL); - WriteFile(winFd, "\n", 1, &written, NULL); - } -#endif - - } - - fprintf(stderr,"keySignCallback Error status\n"); - std::cerr << ProcessPGPmeError(params->err) << std::endl; - - return params->err; -} - - - -/* Callback function for assigning trust level */ - -static gpgme_error_t trustCallback(void *opaque, gpgme_status_code_t status, \ - const char *args, int fd) { - - class EditParams *params = (class EditParams *)opaque; - class TrustParams *tparams = (class TrustParams *)params->oParams; - const char *result = NULL; - - /* printf stuff out */ -#ifdef GPG_DEBUG - if (status == GPGME_STATUS_EOF) - fprintf(stderr,"keySignCallback GPGME_STATUS_EOF\n"); - if (status == GPGME_STATUS_GOT_IT) - fprintf(stderr,"keySignCallback GPGME_STATUS_GOT_IT\n"); - if (status == GPGME_STATUS_USERID_HINT) - fprintf(stderr,"keySignCallback GPGME_STATUS_USERID_HINT\n"); - if (status == GPGME_STATUS_NEED_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_NEED_PASSPHRASE\n"); - if (status == GPGME_STATUS_GOOD_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_GOOD_PASSPHRASE\n"); - if (status == GPGME_STATUS_BAD_PASSPHRASE) - fprintf(stderr,"keySignCallback GPGME_STATUS_BAD_PASSPHRASE\n"); - if (status == GPGME_STATUS_GET_LINE) - fprintf(stderr,"keySignCallback GPGME_STATUS_GET_LINE\n"); - if (status == GPGME_STATUS_GET_BOOL) - fprintf(stderr,"keySignCallback GPGME_STATUS_GET_BOOL \n"); - if (status == GPGME_STATUS_ALREADY_SIGNED) - fprintf(stderr,"keySignCallback GPGME_STATUS_ALREADY_SIGNED\n"); - - /* printf stuff out */ - if (params->state == TRUST_START) - fprintf(stderr,"keySignCallback params->state TRUST_START\n"); - if (params->state == TRUST_COMMAND) - fprintf(stderr,"keySignCallback params->state TRUST_COMMAND\n"); - if (params->state == TRUST_VALUE) - fprintf(stderr,"keySignCallback params->state TRUST_VALUE\n"); - if (params->state == TRUST_REALLY_ULTIMATE) - fprintf(stderr,"keySignCallback params->state TRUST_REALLY_ULTIMATE\n"); - if (params->state == TRUST_QUIT) - fprintf(stderr,"keySignCallback params->state TRUST_QUIT\n"); - if (params->state == TRUST_ERROR) - fprintf(stderr,"keySignCallback params->state TRUST_ERROR\n"); -#endif - - - if(status == GPGME_STATUS_EOF || - status == GPGME_STATUS_GOT_IT) { - return params->err; - } - - - switch (params->state) - { - case TRUST_START: - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) { - params->state = TRUST_COMMAND; - result = "trust"; - } else { - params->state = TRUST_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - - case TRUST_COMMAND: - if (status == GPGME_STATUS_GET_LINE && - (!std::string("edit_ownertrust.value").compare(args))) { - params->state = TRUST_VALUE; - result = tparams->trustLvl.c_str();; - } else { - params->state = TRUST_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case TRUST_VALUE: - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) { - params->state = TRUST_QUIT; - result = "quit"; - } - else if (status == GPGME_STATUS_GET_BOOL && - (!std::string("edit_ownertrust.set_ultimate.okay").compare(args))) { - params->state = TRUST_REALLY_ULTIMATE; - result = "Y"; - } - else { - params->state = TRUST_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case TRUST_REALLY_ULTIMATE: - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) { - params->state = TRUST_QUIT; - result = "quit"; - } else { - params->state = TRUST_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case TRUST_QUIT: - if (status == GPGME_STATUS_GET_BOOL && - (!std::string("keyedit.save.okay").compare(args))) { - params->state = TRUST_SAVE; - result = "Y"; - } else { - params->state = TRUST_ERROR; - params->err = gpg_error (GPG_ERR_GENERAL); - } - break; - case TRUST_ERROR: - if (status == GPGME_STATUS_GET_LINE && - (!std::string("keyedit.prompt").compare(args))) { - /* Go to quit operation state */ - params->state = TRUST_QUIT; - result = "quit"; - } else { - params->state = TRUST_ERROR; - } - break; - } - - if (result) - { -#ifndef WINDOWS_SYS - if (*result) - write (fd, result, strlen (result)); - write (fd, "\n", 1); -#else - DWORD written = 0; - HANDLE winFd = (HANDLE) fd; - if (*result) - WriteFile(winFd, result, strlen (result), &written, NULL); - WriteFile(winFd, "\n", 1, &written, NULL); -#endif -#ifdef GPG_DEBUG - std::cerr << "trustCallback() result : " << result << std::endl; -#endif - } - - return params->err; + int res = PGPHandler::privateTrustCertificate(PGPIdType(id),trustlvl) ; + _force_sync_database = true ; + return res ; } // -----------------------------------------------------------------------------------// // -------------------------------- Config functions ------------------------------ // // -----------------------------------------------------------------------------------// // -RsSerialiser *AuthGPGimpl::setupSerialiser() +RsSerialiser *AuthGPG::setupSerialiser() { RsSerialiser *rss = new RsSerialiser ; rss->addSerialType(new RsGeneralConfigSerialiser()); return rss ; } -bool AuthGPGimpl::saveList(bool& cleanup, std::list& lst) +bool AuthGPG::saveList(bool& cleanup, std::list& lst) { - #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::saveList() called" << std::endl ; - #endif - - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - - cleanup = true ; - - // Now save config for network digging strategies - RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; - std::map::iterator mapIt; - for (mapIt = mAcceptToConnectMap.begin(); mapIt != mAcceptToConnectMap.end(); mapIt++) - { - // skip our own id. - if (mapIt->first == mOwnGpgId) - { - continue; - } - // skip if we dont accept them. - if (!(mapIt->second)) - { - continue; - } - - RsTlvKeyValue kv; - kv.key = mapIt->first; #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::saveList() called (mapIt->second) : " << (mapIt->second) << std::endl ; + std::cerr << "AuthGPG::saveList() called" << std::endl ; #endif - kv.value = "TRUE"; - vitem->tlvkvs.pairs.push_back(kv) ; - } - lst.push_back(vitem); + std::list ids ; + getGPGAcceptedList(ids) ; // needs to be done before the lock - return true; + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + + cleanup = true ; + + // Now save config for network digging strategies + RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; + + for (std::list::const_iterator it(ids.begin()); it != ids.end(); ++it) + if((*it) != mOwnGpgId.toStdString()) // skip our own id. + { + RsTlvKeyValue kv; + kv.key = *it ; +#ifdef GPG_DEBUG + std::cerr << "AuthGPG::saveList() called (it->second) : " << (it->second) << std::endl ; +#endif + kv.value = "TRUE"; + vitem->tlvkvs.pairs.push_back(kv) ; + } + lst.push_back(vitem); + + return true; } -bool AuthGPGimpl::loadList(std::list& load) +bool AuthGPG::loadList(std::list& load) { - #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::loadList() Item Count: " << load.size() << std::endl; - #endif +#ifdef GPG_DEBUG + std::cerr << "AuthGPG::loadList() Item Count: " << load.size() << std::endl; +#endif -// already stored in AuthGPGimpl::InitAuth -// storeAllKeys(); - - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - /* load the list of accepted gpg keys */ - std::list::iterator it; - for(it = load.begin(); it != load.end(); it++) + RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + /* load the list of accepted gpg keys */ + std::list::iterator it; + for(it = load.begin(); it != load.end(); it++) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); if(vitem) { #ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::loadList() General Variable Config Item:" << std::endl; + std::cerr << "AuthGPG::loadList() General Variable Config Item:" << std::endl; vitem->print(std::cerr, 10); std::cerr << std::endl; #endif std::list::iterator kit; for(kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); kit++) - { - if (kit->key == mOwnGpgId) - { - continue; - } - - /* only allowed in the map if the gpg certificate exists */ - certmap::iterator it; - if (mKeyList.end() != (it = mKeyList.find(kit->key))) - { -#ifdef GPG_DEBUG - std::cerr << "AuthGPGimpl::loadList() setting accept to : " << (kit->value == "TRUE"); - std::cerr << " for gpg key id : " << kit->key << std::endl; -#endif - mAcceptToConnectMap[kit->key] = (kit->value == "TRUE"); - it->second.accept_connection = (kit->value == "TRUE"); - } - } - } - delete (*it); - } - return true; + if (kit->key != mOwnGpgId.toStdString()) + PGPHandler::setAcceptConnexion(PGPIdType(kit->key), (kit->value == "TRUE")); + } + delete (*it); + } + return true; } -bool AuthGPGimpl::addService(AuthGPGService *service) +bool AuthGPG::addService(AuthGPGService *service) { - RsStackMutex stack(gpgMtxService); /********* LOCKED *********/ + RsStackMutex stack(gpgMtxService); /********* LOCKED *********/ - if (std::find(services.begin(), services.end(), service) != services.end()) { - /* it exists already! */ - return false; - } - - services.push_back(service); - return true; -} - - -/***************************** HACK to Cleanup OSX Zombies *****************************/ - - -#ifdef __APPLE__ -#include -#endif - -void cleanupZombies(int numkill) -{ - -#ifdef __APPLE__ - - pid_t wpid = -1; // any child. - int stat_loc = 0; - int options = WNOHANG ; - - //std::cerr << "cleanupZombies() checking for dead children"; - //std::cerr << std::endl; - - int i; - for(i = 0; i < numkill; i++) - { - pid_t childpid = waitpid(wpid, &stat_loc, options); - - if (childpid > 0) - { - std::cerr << "cleanupZombies() Found stopped child with pid: " << childpid; - std::cerr << std::endl; - } - else - { - //std::cerr << "cleanupZombies() No Zombies around!"; - //std::cerr << std::endl; - break; - } + if (std::find(services.begin(), services.end(), service) != services.end()) { + /* it exists already! */ + return false; } - //std::cerr << "cleanupZombies() Killed " << i << " zombies"; - //std::cerr << std::endl; -#else - /* remove unused parameter warnings */ - (void) numkill; -#endif - - return; + services.push_back(service); + return true; } - + + diff --git a/libretroshare/src/pqi/authgpg.h b/libretroshare/src/pqi/authgpg.h index d5733e74e..121e5a81d 100644 --- a/libretroshare/src/pqi/authgpg.h +++ b/libretroshare/src/pqi/authgpg.h @@ -50,6 +50,7 @@ #include #include #include "pqi/p3cfgmgr.h" +#include "pgp/pgphandler.h" #define MAX_GPG_SIGNATURE_SIZE 4096 @@ -57,39 +58,8 @@ class RsPeerDetails; /*! * gpgcert is the identifier for a person. - * It is a wrapper class for a GPGme OpenPGP certificate. + * It is a wrapper class for a OpenPGP certificate. */ -class AuthGPG; - -class gpgcert -{ - public: - gpgcert(); - ~gpgcert(); - - std::string id; - std::string name; - std::string email; - - std::string fpr; /* fingerprint */ - std::list signers; - - uint32_t trustLvl; - uint32_t validLvl; - - bool ownsign; - - //This is not gpg, but RS data. A gpg peer can be accepted for connecting but not signed. - bool accept_connection; - - gpgme_key_t key; - - // Cached Certificates... - bool mHaveCachedCert; - std::string mCachedCert; - - -}; class AuthGPGOperation { @@ -135,427 +105,289 @@ public: virtual void setGPGOperation(AuthGPGOperation *operation) = 0; }; -/*! - * The certificate map type - */ -typedef std::map certmap; - -//! provides basic gpg functionality -/*! - * - * This provides retroshare basic gpg functionality and - * key/web-of-trust management, also handle cert intialisation for retroshare - */ - -extern void AuthGPGInit(); -extern void AuthGPGExit(); - -class AuthGPG : public RsThread -{ - - public: - //AuthGPG(); - -static AuthGPG *getAuthGPG(); - - /** - * @param ids list of gpg certificate ids (note, not the actual certificates) - */ -virtual bool availableGPGCertificatesWithPrivateKeys(std::list &ids) = 0; -virtual bool printKeys() = 0; - -/*********************************************************************************/ -/************************* STAGE 1 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 1: Initialisation.... As we are switching to OpenPGP the init functions - * will be different. Just move the initialisation functions over.... - * - * As GPGMe requires external calls to the GPG executable, which could potentially - * be expensive, We'll want to cache the GPG keys in this class. - * This should be done at initialisation, and saved in a map. - * (see storage at the end of the class) - * - ****/ -virtual bool active() = 0; - - /* Initialize */ -virtual bool InitAuth () = 0; - - /* Init by generating new Own PGP Cert, or selecting existing PGP Cert */ -virtual int GPGInit(const std::string &ownId) = 0; -virtual bool CloseAuth() = 0; -virtual bool GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &errString) = 0; - -/*********************************************************************************/ -/************************* STAGE 3 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 3: These are some of the most commonly used functions in Retroshare. - * - * More commonly used functions. - * - * provide access to details in cache list. - * - ****/ -virtual std::string getGPGName(const std::string &pgp_id) = 0; -virtual std::string getGPGEmail(const std::string &pgp_id) = 0; - - /* PGP web of trust management */ -virtual std::string getGPGOwnId() = 0; -virtual std::string getGPGOwnName() = 0; - -//virtual std::string getGPGOwnEmail() = 0; -virtual bool getGPGDetails(const std::string &id, RsPeerDetails &d) = 0; -virtual bool getGPGAllList(std::list &ids) = 0; -virtual bool getGPGValidList(std::list &ids) = 0; -virtual bool getGPGAcceptedList(std::list &ids) = 0; -virtual bool getGPGSignedList(std::list &ids) = 0; -virtual bool isGPGValid(const std::string &id) = 0; -virtual bool isGPGSigned(const std::string &id) = 0; -virtual bool isGPGAccepted(const std::string &id) = 0; -virtual bool isGPGId(const std::string &id) = 0; - -/*********************************************************************************/ -/************************* STAGE 4 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 4: Loading and Saving Certificates. (Strings and Files) - * - ****/ -virtual bool LoadCertificateFromString(const std::string &pem, std::string &gpg_id,std::string& error_string) = 0; -virtual std::string SaveCertificateToString(const std::string &id,bool include_signatures) = 0; - -/*********************************************************************************/ -/************************* STAGE 6 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 6: Authentication, Trust and Signing. - * - * This is some of the harder functions, but they should have been - * done in gpgroot already. - * - ****/ - -virtual bool AllowConnection(const std::string &gpg_id, bool accept) = 0; - -virtual bool SignCertificateLevel0(const std::string &id) = 0; -virtual bool RevokeCertificate(const std::string &id) = 0; /* Particularly hard - leave for later */ -//virtual bool TrustCertificateNone(std::string id) = 0; -//virtual bool TrustCertificateMarginally(std::string id) = 0; -//virtual bool TrustCertificateFully(std::string id) = 0; -virtual bool TrustCertificate(const std::string &id, int trustlvl) = 0; //trustlvl is 2 for none, 3 for marginal and 4 for full trust - -/*********************************************************************************/ -/************************* STAGE 7 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 7: Signing Data. - * - * There should also be Encryption Functions... (do later). - * - ****/ -//virtual bool SignData(std::string input, std::string &sign) = 0; -//virtual bool SignData(const void *data, const uint32_t len, std::string &sign) = 0; -//virtual bool SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen) = 0; -virtual bool SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen) = 0; -virtual bool VerifySignBin(const void*, uint32_t, unsigned char*, unsigned int, const std::string &withfingerprint) = 0; -virtual bool decryptText(gpgme_data_t CIPHER, gpgme_data_t PLAIN) = 0; -virtual bool encryptText(gpgme_data_t PLAIN, gpgme_data_t CIPHER) = 0; -//END of PGP public functions - -/* GPG service */ -virtual bool addService(AuthGPGService *service) = 0; - -}; - -/* The real implementation! */ - - -class AuthGPGimpl : public AuthGPG, public p3Config +class AuthGPG: public p3Config, public RsThread, public PGPHandler { public: - AuthGPGimpl(); - ~AuthGPGimpl(); + static void init( const std::string& path_to_pubring, + const std::string& path_to_secring, + const std::string& path_to_trustdb, + const std::string& pgp_lock_file); - /** - * @param ids list of gpg certificate ids (note, not the actual certificates) - */ -virtual bool availableGPGCertificatesWithPrivateKeys(std::list &ids); + static void exit(); + static AuthGPG *getAuthGPG() { return _instance ; } -virtual bool printKeys(); + /** + * @param ids list of gpg certificate ids (note, not the actual certificates) + */ + virtual bool availableGPGCertificatesWithPrivateKeys(std::list &ids); -/*********************************************************************************/ -/************************* STAGE 1 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 1: Initialisation.... As we are switching to OpenPGP the init functions - * will be different. Just move the initialisation functions over.... - * - * As GPGMe requires external calls to the GPG executable, which could potentially - * be expensive, We'll want to cache the GPG keys in this class. - * This should be done at initialisation, and saved in a map. - * (see storage at the end of the class) - * - ****/ -virtual bool active(); + /*********************************************************************************/ + /************************* STAGE 1 ***********************************************/ + /*********************************************************************************/ + /***** + * STAGE 1: Initialisation.... As we are switching to OpenPGP the init functions + * will be different. Just move the initialisation functions over.... + * + * As GPGMe requires external calls to the GPG executable, which could potentially + * be expensive, We'll want to cache the GPG keys in this class. + * This should be done at initialisation, and saved in a map. + * (see storage at the end of the class) + * + ****/ + virtual bool active(); - /* Initialize */ -virtual bool InitAuth (); + // /* Initialize */ + // virtual bool InitAuth (); + // virtual bool CloseAuth(); - /* Init by generating new Own PGP Cert, or selecting existing PGP Cert */ -virtual int GPGInit(const std::string &ownId); -virtual bool CloseAuth(); -virtual bool GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &errString); + /* Init by generating new Own PGP Cert, or selecting existing PGP Cert */ -/*********************************************************************************/ -/************************* STAGE 3 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 3: These are some of the most commonly used functions in Retroshare. - * - * More commonly used functions. - * - * provide access to details in cache list. - * - ****/ -virtual std::string getGPGName(const std::string &pgp_id); -virtual std::string getGPGEmail(const std::string &pgp_id); + virtual int GPGInit(const std::string &ownId); + virtual bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, std::string &pgpId, std::string &errString); - /* PGP web of trust management */ -virtual std::string getGPGOwnId(); -virtual std::string getGPGOwnName(); + /*********************************************************************************/ + /************************* STAGE 3 ***********************************************/ + /*********************************************************************************/ + /***** + * STAGE 3: These are some of the most commonly used functions in Retroshare. + * + * More commonly used functions. + * + * provide access to details in cache list. + * + ****/ + virtual std::string getGPGName(const std::string &pgp_id,bool *success = NULL); + virtual std::string getGPGEmail(const std::string &pgp_id,bool *success = NULL); -//virtual std::string getGPGOwnEmail(); -virtual bool getGPGDetails(const std::string &id, RsPeerDetails &d); -virtual bool getGPGAllList(std::list &ids); -virtual bool getGPGValidList(std::list &ids); -virtual bool getGPGAcceptedList(std::list &ids); -virtual bool getGPGSignedList(std::list &ids); -virtual bool isGPGValid(const std::string &id); -virtual bool isGPGSigned(const std::string &id); -virtual bool isGPGAccepted(const std::string &id); -virtual bool isGPGId(const std::string &id); + /* PGP web of trust management */ + virtual std::string getGPGOwnId(); + virtual std::string getGPGOwnName(); -/*********************************************************************************/ -/************************* STAGE 4 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 4: Loading and Saving Certificates. (Strings and Files) - * - ****/ -virtual bool LoadCertificateFromString(const std::string &pem, std::string &gpg_id,std::string& error_string); -virtual std::string SaveCertificateToString(const std::string &id,bool include_signatures) ; + //virtual std::string getGPGOwnEmail(); + virtual bool isKeySupported(const std::string &id) const ; + virtual bool haveSecretKey(const std::string &id) const ; + virtual bool getGPGDetails(const std::string &id, RsPeerDetails &d); + virtual bool getGPGAllList(std::list &ids); + virtual bool getGPGValidList(std::list &ids); + virtual bool getGPGAcceptedList(std::list &ids); + virtual bool getGPGSignedList(std::list &ids); + virtual bool importProfile(const std::string& filename,std::string& gpg_id,std::string& import_error) ; + virtual bool exportProfile(const std::string& filename,const std::string& gpg_id) ; -// Cached certificates. -bool cacheGPGCertificate(const std::string &id, const std::string &certificate); -bool getCachedGPGCertificate(const std::string &id, std::string &certificate); + /*********************************************************************************/ + /************************* STAGE 4 ***********************************************/ + /*********************************************************************************/ + /***** + * STAGE 4: Loading and Saving Certificates. (Strings and Files) + * + ****/ + virtual bool LoadCertificateFromString(const std::string &pem, std::string &gpg_id,std::string& error_string); + virtual std::string SaveCertificateToString(const std::string &id,bool include_signatures) ; -/*********************************************************************************/ -/************************* STAGE 6 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 6: Authentication, Trust and Signing. - * - * This is some of the harder functions, but they should have been - * done in gpgroot already. - * - ****/ -virtual bool AllowConnection(const std::string &gpg_id, bool accept); + // Cached certificates. + bool getCachedGPGCertificate(const std::string &id, std::string &certificate); -virtual bool SignCertificateLevel0(const std::string &id); -virtual bool RevokeCertificate(const std::string &id); /* Particularly hard - leave for later */ + /*********************************************************************************/ + /************************* STAGE 6 ***********************************************/ + /*********************************************************************************/ + /***** + * STAGE 6: Authentication, Trust and Signing. + * + * This is some of the harder functions, but they should have been + * done in gpgroot already. + * + ****/ + virtual bool AllowConnection(const std::string &gpg_id, bool accept); -//virtual bool TrustCertificateNone(std::string id); -//virtual bool TrustCertificateMarginally(std::string id); -//virtual bool TrustCertificateFully(std::string id); -virtual bool TrustCertificate(const std::string &id, int trustlvl); //trustlvl is 2 for none, 3 for marginal and 4 for full trust + virtual bool SignCertificateLevel0(const std::string &id); + virtual bool RevokeCertificate(const std::string &id); /* Particularly hard - leave for later */ -/*********************************************************************************/ -/************************* STAGE 7 ***********************************************/ -/*********************************************************************************/ -/***** - * STAGE 7: Signing Data. - * - * There should also be Encryption Functions... (do later). - * - ****/ -//virtual bool SignData(std::string input, std::string &sign); -//virtual bool SignData(const void *data, const uint32_t len, std::string &sign); -//virtual bool SignDataBin(std::string input, unsigned char *sign, unsigned int *signlen); -virtual bool SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen); -virtual bool VerifySignBin(const void*, uint32_t, unsigned char*, unsigned int, const std::string &withfingerprint); -virtual bool decryptText(gpgme_data_t CIPHER, gpgme_data_t PLAIN); -virtual bool encryptText(gpgme_data_t PLAIN, gpgme_data_t CIPHER); -//END of PGP public functions + virtual bool TrustCertificate(const std::string &id, int trustlvl); //trustlvl is 2 for none, 3 for marginal and 4 for full trust -/* GPG service */ -virtual bool addService(AuthGPGService *service); + /*********************************************************************************/ + /************************* STAGE 7 ***********************************************/ + /*********************************************************************************/ + /***** + * STAGE 7: Signing Data. + * + * There should also be Encryption Functions... (do later). + * + ****/ + virtual bool SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen); + virtual bool VerifySignBin(const void*, uint32_t, unsigned char*, unsigned int, const std::string &withfingerprint); - protected: -/*****************************************************************/ -/*********************** p3config ******************************/ - /* Key Functions to be overloaded for Full Configuration */ - virtual RsSerialiser *setupSerialiser(); - virtual bool saveList(bool &cleanup, std::list&); - virtual bool loadList(std::list& load); -/*****************************************************************/ + virtual bool decryptTextFromFile( std::string& text,const std::string& filename); + virtual bool encryptTextToFile (const std::string& text,const std::string& filename); + + bool getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&) = NULL) ; + + //END of PGP public functions + + /* GPG service */ + virtual bool addService(AuthGPGService *service) ; + + protected: + AuthGPG(const std::string& path_to_pubring, const std::string& path_to_secring,const std::string& path_to_trustdb,const std::string& pgp_lock_file); + virtual ~AuthGPG(); + + /*****************************************************************/ + /*********************** p3config ******************************/ + /* Key Functions to be overloaded for Full Configuration */ + virtual RsSerialiser *setupSerialiser(); + virtual bool saveList(bool &cleanup, std::list&); + virtual bool loadList(std::list& load); + /*****************************************************************/ private: - /* SKTAN */ - //void showData(gpgme_data_t dh); - //void createDummyFriends(void); //NYI + /* SKTAN */ + //void showData(gpgme_data_t dh); + //void createDummyFriends(void); //NYI + /* Internal functions */ + bool DoOwnSignature(const void *, unsigned int, void *, unsigned int *); + bool VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const std::string &withfingerprint); - /* Internal functions */ - bool DoOwnSignature(const void *, unsigned int, void *, unsigned int *); - bool VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const std::string &withfingerprint); + /* Sign/Trust stuff */ + int privateSignCertificate(const std::string &id); + int privateRevokeCertificate(const std::string &id); /* revoke the signature on Certificate */ + int privateTrustCertificate(const std::string &id, int trustlvl); - /* Sign/Trust stuff */ - int privateSignCertificate(const std::string &id); - int privateRevokeCertificate(const std::string &id); /* revoke the signature on Certificate */ - int privateTrustCertificate(const std::string &id, int trustlvl); + // store all keys in map mKeyList to avoid calling gpgme exe repeatedly + //bool storeAllKeys(); + //bool storeAllKeys_tick(); - // store all keys in map mKeyList to avoid calling gpgme exe repeatedly - bool storeAllKeys(); - bool storeAllKeys_tick(); + // Not used anymore + // bool updateTrustAllKeys_locked(); -// Not used anymore -// bool updateTrustAllKeys_locked(); + /* GPG service */ + void processServices(); - /* GPG service */ - void processServices(); + bool printAllKeys_locked(); + bool printOwnKeys_locked(); - bool printAllKeys_locked(); - bool printOwnKeys_locked(); + /* own thread */ + virtual void run(); - /* own thread */ - virtual void run(); + private: -private: + static AuthGPG *instance_gpg; // pointeur vers le singleton - static AuthGPG *instance_gpg; // pointeur vers le singleton + RsMutex gpgMtxService; + RsMutex gpgMtxEngine; - RsMutex gpgMtxEngine; - /* Below is protected via the mutex */ + /* Below is protected via the mutex */ - gpgme_engine_info_t INFO; - gpgme_ctx_t CTX; + // gpgme_engine_info_t INFO; + // gpgme_ctx_t CTX; - RsMutex gpgMtxData; - /* Below is protected via the mutex */ + RsMutex gpgMtxData; + /* Below is protected via the mutex */ - certmap mKeyList; - time_t mStoreKeyTime; + time_t mStoreKeyTime; - bool gpgmeInit; + PGPIdType mOwnGpgId; + bool gpgKeySelected; + bool _force_sync_database ; - bool gpgmeKeySelected; - - std::string mOwnGpgId; - gpgcert mOwnGpgCert; + std::list services ; - std::map mAcceptToConnectMap; - - RsMutex gpgMtxService; - /* Below is protected via the mutex */ - - std::list services; -}; - -/*! - * Sign a key - **/ -typedef enum -{ - SIGN_START, - SIGN_COMMAND, - SIGN_UIDS, - SIGN_SET_EXPIRE, - SIGN_SET_CHECK_LEVEL, - SIGN_ENTER_PASSPHRASE, - SIGN_CONFIRM, - SIGN_QUIT, - SIGN_SAVE, - SIGN_ERROR -} SignState; - - -/*! - * Change the key ownertrust - **/ -typedef enum -{ - TRUST_START, - TRUST_COMMAND, - TRUST_VALUE, - TRUST_REALLY_ULTIMATE, - TRUST_QUIT, - TRUST_SAVE, - TRUST_ERROR -} TrustState; - - - -/*! - * This is the generic data object passed to the - * callback function in a gpgme_op_edit operation. - * The contents of this object are modified during - * each callback, to keep track of states, errors - * and other data. - */ -class EditParams -{ - public: - int state; - - /*! - * The return code of gpgme_op_edit() is the return value of - * the last invocation of the callback. But returning an error - * from the callback does not abort the edit operation, so we - * must remember any error. - */ - gpg_error_t err; - - /// Parameters specific to the key operation - void *oParams; - - EditParams(int state, void *oParams) { - this->state = state; - this->err = gpgme_error(GPG_ERR_NO_ERROR); - this->oParams = oParams; - } - -}; - -/*! - * Data specific to key signing - **/ -class SignParams -{ - public: - - std::string checkLvl; - - SignParams(std::string checkLvl) { - this->checkLvl = checkLvl; - } -}; - -/*! - * Data specific to key signing - **/ -class TrustParams -{ - public: - - std::string trustLvl; - - TrustParams(std::string trustLvl) { - this->trustLvl = trustLvl; - } + static AuthGPG *_instance ; }; + /*! + * Sign a key + **/ + typedef enum + { + SIGN_START, + SIGN_COMMAND, + SIGN_UIDS, + SIGN_SET_EXPIRE, + SIGN_SET_CHECK_LEVEL, + SIGN_ENTER_PASSPHRASE, + SIGN_CONFIRM, + SIGN_QUIT, + SIGN_SAVE, + SIGN_ERROR + } SignState; + + + /*! + * Change the key ownertrust + **/ + typedef enum + { + TRUST_START, + TRUST_COMMAND, + TRUST_VALUE, + TRUST_REALLY_ULTIMATE, + TRUST_QUIT, + TRUST_SAVE, + TRUST_ERROR + } TrustState; + + + + /*! + * This is the generic data object passed to the + * callback function in a gpgme_op_edit operation. + * The contents of this object are modified during + * each callback, to keep track of states, errors + * and other data. + */ + class EditParams + { + public: + int state; + + /*! + * The return code of gpgme_op_edit() is the return value of + * the last invocation of the callback. But returning an error + * from the callback does not abort the edit operation, so we + * must remember any error. + */ + gpg_error_t err; + + /// Parameters specific to the key operation + void *oParams; + + EditParams(int state, void *oParams) { + this->state = state; + this->err = gpgme_error(GPG_ERR_NO_ERROR); + this->oParams = oParams; + } + + }; + + /*! + * Data specific to key signing + **/ + class SignParams + { + public: + + std::string checkLvl; + + SignParams(std::string checkLvl) { + this->checkLvl = checkLvl; + } + }; + + /*! + * Data specific to key signing + **/ + class TrustParams + { + public: + + std::string trustLvl; + + TrustParams(std::string trustLvl) { + this->trustLvl = trustLvl; + } + }; + #endif + diff --git a/libretroshare/src/pqi/authssl.cc b/libretroshare/src/pqi/authssl.cc index 6c0e6cc2b..d610c5068 100644 --- a/libretroshare/src/pqi/authssl.cc +++ b/libretroshare/src/pqi/authssl.cc @@ -204,7 +204,10 @@ void setAuthSSL(AuthSSL *newssl) void AuthSSLInit() { - instance_ssl = new AuthSSLimpl(); + if (instance_ssl == NULL) + { + instance_ssl = new AuthSSLimpl(); + } } AuthSSL *AuthSSL::getAuthSSL() diff --git a/libretroshare/src/pqi/cleanupxpgp.cc b/libretroshare/src/pqi/cleanupxpgp.cc index c2ed8858c..16298eee3 100644 --- a/libretroshare/src/pqi/cleanupxpgp.cc +++ b/libretroshare/src/pqi/cleanupxpgp.cc @@ -287,7 +287,18 @@ std::string cleanUpCertificate(const std::string& badCertificate,int& error_code while(currBadCertIdx < endCertStartIdx1 && (badCertificate[currBadCertIdx] == '=' || badCertificate[currBadCertIdx] == ' ' || badCertificate[currBadCertIdx] == '\n' )) currBadCertIdx++ ; - cleanCertificate += "==\n="; + switch(cntPerLine % 4) + { + case 0: break ; + case 1: std::cerr<<"Certificate corrupted beyond repair: wrongnumber of chars on last line (n%4=1)"< &gpg_ids) = 0; diff --git a/libretroshare/src/rsserver/p3face-config.cc b/libretroshare/src/rsserver/p3face-config.cc index 4903bbd5b..9624a43e4 100644 --- a/libretroshare/src/rsserver/p3face-config.cc +++ b/libretroshare/src/rsserver/p3face-config.cc @@ -190,5 +190,5 @@ void RsServer::rsGlobalShutDown() mBlogs->join(); #endif - AuthGPGExit(); + AuthGPG::exit(); } diff --git a/libretroshare/src/rsserver/p3peers.cc b/libretroshare/src/rsserver/p3peers.cc index 474c96571..b9d08e432 100644 --- a/libretroshare/src/rsserver/p3peers.cc +++ b/libretroshare/src/rsserver/p3peers.cc @@ -595,6 +595,10 @@ bool p3Peers::getPeerDetails(const std::string &id, RsPeerDetails &d) } #endif +bool p3Peers::isKeySupported(const std::string& id) +{ + return AuthGPG::getAuthGPG()->isKeySupported(id); +} std::string p3Peers::getGPGName(const std::string &gpg_id) { diff --git a/libretroshare/src/rsserver/p3peers.h b/libretroshare/src/rsserver/p3peers.h index 3367440d7..5bcf36403 100644 --- a/libretroshare/src/rsserver/p3peers.h +++ b/libretroshare/src/rsserver/p3peers.h @@ -63,6 +63,7 @@ virtual bool getPeerDetails(const std::string &ssl_or_gpg_id, RsPeerDetails &d); /* Using PGP Ids */ virtual std::string getGPGOwnId(); virtual std::string getGPGId(const std::string &ssl_id); +virtual bool isKeySupported(const std::string& ids); virtual bool getGPGAcceptedList(std::list &ids); virtual bool getGPGSignedList(std::list &ids); virtual bool getGPGValidList(std::list &ids); diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 5a1881437..b3f782048 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -92,11 +92,7 @@ class RsInitConfig /* for certificate creation */ //static std::string gpgPasswd; -#ifndef WINDOWS_SYS - static int lockHandle; -#else - static HANDLE lockHandle; -#endif + static rs_lock_handle_t lockHandle; /* These fields are needed for login */ static std::string loginId; @@ -151,11 +147,7 @@ static const int SSLPWD_LEN = 64; std::list RsInitConfig::accountIds; std::string RsInitConfig::preferedId; -#ifndef WINDOWS_SYS - int RsInitConfig::lockHandle; -#else - HANDLE RsInitConfig::lockHandle; -#endif +rs_lock_handle_t RsInitConfig::lockHandle; std::string RsInitConfig::configDir; std::string RsInitConfig::load_cert; @@ -197,7 +189,7 @@ bool RsInitConfig::udpListenerOnly; /* Uses private class - so must be hidden */ -static bool getAvailableAccounts(std::list &ids); +static bool getAvailableAccounts(std::list &ids,int& failing_accounts); static bool checkAccount(std::string accountdir, accountId &id); static std::string toUpperCase(const std::string& s) @@ -253,7 +245,7 @@ void RsInit::InitRsConfig() #ifdef WINDOWS_SYS // test for portable version - if (GetFileAttributes (L"gpg.exe") != (DWORD) -1 && GetFileAttributes (L"gpgme-w32spawn.exe") != (DWORD) -1) { + if (GetFileAttributes(L"portable") != (DWORD) -1) { // use portable version RsInitConfig::portable = true; } @@ -611,9 +603,7 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck */ /* create singletons */ AuthSSLInit(); - AuthGPGInit(); - - AuthSSL::getAuthSSL() -> InitAuth(NULL, NULL, NULL); + AuthSSL::getAuthSSL() -> InitAuth(NULL, NULL, NULL); // first check config directories, and set bootstrap values. if(!setupBaseDir()) @@ -621,17 +611,31 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck get_configinit(RsInitConfig::basedir, RsInitConfig::preferedId); + std::string pgp_dir = RsInitConfig::basedir + "/pgp" ; + if(!RsDirUtil::checkCreateDirectory(pgp_dir)) + throw std::runtime_error("Cannot create pgp directory " + pgp_dir) ; + + AuthGPG::init( pgp_dir + "/retroshare_public_keyring.gpg", + pgp_dir + "/retroshare_secret_keyring.gpg", + pgp_dir + "/retroshare_trustdb.gpg", + pgp_dir + "/lock"); + /* Initialize AuthGPG */ - if (AuthGPG::getAuthGPG()->InitAuth() == false) { - std::cerr << "AuthGPG::InitAuth failed" << std::endl; - return RS_INIT_AUTH_FAILED; - } + // if (AuthGPG::getAuthGPG()->InitAuth() == false) { + // std::cerr << "AuthGPG::InitAuth failed" << std::endl; + // return RS_INIT_AUTH_FAILED; + // } //std::list ids; std::list::iterator it; - getAvailableAccounts(RsInitConfig::accountIds); + int failing_accounts ; - // if a different user id has been passed to cmd line check for that instead + getAvailableAccounts(RsInitConfig::accountIds,failing_accounts); + + if(failing_accounts > 0 && RsInitConfig::accountIds.empty()) + return RS_INIT_NO_KEYRING ; + + // if a different user id has been passed to cmd line check for that instead std::string lower_case_user_string = toLowerCase(prefUserString) ; std::string upper_case_user_string = toUpperCase(prefUserString) ; @@ -669,8 +673,9 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck { std::cerr << " * Preferred * " << std::endl; userId = it->sslId; - userName = it->pgpName; + userName = it->pgpName; existingUser = true; + break; } } if (!existingUser) @@ -702,6 +707,54 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck /**************************** Access Functions for Init Data **************************/ +bool RsInit::exportIdentity(const std::string& fname,const std::string& id) +{ + return AuthGPG::getAuthGPG()->exportProfile(fname,id); +} + +bool RsInit::importIdentity(const std::string& fname,std::string& id,std::string& import_error) +{ + return AuthGPG::getAuthGPG()->importProfile(fname,id,import_error); +} + +bool RsInit::copyGnuPGKeyrings() +{ + std::string pgp_dir = RsInitConfig::basedir + "/pgp" ; + if(!RsDirUtil::checkCreateDirectory(pgp_dir)) + throw std::runtime_error("Cannot create pgp directory " + pgp_dir) ; + + std::string source_public_keyring; + std::string source_secret_keyring; + +#ifdef WINDOWS_SYS + if (RsInit::isPortable()) + { + source_public_keyring = RsInit::RsConfigDirectory() + "/gnupg/pubring.gpg"; + source_secret_keyring = RsInit::RsConfigDirectory() + "/gnupg/secring.gpg" ; + } else { + source_public_keyring = RsInitConfig::basedir + "/../gnupg/pubring.gpg" ; + source_secret_keyring = RsInitConfig::basedir + "/../gnupg/secring.gpg" ; + } +#else + // We need a specific part for MacOS and Linux as well + source_public_keyring = RsInitConfig::basedir + "/../.gnupg/pubring.gpg" ; + source_secret_keyring = RsInitConfig::basedir + "/../.gnupg/secring.gpg" ; +#endif + + if(!RsDirUtil::copyFile(source_public_keyring,pgp_dir + "/retroshare_public_keyring.gpg")) + { + std::cerr << "Cannot copy pub keyring " << source_public_keyring << " to destination file " << pgp_dir + "/retroshare_public_keyring.pgp" << std::endl; + return false ; + } + if(!RsDirUtil::copyFile(source_secret_keyring,pgp_dir + "/retroshare_secret_keyring.gpg")) + { + std::cerr << "Cannot copy sec keyring " << source_secret_keyring << " to destination file " << pgp_dir + "/retroshare_secret_keyring.pgp" << std::endl; + return false ; + } + + return true ; +} + bool RsInit::getPreferedAccountId(std::string &id) { id = RsInitConfig::preferedId; @@ -939,8 +992,9 @@ std::string RsInit::getRetroshareDataDirectory() /* directories with valid certificates in the expected location */ -bool getAvailableAccounts(std::list &ids) +bool getAvailableAccounts(std::list &ids,int& failing_accounts) { + failing_accounts = 0 ; /* get the directories */ std::list directories; std::list::iterator it; @@ -1023,6 +1077,8 @@ bool getAvailableAccounts(std::list &ids) #endif ids.push_back(tmpId); } + else + ++failing_accounts ; } return true; } @@ -1046,35 +1102,39 @@ static bool checkAccount(std::string accountdir, accountId &id) std::string cert_name = basename + "_cert.pem"; std::string userName, userId; - #ifdef AUTHSSL_DEBUG +#ifdef AUTHSSL_DEBUG std::cerr << "checkAccount() dir: " << accountdir << std::endl; - #endif - +#endif bool ret = false; /* check against authmanagers private keys */ - if (LoadCheckX509(cert_name.c_str(), id.pgpId, id.location, id.sslId)) - { - #ifdef AUTHSSL_DEBUG - std::cerr << "location: " << id.location << " id: " << id.sslId << std::endl; - #endif + if (LoadCheckX509(cert_name.c_str(), id.pgpId, id.location, id.sslId)) + { +#ifdef AUTHSSL_DEBUG + std::cerr << "location: " << id.location << " id: " << id.sslId << std::endl; + std::cerr << "issuerName: " << id.pgpId << " id: " << id.sslId << std::endl; +#endif + if(! RsInit::GetPGPLoginDetails(id.pgpId, id.pgpName, id.pgpEmail)) + return false ; - #ifdef GPG_DEBUG - std::cerr << "issuerName: " << id.pgpId << " id: " << id.sslId << std::endl; - #endif - RsInit::GetPGPLoginDetails(id.pgpId, id.pgpName, id.pgpEmail); - #ifdef GPG_DEBUG - std::cerr << "PGPLoginDetails: " << id.pgpId << " name: " << id.pgpName; - std::cerr << " email: " << id.pgpEmail << std::endl; - #endif - ret = true; - } - else - { - std::cerr << "GetIssuerName FAILED!" << std::endl; - ret = false; - } + if(!AuthGPG::getAuthGPG()->isKeySupported(id.pgpId)) + return false ; + + if(!AuthGPG::getAuthGPG()->haveSecretKey(id.pgpId)) + return false ; + +#ifdef GPG_DEBUG + std::cerr << "PGPLoginDetails: " << id.pgpId << " name: " << id.pgpName; + std::cerr << " email: " << id.pgpEmail << std::endl; +#endif + ret = true; + } + else + { + std::cerr << "GetIssuerName FAILED!" << std::endl; + ret = false; + } return ret; } @@ -1101,8 +1161,14 @@ int RsInit::GetPGPLoginDetails(const std::string& id, std::string &name, st std::cerr << "RsInit::GetPGPLoginDetails for \"" << id << "\"" << std::endl; #endif - name = AuthGPG::getAuthGPG()->getGPGName(id); - email = AuthGPG::getAuthGPG()->getGPGEmail(id); + bool ok = true ; + name = AuthGPG::getAuthGPG()->getGPGName(id,&ok); + if(!ok) + return 0 ; + email = AuthGPG::getAuthGPG()->getGPGEmail(id,&ok); + if(!ok) + return 0 ; + if (name != "") { return 1; } else { @@ -1123,75 +1189,9 @@ int RsInit::GetPGPLoginDetails(const std::string& id, std::string &name, st int RsInit::LockConfigDirectory(const std::string& accountDir, std::string& lockFilePath) { const std::string lockFile = accountDir + "/" + "lock"; - lockFilePath = lockFile; -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ -#ifndef WINDOWS_SYS - if(RsInitConfig::lockHandle != -1) - close(RsInitConfig::lockHandle); - // open the file in write mode, create it if necessary, truncate it (it should be empty) - RsInitConfig::lockHandle = open(lockFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - if(RsInitConfig::lockHandle == -1) - { - std::cerr << "Could not open lock file " << lockFile.c_str() << std::flush; - perror(NULL); - return 2; - } - - // see "man fcntl" for the details, in short: non blocking lock creation on the whole file contents - struct flock lockDetails; - lockDetails.l_type = F_WRLCK; - lockDetails.l_whence = SEEK_SET; - lockDetails.l_start = 0; - lockDetails.l_len = 0; - - if(fcntl(RsInitConfig::lockHandle, F_SETLK, &lockDetails) == -1) - { - int fcntlErr = errno; - std::cerr << "Could not request lock on file " << lockFile.c_str() << std::flush; - perror(NULL); - - // there's no lock so let's release the file handle immediately - close(RsInitConfig::lockHandle); - RsInitConfig::lockHandle = -1; - - if(fcntlErr == EACCES || fcntlErr == EAGAIN) - return 1; - else - return 2; - } - - return 0; -#else - if (RsInitConfig::lockHandle) { - CloseHandle(RsInitConfig::lockHandle); - } - - std::wstring wlockFile; - librs::util::ConvertUtf8ToUtf16(lockFile, wlockFile); - - // open the file in write mode, create it if necessary - RsInitConfig::lockHandle = CreateFile(wlockFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); - - if (RsInitConfig::lockHandle == INVALID_HANDLE_VALUE) { - DWORD lasterror = GetLastError(); - - std::cerr << "Could not open lock file " << lockFile.c_str() << std::endl; - std::cerr << "Last error: " << lasterror << std::endl << std::flush; - perror(NULL); - - if (lasterror == ERROR_SHARING_VIOLATION || lasterror == ERROR_ACCESS_DENIED) { - return 1; - } - return 2; - } - - return 0; -#endif -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ + return RsDirUtil::createLockFile(lockFile,RsInitConfig::lockHandle) ; } /* @@ -1200,21 +1200,7 @@ int RsInit::LockConfigDirectory(const std::string& accountDir, std::string& lock */ void RsInit::UnlockConfigDirectory() { -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ -#ifndef WINDOWS_SYS - if(RsInitConfig::lockHandle != -1) - { - close(RsInitConfig::lockHandle); - RsInitConfig::lockHandle = -1; - } -#else - if(RsInitConfig::lockHandle) - { - CloseHandle(RsInitConfig::lockHandle); - RsInitConfig::lockHandle = NULL; - } -#endif -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ + RsDirUtil::releaseLockFile(RsInitConfig::lockHandle) ; } @@ -2101,7 +2087,7 @@ int RsServer::StartupRetroShare() /****** New Ft Server **** !!! */ ftserver = new ftServer(mPeerMgr, mLinkMgr); - ftserver->setP3Interface(pqih); + ftserver->setP3Interface(pqih); ftserver->setConfigDirectory(RsInitConfig::configDir); ftserver->SetupFtServer(&(getNotify())); @@ -2299,7 +2285,7 @@ int RsServer::StartupRetroShare() //mConfigMgr->addConfiguration("ftserver.cfg", ftserver); // - mConfigMgr->addConfiguration("gpg_prefs.cfg", (AuthGPGimpl *) AuthGPG::getAuthGPG()); + mConfigMgr->addConfiguration("gpg_prefs.cfg", AuthGPG::getAuthGPG()); mConfigMgr->loadConfiguration(); mConfigMgr->addConfiguration("peers.cfg", mPeerMgr); diff --git a/libretroshare/src/rsserver/rsloginhandler.cc b/libretroshare/src/rsserver/rsloginhandler.cc index 10ef54674..432c1df5f 100644 --- a/libretroshare/src/rsserver/rsloginhandler.cc +++ b/libretroshare/src/rsserver/rsloginhandler.cc @@ -635,23 +635,25 @@ bool RsLoginHandler::checkAndStoreSSLPasswdIntoGPGFile(const std::string& ssl_id return true ; } - sslPassphraseFile = RsDirUtil::rs_fopen(getSSLPasswdFileName(ssl_id).c_str(), "w"); + // sslPassphraseFile = RsDirUtil::rs_fopen(getSSLPasswdFileName(ssl_id).c_str(), "w"); - if(sslPassphraseFile == NULL) - { - std::cerr << "RsLoginHandler::storeSSLPasswdIntoGPGFile(): could not write to file " << getSSLPasswdFileName(ssl_id) << std::endl; - return false ; - } - else - std::cerr << "openned sslPassphraseFile : " << getSSLPasswdFileName(ssl_id) << std::endl; - - gpgme_data_t cipher; - gpgme_data_t plain; - gpgme_data_new_from_mem(&plain, ssl_passwd.c_str(), ssl_passwd.length(), 1); - gpgme_data_new_from_stream (&cipher, sslPassphraseFile); + // if(sslPassphraseFile == NULL) + // { + // std::cerr << "RsLoginHandler::storeSSLPasswdIntoGPGFile(): could not write to file " << getSSLPasswdFileName(ssl_id) << std::endl; + // return false ; + // } + // else + // std::cerr << "openned sslPassphraseFile : " << getSSLPasswdFileName(ssl_id) << std::endl; + // + // gpgme_data_t cipher; + // gpgme_data_t plain; + // gpgme_data_new_from_mem(&plain, ssl_passwd.c_str(), ssl_passwd.length(), 1); + // gpgme_data_new_from_stream (&cipher, sslPassphraseFile); bool ok ; - if (0 < AuthGPG::getAuthGPG()->encryptText(plain, cipher)) + std::string cipher ; + + if(AuthGPG::getAuthGPG()->encryptTextToFile(ssl_passwd, getSSLPasswdFileName(ssl_id))) { std::cerr << "Encrypting went ok !" << std::endl; ok= true ; @@ -662,10 +664,9 @@ bool RsLoginHandler::checkAndStoreSSLPasswdIntoGPGFile(const std::string& ssl_id ok= false ; } - gpgme_data_release (cipher); - gpgme_data_release (plain); - - fclose(sslPassphraseFile); + // gpgme_data_release (cipher); + // gpgme_data_release (plain); + // fclose(sslPassphraseFile); return ok ; } @@ -682,34 +683,40 @@ bool RsLoginHandler::getSSLPasswdFromGPGFile(const std::string& ssl_id,std::stri std::cerr << "No password provided, and no sslPassphraseFile : " << getSSLPasswdFileName(ssl_id).c_str() << std::endl; return 0; } + fclose(sslPassphraseFile); std::cerr << "opening sslPassphraseFile : " << getSSLPasswdFileName(ssl_id).c_str() << std::endl; - gpgme_data_t cipher; - gpgme_data_t plain; - gpgme_data_new (&plain); +// gpgme_data_t cipher; +// gpgme_data_t plain; +// gpgme_data_new (&plain); - if( gpgme_data_new_from_stream (&cipher, sslPassphraseFile) != GPG_ERR_NO_ERROR) - { - std::cerr << "Error while creating stream from ssl passwd file." << std::endl ; - return 0 ; - } - if (0 < AuthGPG::getAuthGPG()->decryptText(cipher, plain)) +// if( gpgme_data_new_from_stream (&cipher, sslPassphraseFile) != GPG_ERR_NO_ERROR) +// { +// std::cerr << "Error while creating stream from ssl passwd file." << std::endl ; +// return 0 ; +// } + + std::string plain ; + + if (AuthGPG::getAuthGPG()->decryptTextFromFile(plain,getSSLPasswdFileName(ssl_id))) { std::cerr << "Decrypting went ok !" << std::endl; - gpgme_data_write (plain, "", 1); - sslPassword = std::string(gpgme_data_release_and_get_mem(plain, NULL)); +// gpgme_data_write (plain, "", 1); +// sslPassword = std::string(gpgme_data_release_and_get_mem(plain, NULL)); + sslPassword = plain ; std::cerr << "sslpassword: " << "********************" << std::endl; - gpgme_data_release (cipher); - fclose(sslPassphraseFile); + std::cerr << "sslpassword: \"" << sslPassword << "\"" << std::endl; +// gpgme_data_release (cipher); +// fclose(sslPassphraseFile); return true ; } else { - gpgme_data_release (plain); - gpgme_data_release (cipher); - fclose(sslPassphraseFile); +// gpgme_data_release (plain); +// gpgme_data_release (cipher); +// fclose(sslPassphraseFile); sslPassword = "" ; std::cerr << "Error : decrypting went wrong !" << std::endl; diff --git a/libretroshare/src/services/p3disc.cc b/libretroshare/src/services/p3disc.cc index af972a284..5fea4f9a0 100644 --- a/libretroshare/src/services/p3disc.cc +++ b/libretroshare/src/services/p3disc.cc @@ -861,28 +861,6 @@ void p3disc::recvDiscReply(RsDiscReply *dri) void p3disc::removeFriend(std::string /*ssl_id*/) { - -// DON'T KNOW WHY SSL IDS were saved -> the results are never used -#if 0 - -#ifdef P3DISC_DEBUG - std::cerr << "p3disc::removeFriend() called for : " << ssl_id << std::endl; -#endif - //if we deleted the gpg_id, don't store the friend deletion as if we add back the gpg_id, we won't have the ssl friends back - std::string gpg_id = rsPeers->getGPGId(ssl_id); -#ifdef P3DISC_DEBUG - std::cerr << "p3disc::removeFriend() gpg_id : " << gpg_id << std::endl; -#endif - if (gpg_id == AuthGPG::getAuthGPG()->getGPGOwnId() || rsPeers->isGPGAccepted(rsPeers->getGPGId(ssl_id))) { -#ifdef P3DISC_DEBUG - std::cerr << "p3disc::removeFriend() storing the friend deletion." << ssl_id << std::endl; -#endif - deletedSSLFriendsIds[ssl_id] = time(NULL);//just keep track of the deleted time - IndicateConfigChanged(); - } - -#endif - } /*************************************************************************************/ diff --git a/libretroshare/src/tests/pgp/Makefile b/libretroshare/src/tests/pgp/Makefile new file mode 100644 index 000000000..70b5d2570 --- /dev/null +++ b/libretroshare/src/tests/pgp/Makefile @@ -0,0 +1,32 @@ + +RS_TOP_DIR = ../.. +DHT_TOP_DIR = ../../../../libbitdht/src +OPENPGP_INCLUDE_DIR = ../../../../openpgpsdk/src + +##### Define any flags that are needed for this section ####### +############################################################### + +############################################################### +include $(RS_TOP_DIR)/tests/scripts/config.mk +############################################################### + +TESTOBJ = test_pgp_handler.o test_pgp_signature_parsing.o test_key_parsing.o +TESTS = test_pgp_handler test_pgp_signature_parsing test_key_parsing + +#rsbaseitem_test + +all: tests + +test_pgp_handler : test_pgp_handler.o + $(CC) $(CFLAGS) -o test_pgp_handler test_pgp_handler.o $(OBJ) $(LIBS) -L../../../../openpgpsdk/src/lib/ -lops -lbz2 + +test_pgp_signature_parsing : test_pgp_signature_parsing.o + $(CC) $(CFLAGS) -o test_pgp_signature_parsing test_pgp_signature_parsing.o $(OBJ) $(LIBS) -L../../../../openpgpsdk/src/lib/ -lops -lbz2 + +test_key_parsing : test_key_parsing.o + $(CC) $(CFLAGS) -o test_key_parsing test_key_parsing.o ../../../../openpgpsdk/src/lib/libops.a -lssl -lcrypto -lbz2 + +############################################################### +include $(RS_TOP_DIR)/scripts/rules.mk +############################################################### + diff --git a/libretroshare/src/tests/pgp/argstream.h b/libretroshare/src/tests/pgp/argstream.h new file mode 100644 index 000000000..137856794 --- /dev/null +++ b/libretroshare/src/tests/pgp/argstream.h @@ -0,0 +1,814 @@ +/* Copyright (C) 2004 Xavier Décoret +* + * argsteam is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Foobar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ARGSTREAM_H +#define ARGSTREAM_H + + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + class argstream; + + template + class ValueHolder; + + template + argstream& operator>> (argstream&, const ValueHolder&); + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of ValueHolder + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + template + class ValueHolder + { + public: + ValueHolder(char s, + const char* l, + T& b, + const char* desc, + bool mandatory); + ValueHolder(const char* l, + T& b, + const char* desc, + bool mandatory); + ValueHolder(char s, + T& b, + const char* desc, + bool mandatory); + friend argstream& operator>><>(argstream& s,const ValueHolder& v); + std::string name() const; + std::string description() const; + private: + std::string shortName_; + std::string longName_; + T* value_; + T initialValue_; + std::string description_; + bool mandatory_; + }; + template + inline ValueHolder + parameter(char s, + const char* l, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(s,l,b,desc,mandatory); + } + template + inline ValueHolder + parameter(char s, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(s,b,desc,mandatory); + } + template + inline ValueHolder + parameter(const char* l, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(l,b,desc,mandatory); + } + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of OptionHolder + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + class OptionHolder + { + public: + inline OptionHolder(char s, + const char* l, + bool& b, + const char* desc); + inline OptionHolder(const char* l, + bool& b, + const char* desc); + inline OptionHolder(char s, + bool& b, + const char* desc); + friend argstream& operator>>(argstream& s,const OptionHolder& v); + inline std::string name() const; + inline std::string description() const; + protected: + inline OptionHolder(char s, + const char* l, + const char* desc); + friend OptionHolder help(char s='h', + const char* l="help", + const char* desc="Display this help"); + private: + std::string shortName_; + std::string longName_; + bool* value_; + std::string description_; + }; + inline OptionHolder + option(char s, + const char* l, + bool& b, + const char* desc="") + { + return OptionHolder(s,l,b,desc); + } + inline OptionHolder + option(char s, + bool& b, + const char* desc="") + { + return OptionHolder(s,b,desc); + } + inline OptionHolder + option(const char* l, + bool& b, + const char* desc="") + { + return OptionHolder(l,b,desc); + } + inline OptionHolder + help(char s, + const char* l, + const char* desc) + { + return OptionHolder(s,l,desc); + } + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of ValuesHolder + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + template + class ValuesHolder + { + public: + ValuesHolder(const O& o, + const char* desc, + int len); + template friend argstream& operator>>(argstream& s,const ValuesHolder& v); + std::string name() const; + std::string description() const; + typedef T value_type; + private: + mutable O value_; + std::string description_; + int len_; + char letter_; + }; + template + inline ValuesHolder + values(const O& o, + const char* desc="", + int len=-1) + { + return ValuesHolder(o,desc,len); + } + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of ValueParser + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + template + class ValueParser + { + public: + inline T operator()(const std::string& s) const + { + std::istringstream is(s); + T t; + is>>t; + return t; + } + }; + // We need to specialize for string otherwise parsing of a value that + // contains space (for example a string with space passed in quotes on the + // command line) would parse only the first element of the value!!! + template <> + class ValueParser + { + public: + inline std::string operator()(const std::string& s) const + { + return s; + } + }; + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of argstream + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + class argstream + { + public: + inline argstream(int argc,char** argv); + inline argstream(const char* c); + template + friend argstream& operator>>(argstream& s,const ValueHolder& v); + friend inline argstream& operator>>(argstream& s,const OptionHolder& v); + template + friend argstream& operator>>(argstream& s,const ValuesHolder& v); + + inline bool helpRequested() const; + inline bool isOk() const; + inline std::string errorLog() const; + inline std::string usage() const; + inline void defaultErrorHandling(bool ignoreUnused=false) const; + static inline char uniqueLetter(); + protected: + void parse(int argc,char** argv); + private: + typedef std::list::iterator value_iterator; + typedef std::pair help_entry; + std::string progName_; + std::map options_; + std::list values_; + bool minusActive_; + bool isOk_; + std::deque argHelps_; + std::string cmdLine_; + std::deque errors_; + bool helpRequested_; + }; + //************************************************************ + // Implementation of ValueHolder + //************************************************************ + template + ValueHolder::ValueHolder(char s, + const char* l, + T& v, + const char* desc, + bool mandatory) + : shortName_(1,s), + longName_(l), + value_(&v), + initialValue_(v), + description_(desc), + mandatory_(mandatory) + { + } + template + ValueHolder::ValueHolder(const char* l, + T& v, + const char* desc, + bool mandatory) + : longName_(l), + value_(&v), + initialValue_(v), + description_(desc), + mandatory_(mandatory) + { + } + template + ValueHolder::ValueHolder(char s, + T& v, + const char* desc, + bool mandatory) + : shortName_(1,s), + value_(&v), + initialValue_(v), + description_(desc), + mandatory_(mandatory) + { + } + template + std::string + ValueHolder::name() const + { + std::ostringstream os; + if (!shortName_.empty()) os<<'-'< + std::string + ValueHolder::description() const + { + std::ostringstream os; + os< + //************************************************************ + template + ValuesHolder::ValuesHolder(const O& o, + const char* desc, + int len) + : value_(o), + description_(desc), + len_(len) + { + letter_ = argstream::uniqueLetter(); + } + template + std::string + ValuesHolder::name() const + { + std::ostringstream os; + os< + std::string + ValuesHolder::description() const + { + return description_; + } + //************************************************************ + // Implementation of argstream + //************************************************************ + inline + argstream::argstream(int argc,char** argv) + : progName_(argv[0]), + minusActive_(true), + isOk_(true) + { + parse(argc,argv); + } + inline + argstream::argstream(const char* c) + : progName_(""), + minusActive_(true), + isOk_(true) + { + std::string s(c); + // Build argc, argv from s. We must add a dummy first element for + // progName because parse() expects it!! + std::deque args; + args.push_back(""); + std::istringstream is(s); + while (is.good()) + { + std::string t; + is>>t; + args.push_back(t); + } + char* pargs[args.size()]; + char** p = pargs; + for (std::deque::const_iterator + iter = args.begin(); + iter != args.end();++iter) + { + *p++ = const_cast(iter->c_str()); + } + parse(args.size(),pargs); + } + inline void + argstream::parse(int argc,char** argv) + { + // Run thru all arguments. + // * it has -- in front : it is a long name option, if remainder is empty, + // it is an error + // * it has - in front : it is a sequence of short name options, if + // remainder is empty, deactivates option (- will + // now be considered a char). + // * if any other char, or if option was deactivated + // : it is a value. Values are split in parameters + // (immediately follow an option) and pure values. + // Each time a value is parsed, if the previously parsed argument was an + // option, then the option is linked to the value in case of it is a + // option with parameter. The subtle point is that when several options + // are given with short names (ex: -abc equivalent to -a -b -c), the last + // parsed option is -c). + // Since we use map for option, any successive call overides the previous + // one: foo -a -b -a hello is equivalent to foo -b -a hello + // For values it is not true since we might have several times the same + // value. + value_iterator* lastOption = NULL; + for (char** a = argv,**astop=a+argc;++a!=astop;) + { + std::string s(*a); + if (minusActive_ && s[0] == '-') + { + if (s.size() > 1 && s[1] == '-') + { + if (s.size() == 2) + { + minusActive_ = false; + continue; + } + lastOption = &(options_[s.substr(2)] = values_.end()); + } + else + { + if (s.size() > 1) + { + // Parse all chars, if it is a minus we have an error + for (std::string::const_iterator cter = s.begin(); + ++cter != s.end();) + { + if (*cter == '-') + { + isOk_ = false; + std::ostringstream os; + os<<"- in the middle of a switch "<::const_iterator + iter = options_.begin();iter != options_.end();++iter) + { + std::cout<<"DEBUG: option "<first; + if (iter->second != values_.end()) + { + std::cout<<" -> "<<*(iter->second); + } + std::cout<::const_iterator + iter = values_.begin();iter != values_.end();++iter) + { + std::cout<<"DEBUG: value "<<*iter<::const_iterator + iter = argHelps_.begin();iter != argHelps_.end();++iter) + { + if (lmaxfirst.size()) lmax = iter->first.size(); + } + for (std::deque::const_iterator + iter = argHelps_.begin();iter != argHelps_.end();++iter) + { + os<<'\t'<first<first.size(),' ') + <<" : "<second<<'\n'; + } + return os.str(); + } + inline std::string + argstream::errorLog() const + { + std::string s; + for(std::deque::const_iterator iter = errors_.begin(); + iter != errors_.end();++iter) + { + s += *iter; + s += '\n'; + } + return s; + } + inline char + argstream::uniqueLetter() + { + static unsigned int c = 'a'; + return c++; + } + template + argstream& + operator>>(argstream& s,const ValueHolder& v) + { + // Search in the options if there is any such option defined either with a + // short name or a long name. If both are found, only the last one is + // used. +#ifdef ARGSTREAM_DEBUG + std::cout<<"DEBUG: searching "<::iterator iter = + s.options_.find(v.shortName_); + if (iter == s.options_.end()) + { + iter = s.options_.find(v.longName_); + } + if (iter != s.options_.end()) + { + // If we find counterpart for value holder on command line, either it + // has an associated value in which case we assign it, or it has not, in + // which case we have an error. + if (iter->second != s.values_.end()) + { +#ifdef ARGSTREAM_DEBUG + std::cout<<"DEBUG: found value "<<*(iter->second)< p; + *(v.value_) = p(*(iter->second)); + // The option and its associated value are removed, the subtle thing + // is that someother options might have this associated value too, + // which we must invalidate. + s.values_.erase(iter->second); + for (std::map::iterator + jter = s.options_.begin();jter != s.options_.end();++jter) + { + if (jter->second == iter->second) + { + jter->second = s.values_.end(); + } + } + s.options_.erase(iter); + } + else + { + s.isOk_ = false; + std::ostringstream os; + os<<"No value following switch "<first + <<" on command line"; + s.errors_.push_back(os.str()); + } + } + else + { + if (v.mandatory_) + { + s.isOk_ = false; + std::ostringstream os; + os<<"Mandatory parameter "; + if (!v.shortName_.empty()) os<<'-'<>(argstream& s,const OptionHolder& v) + { + // Search in the options if there is any such option defined either with a + // short name or a long name. If both are found, only the last one is + // used. +#ifdef ARGSTREAM_DEBUG + std::cout<<"DEBUG: searching "<::iterator iter = + s.options_.find(v.shortName_); + if (iter == s.options_.end()) + { + iter = s.options_.find(v.longName_); + } + if (iter != s.options_.end()) + { + // If we find counterpart for value holder on command line then the + // option is true and if an associated value was found, it is ignored + if (v.value_ != NULL) + { + *(v.value_) = true; + } + else + { + s.helpRequested_ = true; + } + // The option only is removed + s.options_.erase(iter); + } + else + { + if (v.value_ != NULL) + { + *(v.value_) = false; + } + else + { + s.helpRequested_ = false; + } + } + return s; + } + template + argstream& + operator>>(argstream& s,const ValuesHolder& v) + { + s.argHelps_.push_back(argstream::help_entry(v.name(),v.description())); + { + std::ostringstream os; + os<<' '<::iterator first = s.values_.begin(); + // We add to the iterator as much values as we can, limited to the length + // specified (if different of -1) + int n = v.len_ != -1?v.len_:s.values_.size(); + while (first != s.values_.end() && n-->0) + { + // Read the value from the string *first + ValueParser p; + *(v.value_++) = p(*first ); + s.argHelps_.push_back(argstream::help_entry(v.name(),v.description())); + // The value we just removed was maybe "remembered" by an option so we + // remove it now. + for (std::map::iterator + jter = s.options_.begin();jter != s.options_.end();++jter) + { + if (jter->second == first) + { + jter->second = s.values_.end(); + } + } + ++first; + } + // Check if we have enough values + if (n != 0) + { + s.isOk_ = false; + std::ostringstream os; + os<<"Expecting "< +#include + +extern "C" +{ + #include + #include + #include +} + +#include "argstream.h" + +int main(int argc,char *argv[]) +{ + try + { + // test PGPHandler + // + // 0 - init + + bool armoured = false ; + std::string keyfile ; + + argstream as(argc,argv) ; + + as >> parameter('i',"input-key",keyfile,"input key file.",true) + >> option('a',"armoured",armoured,"input is armoured") + >> help() ; + + as.defaultErrorHandling() ; + + ops_keyring_t *kr = (ops_keyring_t*)malloc(sizeof(ops_keyring_t)) ; + kr->nkeys = 0 ; + kr->nkeys_allocated = 0 ; + kr->keys = 0 ; + + if(ops_false == ops_keyring_read_from_file(kr,armoured, keyfile.c_str())) + throw std::runtime_error("PGPHandler::readKeyRing(): cannot read key file. File corrupted, or missing/superfluous armour parameter.") ; + + for(int i=0;inkeys;++i) + { + ops_print_public_keydata(&kr->keys[i]) ; + ops_print_public_keydata_verbose(&kr->keys[i]) ; + ops_print_public_key(&kr->keys[i].key.pkey) ; + } + + ops_list_packets(const_cast(keyfile.c_str()),armoured,kr,NULL) ; + + return 0 ; + } + catch(std::exception& e) + { + std::cerr << "Caught exception: " << e.what() << std::endl; + return 1 ; + } +} + + diff --git a/libretroshare/src/tests/pgp/test_pgp_handler.cc b/libretroshare/src/tests/pgp/test_pgp_handler.cc new file mode 100644 index 000000000..96a4037c8 --- /dev/null +++ b/libretroshare/src/tests/pgp/test_pgp_handler.cc @@ -0,0 +1,155 @@ +// COMPILE_LINE: g++ -o test_pgp_handler test_pgp_handler.cc -I../../../openpgpsdk/include -I../ -L../lib -lretroshare ../../../libbitdht/src/lib/libbitdht.a ../../../openpgpsdk/lib/libops.a -lgnome-keyring -lupnp -lssl -lcrypto -lbz2 +// +#include +#include +#include + +static std::string passphrase_callback(void *data,const char *uid_info,const char *what,int prev_was_bad) +{ + return std::string(getpass(what)) ; +} + +static std::string stringFromBytes(unsigned char *bytes,size_t len) +{ + static const char out[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } ; + + std::string res ; + + for(int j = 0; j < len; j++) + { + res += out[ (bytes[j]>>4) ] ; + res += out[ bytes[j] & 0xf ] ; + } + + return res ; +} + +int main(int argc,char *argv[]) +{ + // test pgp ids. + // + PGPIdType id = PGPIdType(std::string("3e5b22140ef56abb")) ; + + //std::cerr << "Id is : " << std::hex << id.toUInt64() << std::endl; + std::cerr << "Id st : " << id.toStdString() << std::endl; + + // test PGPHandler + // + // 0 - init + + static const std::string pubring = "pubring.gpg" ; + static const std::string secring = "secring.gpg" ; + static const std::string trustdb = "trustdb.gpg" ; + static const std::string lockfile = "lock" ; + + PGPHandler::setPassphraseCallback(&passphrase_callback) ; + PGPHandler pgph(pubring,secring,trustdb,lockfile) ; + +// std::cerr << "Writing public keyring to file tmp_keyring.asc" << std::endl; +// pgph.writePublicKeyring("tmp_keyring.asc") ; + + pgph.printKeys() ; + + std::cerr << std::endl ; + std::cerr << std::endl ; + + std::cerr << "Looking for keys with complete secret/public key pair: " << std::endl; + + std::list lst ; + pgph.availableGPGCertificatesWithPrivateKeys(lst) ; + + for(std::list::const_iterator it(lst.begin());it!=lst.end();++it) + std::cerr << "Found id : " << (*it).toStdString() << std::endl; + + std::string email_str("test@gmail.com") ; + std::string name_str("test") ; + std::string passw_str("test00") ; + + std::cerr << "Now generating a new PGP certificate: " << std::endl; + std::cerr << " email: " << email_str << std::endl; + std::cerr << " passw: " << passw_str << std::endl; + std::cerr << " name : " << name_str << std::endl; + + PGPIdType newid ; + std::string errString ; + + if(!pgph.GeneratePGPCertificate(name_str, email_str, passw_str, newid, errString)) + std::cerr << "Generation of certificate returned error: " << errString << std::endl; + else + std::cerr << "Certificate generation success. New id = " << newid.toStdString() << std::endl; + + PGPIdType id2 = PGPIdType(std::string("618E54CF7670FF5E")) ; + std::cerr << "Now extracting key " << id2.toStdString() << " from keyring:" << std::endl ; + std::string cert = pgph.SaveCertificateToString(id2,false) ; + + std::cerr << "Now, trying to re-read this cert from the string:" << std::endl; + + PGPIdType id3 ; + std::string error_string ; + pgph.LoadCertificateFromString(cert,id3,error_string) ; + + std::cerr << "Loaded cert id: " << id3.toStdString() << ", Error string=\"" << error_string << "\"" << std::endl; + + std::cerr << cert << std::endl; + + std::cerr << "Testing password callback: " << std::endl; + + std::string pass = passphrase_callback(NULL,newid.toStdString().c_str(),"Please enter password: ",false) ; + + std::cerr << "Password = \"" << pass << "\"" << std::endl; + + std::cerr << "Testing signature with keypair " << newid.toStdString() << std::endl; + + static const size_t BUFF_LEN = 25 ; + unsigned char *test_bin = new unsigned char[BUFF_LEN] ; + for(size_t i=0;i +#include +#include + +static std::string passphrase_callback(void *data,const char *uid_info,const char *what,int prev_was_bad) +{ + return std::string(getpass(what)) ; +} + +static std::string stringFromBytes(unsigned char *bytes,size_t len) +{ + static const char out[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } ; + + std::string res ; + + for(int j = 0; j < len; j++) + { + res += out[ (bytes[j]>>4) ] ; + res += out[ bytes[j] & 0xf ] ; + } + + return res ; +} + +int main(int argc,char *argv[]) +{ + // test pgp ids. + // + PGPIdType id("3e5b22140ef56abb") ; + + std::cerr << "Id st : " << id.toStdString() << std::endl; + + // test PGPHandler + // + // 0 - init + + static const std::string pubring = "pubring.gpg" ; + static const std::string secring = "secring.gpg" ; + static const std::string trustdb = "trustdb.gpg" ; + static const std::string lockfil = "lock" ; + + PGPHandler::setPassphraseCallback(&passphrase_callback) ; + PGPHandler pgph(pubring,secring,trustdb,lockfil) ; + + pgph.printKeys() ; +} + diff --git a/libretroshare/src/tests/scripts/config-linux.mk b/libretroshare/src/tests/scripts/config-linux.mk index 3f7a297da..4d6b85893 100644 --- a/libretroshare/src/tests/scripts/config-linux.mk +++ b/libretroshare/src/tests/scripts/config-linux.mk @@ -25,7 +25,7 @@ BITDIR = $(DHT_TOP_DIR)/lib LIBRS = $(LIBDIR)/libretroshare.a BITDHT = $(BITDIR)/libbitdht.a # Unix: Linux/Cygwin -INCLUDE = -I $(RS_TOP_DIR) +INCLUDE = -I $(RS_TOP_DIR) -I$(OPENPGP_INCLUDE_DIR) CFLAGS = -Wall -g $(INCLUDE) #CFLAGS += -fprofile-arcs -ftest-coverage CFLAGS += ${DEFINES} diff --git a/libretroshare/src/util/rsdir.cc b/libretroshare/src/util/rsdir.cc index 201932a7d..ef166eab0 100644 --- a/libretroshare/src/util/rsdir.cc +++ b/libretroshare/src/util/rsdir.cc @@ -27,6 +27,7 @@ // Includes for directory creation. #include #include +#include #include #include "util/rsdir.h" @@ -848,6 +849,118 @@ std::string RsDirUtil::makePath(const std::string &path1, const std::string &pat return path; } +int RsDirUtil::createLockFile(const std::string& lock_file_path, rs_lock_handle_t &lock_handle) +{ + /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS +// Suspended. The user should make sure he's not already using the file descriptor. +// if(lock_handle != -1) +// close(lock_handle); + + // open the file in write mode, create it if necessary, truncate it (it should be empty) + lock_handle = open(lock_file_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if(lock_handle == -1) + { + std::cerr << "Could not open lock file " << lock_file_path.c_str() << std::flush; + perror(NULL); + return 2; + } + + // see "man fcntl" for the details, in short: non blocking lock creation on the whole file contents + struct flock lockDetails; + lockDetails.l_type = F_WRLCK; + lockDetails.l_whence = SEEK_SET; + lockDetails.l_start = 0; + lockDetails.l_len = 0; + + if(fcntl(lock_handle, F_SETLK, &lockDetails) == -1) + { + int fcntlErr = errno; + std::cerr << "Could not request lock on file " << lock_file_path.c_str() << std::flush; + perror(NULL); + + // there's no lock so let's release the file handle immediately + close(lock_handle); + lock_handle = -1; + + if(fcntlErr == EACCES || fcntlErr == EAGAIN) + return 1; + else + return 2; + } + + return 0; +#else +// Suspended. The user should make sure he's not already using the file descriptor. +// +// if (lock_handle) { +// CloseHandle(lock_handle); +// } + + std::wstring wlockFile; + librs::util::ConvertUtf8ToUtf16(lock_file_path, wlockFile); + + // open the file in write mode, create it if necessary + lock_handle = CreateFile(wlockFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + + if (lock_handle == INVALID_HANDLE_VALUE) + { + DWORD lasterror = GetLastError(); + + std::cerr << "Could not open lock file " << lock_file_path.c_str() << std::endl; + std::cerr << "Last error: " << lasterror << std::endl << std::flush; + perror(NULL); + + if (lasterror == ERROR_SHARING_VIOLATION || lasterror == ERROR_ACCESS_DENIED) + return 1; + + return 2; + } + + return 0; +#endif + /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +} + +void RsDirUtil::releaseLockFile(rs_lock_handle_t lockHandle) +{ + /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS + if(lockHandle != -1) + { + close(lockHandle); + lockHandle = -1; + } +#else + if(lockHandle) + { + CloseHandle(lockHandle); + lockHandle = 0; + } +#endif + /******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +} + +RsStackFileLock::RsStackFileLock(const std::string& file_path) +{ + while(RsDirUtil::createLockFile(file_path,_file_handle)) + { + std::cerr << "Cannot acquire file lock " << file_path << ", waiting 1 sec." << std::endl; +#ifdef WINDOWS_SYS + Sleep(1000) ; +#else + sleep(1) ; +#endif + } + std::cerr << "Acquired file handle " << _file_handle << ", lock file:" << file_path << std::endl; +} +RsStackFileLock::~RsStackFileLock() +{ + RsDirUtil::releaseLockFile(_file_handle) ; + std::cerr << "Released file lock with handle " << _file_handle << std::endl; +} + #if 0 // NOT ENABLED YET! /************************* WIDE STRING ***************************/ /************************* WIDE STRING ***************************/ diff --git a/libretroshare/src/util/rsdir.h b/libretroshare/src/util/rsdir.h index 90952c650..ffcd9c4ca 100644 --- a/libretroshare/src/util/rsdir.h +++ b/libretroshare/src/util/rsdir.h @@ -36,6 +36,27 @@ class RsThread; #include +#ifndef WINDOWS_SYS +typedef int rs_lock_handle_t; +#else +typedef /*HANDLE*/ void *rs_lock_handle_t; +#endif + +// This is a scope guard on a given file. Works like a mutex. Is blocking. +// We could do that in another way: derive RsMutex into RsLockFileMutex, and +// use RsStackMutex on it transparently. Only issue: this will cost little more +// because of the multiple inheritance. +// +class RsStackFileLock +{ + public: + RsStackFileLock(const std::string& file_path) ; + ~RsStackFileLock() ; + + private: + rs_lock_handle_t _file_handle ; +}; + namespace RsDirUtil { std::string getTopDir(const std::string&); @@ -70,6 +91,15 @@ bool getFileHash(const std::string& filepath,std::string &hash, uint64_t &size Sha1CheckSum sha1sum(uint8_t *data,uint32_t size) ; +// Creates a lock file with given path, and returns the lock handle +// returns: +// 0: Success +// 1: Another instance already has the lock +// 2 : Unexpected error +int createLockFile(const std::string& lock_file_path, rs_lock_handle_t& lock_handle) ; + +// Removes the lock file with specified handle. +void releaseLockFile(rs_lock_handle_t lockHandle) ; std::wstring getWideTopDir(std::wstring); std::wstring getWideRootDir(std::wstring); diff --git a/libretroshare/src/util/rsid.h b/libretroshare/src/util/rsid.h new file mode 100644 index 000000000..464999a5d --- /dev/null +++ b/libretroshare/src/util/rsid.h @@ -0,0 +1,112 @@ +// This class aims at defining a generic ID type that is a list of bytes. It +// can be converted into a hexadecial string for printing, mainly) or for +// compatibility with old methods. +// +// To use this class, derive your own ID type from it. Examples include: +// +// class PGPIdType: public t_RsGenericIdType<8> +// { +// [..] +// }; +// +// class PGPFingerprintType: public t_RsGenericIdType<20> +// { +// [..] +// }; +// +// With this, there is no implicit conversion between subtypes, and therefore ID mixup +// is impossible. +// +// A simpler way to make ID types is to +// typedef t_RsGenericIdType MyType ; +// +// ID Types with different lengths will be incompatible on compilation. +// +// Warning: never store references to a t_RsGenericIdType accross threads, since the +// cached string convertion is not thread safe. +// +#include +#include + +template class t_RsGenericIdType +{ + public: + t_RsGenericIdType() {} + virtual ~t_RsGenericIdType() {} + + // Explicit constructor from a hexadecimal string + // + explicit t_RsGenericIdType(const std::string& hex_string) ; + + // Explicit constructor from a byte array. The array should have size at least ID_SIZE_IN_BYTES + // + explicit t_RsGenericIdType(const unsigned char bytes[]) ; + + // Converts to a std::string using cached value. + // + std::string toStdString() const ; + const unsigned char *toByteArray() const { return &bytes[0] ; } + static const uint32_t SIZE_IN_BYTES = ID_SIZE_IN_BYTES ; + + bool operator==(const t_RsGenericIdType& fp) const + { + for(uint32_t i=0;i& fp) const + { + return !operator==(fp) ; + } + + private: + unsigned char bytes[ID_SIZE_IN_BYTES] ; +}; + +template std::string t_RsGenericIdType::toStdString() const +{ + static const char out[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } ; + + std::string res(ID_SIZE_IN_BYTES*2,' ') ; + + for(uint32_t j = 0; j < ID_SIZE_IN_BYTES; j++) + { + res[2*j ] = out[ (bytes[j]>>4) ] ; + res[2*j+1] = out[ bytes[j] & 0xf ] ; + } + + return res ; +} + +template t_RsGenericIdType::t_RsGenericIdType(const std::string& s) +{ + int n=0; + if(s.length() != ID_SIZE_IN_BYTES*2) + throw std::runtime_error("t_RsGenericIdType<>::t_RsGenericIdType(std::string&): supplied string in constructor has wrong size.") ; + + for(uint32_t i = 0; i < ID_SIZE_IN_BYTES; ++i) + { + bytes[i] = 0 ; + + for(int k=0;k<2;++k) + { + char b = s[n++] ; + + if(b >= 'A' && b <= 'F') + bytes[i] += (b-'A'+10) << 4*(1-k) ; + else if(b >= 'a' && b <= 'f') + bytes[i] += (b-'a'+10) << 4*(1-k) ; + else if(b >= '0' && b <= '9') + bytes[i] += (b-'0') << 4*(1-k) ; + else + throw std::runtime_error("t_RsGenericIdType<>::t_RsGenericIdType(std::string&): supplied string is not purely hexadecimal") ; + } + } +} + +template t_RsGenericIdType::t_RsGenericIdType(const unsigned char *mem) +{ + memcpy(bytes,mem,ID_SIZE_IN_BYTES) ; +} + diff --git a/openpgpsdk/src/openpgpsdk/accumulate.c b/openpgpsdk/src/openpgpsdk/accumulate.c new file mode 100644 index 000000000..8c789eab0 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/accumulate.c @@ -0,0 +1,197 @@ +/* + * 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 +#include +#include +#include +#include "keyring_local.h" +#include "parse_local.h" +#include +#include +#include +#include + +#include + +typedef struct + { + ops_keyring_t *keyring; + } accumulate_arg_t; + +/** + * \ingroup Core_Callbacks + */ +static ops_parse_cb_return_t +accumulate_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + accumulate_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + const ops_parser_content_union_t *content=&content_->content; + ops_keyring_t *keyring=arg->keyring; + ops_keydata_t *cur=NULL; + const ops_public_key_t *pkey; + + if(keyring->nkeys >= 0) + cur=&keyring->keys[keyring->nkeys]; + + switch(content_->tag) + { + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + // printf("New key\n"); + ++keyring->nkeys; + EXPAND_ARRAY(keyring,keys); + + if(content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + pkey=&content->public_key; + else + pkey=&content->secret_key.public_key; + + memset(&keyring->keys[keyring->nkeys],'\0', + sizeof keyring->keys[keyring->nkeys]); + + ops_keyid(keyring->keys[keyring->nkeys].key_id,pkey); + ops_fingerprint(&keyring->keys[keyring->nkeys].fingerprint,pkey); + + keyring->keys[keyring->nkeys].type=content_->tag; + + if(content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + keyring->keys[keyring->nkeys].key.pkey=*pkey; + else + keyring->keys[keyring->nkeys].key.skey=content->secret_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ID: + // printf("User ID: %s\n",content->user_id.user_id); + if (!cur) + { + OPS_ERROR(cbinfo->errors,OPS_E_P_NO_USERID, "No user id found"); + return OPS_KEEP_MEMORY; + } + // assert(cur); + ops_add_userid_to_keydata(cur, &content->user_id); + free(content->user_id.user_id); + return OPS_KEEP_MEMORY; + + case OPS_PARSER_PACKET_END: + if(!cur) + return OPS_RELEASE_MEMORY; + ops_add_packet_to_keydata(cur, &content->packet); + free(content->packet.raw); + return OPS_KEEP_MEMORY; + + case OPS_PARSER_ERROR: + fprintf(stderr,"Error: %s\n",content->error.error); + assert(0); + break; + + case OPS_PARSER_ERRCODE: + switch(content->errcode.errcode) + { + default: + fprintf(stderr,"parse error: %s\n", + ops_errcode(content->errcode.errcode)); + //assert(0); + } + break; + + default: + break; + } + + // XXX: we now exclude so many things, we should either drop this or + // do something to pass on copies of the stuff we keep + return ops_parse_stacked_cb(content_,cbinfo); + } + +/** + * \ingroup Core_Parse + * + * Parse packets from an input stream until EOF or error. + * + * Key data found in the parsed data is added to #keyring. + * + * \param keyring Pointer to an existing keyring + * \param parse_info Options to use when parsing +*/ + +int ops_parse_and_accumulate(ops_keyring_t *keyring, + ops_parse_info_t *parse_info) + { + int rtn; + + accumulate_arg_t arg; + + assert(!parse_info->rinfo.accumulate); + + memset(&arg,'\0',sizeof arg); + + arg.keyring=keyring; + /* Kinda weird, but to do with counting, and we put it back after */ + --keyring->nkeys; + + ops_parse_cb_push(parse_info,accumulate_cb,&arg); + + parse_info->rinfo.accumulate=ops_true; + + rtn=ops_parse(parse_info); + ++keyring->nkeys; + + return rtn; + } + +static void dump_one_keydata(const ops_keydata_t *key) + { + unsigned n; + + printf("Key ID: "); + hexdump(key->key_id,8); + + printf("\nFingerpint: "); + hexdump(key->fingerprint.fingerprint,key->fingerprint.length); + + printf("\n\nUIDs\n====\n\n"); + for(n=0 ; n < key->nuids ; ++n) + printf("%s\n",key->uids[n].user_id); + + printf("\nPackets\n=======\n"); + for(n=0 ; n < key->npackets ; ++n) + { + printf("\n%03d: ",n); + hexdump(key->packets[n].raw,key->packets[n].length); + } + printf("\n\n"); + } + +// XXX: not a maintained part of the API - use ops_keyring_list() +/** ops_dump_keyring +*/ +void ops_dump_keyring(const ops_keyring_t *keyring) + { + int n; + + for(n=0 ; n < keyring->nkeys ; ++n) + dump_one_keydata(&keyring->keys[n]); + } diff --git a/openpgpsdk/src/openpgpsdk/accumulate.h b/openpgpsdk/src/openpgpsdk/accumulate.h new file mode 100644 index 000000000..eeadb4011 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/accumulate.h @@ -0,0 +1,33 @@ +/* + * 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 OPS_ACCUMULATE_H +#define OPS_ACCUMULATE_H +#endif + +#include "keyring.h" +#include "packet-parse.h" + +int ops_parse_and_accumulate(ops_keyring_t *keyring, + ops_parse_info_t *parse_info); diff --git a/openpgpsdk/src/openpgpsdk/armour.h b/openpgpsdk/src/openpgpsdk/armour.h new file mode 100644 index 000000000..b2f3a8d69 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/armour.h @@ -0,0 +1,54 @@ +/* + * 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 __OPS_ARMOUR_H__ +#define __OPS_ARMOUR_H__ + +#include "packet-parse.h" +#include "signature.h" + +unsigned ops_crc24(unsigned checksum,unsigned char c); + +void ops_reader_push_dearmour(ops_parse_info_t *parse_info); + +void ops_reader_pop_dearmour(ops_parse_info_t *parse_info); +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig); +void ops_writer_push_armoured_message(ops_create_info_t *info); +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info); + +typedef enum + { + OPS_PGP_MESSAGE=1, + OPS_PGP_PUBLIC_KEY_BLOCK, + OPS_PGP_PRIVATE_KEY_BLOCK, + OPS_PGP_MULTIPART_MESSAGE_PART_X_OF_Y, + OPS_PGP_MULTIPART_MESSAGE_PART_X, + OPS_PGP_SIGNATURE + } ops_armor_type_t; + +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type); + +#define CRC24_INIT 0xb704ceL + +#endif /* __OPS_ARMOUR_H__ */ + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/callback.h b/openpgpsdk/src/openpgpsdk/callback.h new file mode 100644 index 000000000..399c3d118 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/callback.h @@ -0,0 +1,37 @@ +/* + * 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 __OPS_CALLBACK_H__ +#define __OPS_CALLBACK_H__ + +#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if(ops_parse_cb((pc),(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0) +/*#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if((cbinfo)->cb(pc,(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0)*/ +//#define CB(cbinfo,t,pc) do { (pc)->tag=(t); if((cbinfo)->cb(pc,(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0) + +#define CBP(info,t,pc) CB(&(info)->cbinfo,t,pc) + +#define ERR(cbinfo,err,code) do { content.content.error.error=err; content.tag=OPS_PARSER_ERROR; ops_parse_cb(&content,(cbinfo)); OPS_ERROR(errors,code,err); return -1; } while(0) + /*#define ERR(err) do { content.content.error.error=err; content.tag=OPS_PARSER_ERROR; ops_parse_cb(&content,cbinfo); return -1; } while(0)*/ + +#define ERRP(info,err) do { C.error.error=err; CBP(info,OPS_PARSER_ERROR,&content); return ops_false; } while(0) + + +#endif /*__OPS_CALLBACK_H__*/ diff --git a/openpgpsdk/src/openpgpsdk/compress.c b/openpgpsdk/src/openpgpsdk/compress.c new file mode 100644 index 000000000..ec9d993ce --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/compress.c @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2005-2009 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer, Alasdair Mackintosh. + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include "parse_local.h" +#include +#include + +static const int debug = 0; + +#define DECOMPRESS_BUFFER 1024 +#define COMPRESS_BUFFER 32768 + +typedef struct + { + ops_compression_type_t type; + ops_region_t *region; + unsigned char in[DECOMPRESS_BUFFER]; + unsigned char out[DECOMPRESS_BUFFER]; + z_stream zstream; // ZIP and ZLIB + size_t offset; + int inflate_ret; + } z_decompress_arg_t; + +typedef struct + { + ops_compression_type_t type; + ops_region_t *region; + char in[DECOMPRESS_BUFFER]; + char out[DECOMPRESS_BUFFER]; + bz_stream bzstream; // BZIP2 + size_t offset; + int inflate_ret; + } bz_decompress_arg_t; + +typedef struct + { + z_stream stream; + unsigned char *src; + unsigned char *dst; + size_t bytes_in; + size_t bytes_out; + } compress_arg_t; + +// \todo remove code duplication between this and bzip2_compressed_data_reader +static int zlib_compressed_data_reader(void *dest,size_t length, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + z_decompress_arg_t *arg=ops_reader_get_arg(rinfo); + assert(arg->type==OPS_C_ZIP || arg->type==OPS_C_ZLIB); + + //ops_parser_content_t content; + int saved=length; + + if(/*arg->region->indeterminate && */ arg->inflate_ret == Z_STREAM_END + && arg->zstream.next_out == &arg->out[arg->offset]) + return 0; + + if(arg->region->length_read == arg->region->length) + { + if(arg->inflate_ret != Z_STREAM_END) + OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Compressed data didn't end when region ended."); + /* + else + return 0; + www.zlib.org + */ + } + + while(length > 0) + { + unsigned len; + + if(&arg->out[arg->offset] == arg->zstream.next_out) + { + int ret; + + arg->zstream.next_out=arg->out; + arg->zstream.avail_out=sizeof arg->out; + arg->offset=0; + if(arg->zstream.avail_in == 0) + { + unsigned n=arg->region->length; + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n > sizeof arg->in) + n=sizeof arg->in; + } + else + n=sizeof arg->in; + + if(!ops_stacked_limited_read(arg->in,n,arg->region, + errors,rinfo,cbinfo)) + return -1; + + arg->zstream.next_in=arg->in; + arg->zstream.avail_in=arg->region->indeterminate + ? arg->region->last_read : n; + } + + ret=inflate(&arg->zstream,Z_SYNC_FLUSH); + if(ret == Z_STREAM_END) + { + if(!arg->region->indeterminate + && arg->region->length_read != arg->region->length) + OPS_ERROR(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR, + "Compressed stream ended before packet end."); + } + else if(ret != Z_OK) + { + fprintf(stderr,"ret=%d\n",ret); + OPS_ERROR(cbinfo->errors,OPS_E_P_DECOMPRESSION_ERROR, arg->zstream.msg); + } + arg->inflate_ret=ret; + } + assert(arg->zstream.next_out > &arg->out[arg->offset]); + len=arg->zstream.next_out-&arg->out[arg->offset]; + if(len > length) + len=length; + memcpy(dest,&arg->out[arg->offset],len); + arg->offset+=len; + length-=len; + } + + return saved; + } + +// \todo remove code duplication between this and zlib_compressed_data_reader +static int bzip2_compressed_data_reader(void *dest,size_t length, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + bz_decompress_arg_t *arg=ops_reader_get_arg(rinfo); + assert(arg->type==OPS_C_BZIP2); + + //ops_parser_content_t content; + int saved=length; + + if(arg->inflate_ret == BZ_STREAM_END + && arg->bzstream.next_out == &arg->out[arg->offset]) + return 0; + + if(arg->region->length_read == arg->region->length) + { + if(arg->inflate_ret != BZ_STREAM_END) + OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Compressed data didn't end when region ended."); + } + + while(length > 0) + { + unsigned len; + + if(&arg->out[arg->offset] == arg->bzstream.next_out) + { + int ret; + + arg->bzstream.next_out=(char *) arg->out; + arg->bzstream.avail_out=sizeof arg->out; + arg->offset=0; + if(arg->bzstream.avail_in == 0) + { + unsigned n=arg->region->length; + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n > sizeof arg->in) + n=sizeof arg->in; + } + else + n=sizeof arg->in; + + if(!ops_stacked_limited_read(arg->in, n, arg->region, errors, + rinfo, cbinfo)) + return -1; + + arg->bzstream.next_in=arg->in; + arg->bzstream.avail_in=arg->region->indeterminate + ? arg->region->last_read : n; + } + + ret=BZ2_bzDecompress(&arg->bzstream); + if(ret == BZ_STREAM_END) + { + if(!arg->region->indeterminate + && arg->region->length_read != arg->region->length) + OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Compressed stream ended before packet end."); + } + else if(ret != BZ_OK) + { + OPS_ERROR_1(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Invalid return %d from BZ2_bzDecompress", ret); + } + arg->inflate_ret=ret; + } + assert(arg->bzstream.next_out > &arg->out[arg->offset]); + len=arg->bzstream.next_out-&arg->out[arg->offset]; + if(len > length) + len=length; + memcpy(dest,&arg->out[arg->offset],len); + arg->offset+=len; + length-=len; + } + + return saved; + } + +/** + * \ingroup Core_Compress + * + * \param *region Pointer to a region + * \param *parse_info How to parse + * \param type Which compression type to expect +*/ + +int ops_decompress(ops_region_t *region,ops_parse_info_t *parse_info, + ops_compression_type_t type) + { + z_decompress_arg_t z_arg; + bz_decompress_arg_t bz_arg; + int ret; + + switch (type) + { + case OPS_C_ZIP: + case OPS_C_ZLIB: + memset(&z_arg,'\0',sizeof z_arg); + + z_arg.region=region; + z_arg.offset=0; + z_arg.type=type; + + z_arg.zstream.next_in=Z_NULL; + z_arg.zstream.avail_in=0; + z_arg.zstream.next_out=z_arg.out; + z_arg.zstream.zalloc=Z_NULL; + z_arg.zstream.zfree=Z_NULL; + z_arg.zstream.opaque=Z_NULL; + break; + + case OPS_C_BZIP2: + memset(&bz_arg,'\0',sizeof bz_arg); + + bz_arg.region=region; + bz_arg.offset=0; + bz_arg.type=type; + + bz_arg.bzstream.next_in=NULL; + bz_arg.bzstream.avail_in=0; + bz_arg.bzstream.next_out=bz_arg.out; + bz_arg.bzstream.bzalloc=NULL; + bz_arg.bzstream.bzfree=NULL; + bz_arg.bzstream.opaque=NULL; + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, + "Compression algorithm %d is not yet supported", type); + return 0; + } + + switch(type) + { + case OPS_C_ZIP: + ret=inflateInit2(&z_arg.zstream,-15); + break; + + case OPS_C_ZLIB: + ret=inflateInit(&z_arg.zstream); + break; + + case OPS_C_BZIP2: + /* + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, "Compression algorithm %s is not yet supported", "BZIP2"); + return 0; + */ + ret=BZ2_bzDecompressInit(&bz_arg.bzstream, 1, 0); + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, + "Compression algorithm %d is not yet supported", type); + return 0; + } + + switch (type) + { + case OPS_C_ZIP: + case OPS_C_ZLIB: + if(ret != Z_OK) + { + OPS_ERROR_1(&parse_info->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Cannot initialise ZIP or ZLIB stream " + "for decompression: error=%d", ret); + return 0; + } + ops_reader_push(parse_info,zlib_compressed_data_reader,NULL,&z_arg); + break; + + case OPS_C_BZIP2: + if (ret != BZ_OK) + { + OPS_ERROR_1(&parse_info->errors, OPS_E_P_DECOMPRESSION_ERROR, + "Cannot initialise BZIP2 stream " + "for decompression: error=%d", ret); + return 0; + } + ops_reader_push(parse_info,bzip2_compressed_data_reader,NULL,&bz_arg); + break; + + default: + OPS_ERROR_1(&parse_info->errors, OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, + "Compression algorithm %d is not yet supported", type); + return 0; + } + + ret=ops_parse(parse_info); + + ops_reader_pop(parse_info); + + return ret; + } + +/** +\ingroup Core_WritePackets +\brief Writes Compressed packet +\param data Data to write out +\param len Length of data +\param cinfo Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t ops_write_compressed(const unsigned char *data, + const unsigned int len, + ops_create_info_t *cinfo) + { + int r=0; + int sz_in=0; + int sz_out=0; + compress_arg_t* compress=ops_mallocz(sizeof *compress); + + // compress the data + const int level=Z_DEFAULT_COMPRESSION; // \todo allow varying levels + compress->stream.zalloc=Z_NULL; + compress->stream.zfree=Z_NULL; + compress->stream.opaque=NULL; + + // all other fields set to zero by use of ops_mallocz + + if (deflateInit(&compress->stream,level) != Z_OK) + { + // can't initialise + assert(0); + } + + // do necessary transformation + // copy input to maintain const'ness of src + assert(compress->src==NULL); + assert(compress->dst==NULL); + + sz_in=len * sizeof (unsigned char); + sz_out= (sz_in * 1.01) + 12; // from zlib webpage + compress->src=ops_mallocz(sz_in); + compress->dst=ops_mallocz(sz_out); + memcpy(compress->src,data,len); + + // setup stream + compress->stream.next_in=compress->src; + compress->stream.avail_in=sz_in; + compress->stream.total_in=0; + + compress->stream.next_out=compress->dst; + compress->stream.avail_out=sz_out; + compress->stream.total_out=0; + + r=deflate(&compress->stream, Z_FINISH); + assert(r==Z_STREAM_END); // need to loop if not + + // write it out + return (ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo) + && ops_write_length(1+compress->stream.total_out, cinfo) + && ops_write_scalar(OPS_C_ZLIB,1,cinfo) + && ops_write(compress->dst, compress->stream.total_out,cinfo)); + } + + +// Writes out the header for the compressed packet. Invoked by the +// partial stream writer. Note that writing the packet tag and the +// packet length is handled by the partial stream writer. +static ops_boolean_t write_compressed_header(ops_create_info_t *info, + void *header_data) + { + OPS_USED(header_data); + // Write the compression type. Currently we just use ZLIB + ops_write_scalar(OPS_C_ZLIB, 1, info); + return ops_true; + } + +static void zlib_error(ops_error_t **errors, z_stream *stream, int error) + { + OPS_ERROR_2(errors,OPS_E_FAIL, + "Error from compression stream %d (%s)", error, + stream->msg == NULL ? "Unknown" : stream->msg); + } + +static ops_boolean_t stream_compress_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + // ZLib doesn't like being asked to compress nothing, so return if + // we are given no input. + if (length == 0) + return ops_true; + if (debug) + fprintf(stderr, "Compressing %u bytes\n", length); + compress_arg_t* compress = ops_writer_get_arg(winfo); + compress->bytes_in += length; + compress->stream.next_in = (void*) src; + compress->stream.avail_in = length; + ops_boolean_t result = ops_true; + do + { + compress->stream.next_out = compress->dst; + compress->stream.avail_out = COMPRESS_BUFFER; + int retcode = deflate(&compress->stream, Z_NO_FLUSH); + if (retcode != Z_OK) + { + zlib_error(errors, &compress->stream, retcode); + deflateEnd(&compress->stream); + return ops_false; + } + unsigned bytes_to_write = COMPRESS_BUFFER - compress->stream.avail_out; + if (debug) + fprintf(stderr, "bytes_to_write = %u\n", bytes_to_write); + compress->bytes_out += bytes_to_write; + result = ops_stacked_write(compress->dst, bytes_to_write, errors, + winfo); + } + while (result && compress->stream.avail_out == 0); + + return result; + } + +static ops_boolean_t stream_compress_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + compress_arg_t* compress = ops_writer_get_arg(winfo); + compress->stream.next_in = NULL; + compress->stream.avail_in = 0; + int retcode = Z_OK; + int output_size = COMPRESS_BUFFER; + ops_boolean_t result = ops_true; + do + { + compress->stream.next_out = compress->dst; + compress->stream.avail_out = output_size; + retcode = deflate(&compress->stream, Z_FINISH); + if (retcode != Z_STREAM_END && retcode != Z_OK) + { + zlib_error(errors, &compress->stream, retcode); + deflateEnd(&compress->stream); + return ops_false; + } + int bytes_to_write = output_size - compress->stream.avail_out; + if (debug) + fprintf(stderr, "At end, bytes_to_write = %u\n", bytes_to_write); + compress->bytes_out += bytes_to_write; + result = ops_stacked_write(compress->dst, bytes_to_write, errors, + winfo); + + // If deflate returns Z_OK after we have asked to flush, it means + // that there was not enough space in the output buffer. Increase + // the buffer size and try again. + if (retcode != Z_STREAM_END) + { + if (debug) + fprintf(stderr, "Reallocating %u\n", output_size * 2); + output_size *= 2; + compress->dst = realloc(compress->dst, output_size); + } + } + while (result && retcode != Z_STREAM_END); + + int error = deflateEnd(&compress->stream); + if (error != Z_OK) + { + zlib_error(errors, &compress->stream, error); + return ops_false; + } + return result; + } + +static void stream_compress_destroyer(ops_writer_info_t *winfo) + { + compress_arg_t* compress = ops_writer_get_arg(winfo); + if (debug) + fprintf(stderr, "Compressed %zu to %zu\n", compress->bytes_in, + compress->bytes_out); + free(compress->dst); + free(compress); + } + +/** +\ingroup Core_WritePackets +\brief Pushes a compressed writer onto the stack. Data written + will be encoded as a compressed packet. +\param cinfo Write settings +*/ +void ops_writer_push_compressed(ops_create_info_t *cinfo) + { + // This is a streaming writer, so we don't know the length in + // advance. Use a partial writer to handle the partial body + // packet lengths. + ops_writer_push_partial(COMPRESS_BUFFER, + cinfo, OPS_PTAG_CT_COMPRESSED, + write_compressed_header, NULL); + + // Create arg to be used with this writer + // Remember to free this in the destroyer + compress_arg_t *compress = ops_mallocz(sizeof *compress); + + compress->dst = malloc(COMPRESS_BUFFER); + const int level=Z_DEFAULT_COMPRESSION; // \todo allow varying levels + compress->stream.zalloc=Z_NULL; + compress->stream.zfree=Z_NULL; + compress->stream.opaque=NULL; + compress->stream.avail_out = COMPRESS_BUFFER; + // all other fields set to zero by use of ops_mallocz + + if (deflateInit(&compress->stream, level) != Z_OK) + // can't initialise. Is there a better way to handle this? + assert(0); + + // And push writer on stack + ops_writer_push(cinfo, stream_compress_writer, stream_compress_finaliser, + stream_compress_destroyer, compress); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/compress.h b/openpgpsdk/src/openpgpsdk/compress.h new file mode 100644 index 000000000..7cac1ceb2 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/compress.h @@ -0,0 +1,34 @@ +/* + * 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 "packet-parse.h" + +int ops_decompress(ops_region_t *region,ops_parse_info_t *parse_info, + ops_compression_type_t type); + +ops_boolean_t ops_write_compressed(const unsigned char* data, + const unsigned int len, + ops_create_info_t *cinfo); + +void ops_writer_push_compressed(ops_create_info_t *cinfo); diff --git a/openpgpsdk/src/openpgpsdk/configure.h b/openpgpsdk/src/openpgpsdk/configure.h new file mode 100644 index 000000000..63051b316 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/configure.h @@ -0,0 +1,15 @@ +/* generated by configure from include/openpgpsdk/configure.h.template. Don't edit. */ + +#define HAVE_ALLOCA_H 0 +#define TIME_T_FMT "%ld" + +/* for silencing unused parameter warnings */ +#define OPS_USED(x) (x)=(x) + +/* for tests, flag to tell gpg not to use blocking randomness */ +#define GNUPG_QUICK_RANDOM "--quick-random" + +/* Avoid a bunch of #ifs */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif diff --git a/openpgpsdk/src/openpgpsdk/create.c b/openpgpsdk/src/openpgpsdk/create.c new file mode 100644 index 000000000..9c677fd11 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/create.c @@ -0,0 +1,1388 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include +#include + +static int debug=0; + +/** + * \ingroup Core_Create + * \param length + * \param type + * \param info + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_ss_header(unsigned length,ops_content_tag_t type, + ops_create_info_t *info) + { + return ops_write_length(length,info) + && ops_write_scalar(type-OPS_PTAG_SIGNATURE_SUBPACKET_BASE,1,info); + } + +/* 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 + * + * ops_fast_create_user_id() sets id->user_id to the given user_id. + * This is fast because it is only copying a char*. However, if user_id + * is changed or freed in the future, this could have injurious results. + * \param id + * \param user_id + */ + +void ops_fast_create_user_id(ops_user_id_t *id,unsigned char *user_id) + { + id->user_id=user_id; + } + +/** + * \ingroup Core_WritePackets + * \brief Writes a User Id packet + * \param id + * \param info + * \return ops_true if OK, otherwise ops_false + */ +ops_boolean_t ops_write_struct_user_id(ops_user_id_t *id, + ops_create_info_t *info) + { + return ops_write_ptag(OPS_PTAG_CT_USER_ID,info) + && ops_write_length(strlen((char *)id->user_id),info) + && ops_write(id->user_id,strlen((char *)id->user_id),info); + } + +/** + * \ingroup Core_WritePackets + * \brief Write a User Id packet. + * \param user_id + * \param info + * + * \return return value from ops_write_struct_user_id() + */ +ops_boolean_t ops_write_user_id(const unsigned char *user_id, + ops_create_info_t *info) + { + ops_user_id_t id; + + id.user_id=(unsigned char *)user_id; + return ops_write_struct_user_id(&id,info); + } + +/** +\ingroup Core_MPI +*/ +static unsigned mpi_length(const BIGNUM *bn) +{ + return 2+(BN_num_bits(bn)+7)/8; +} + +static unsigned public_key_length(const ops_public_key_t *key) +{ + switch(key->algorithm) + { + case OPS_PKA_RSA: return mpi_length(key->key.rsa.n) + +mpi_length(key->key.rsa.e); + + case OPS_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 OPS_PKA_ELGAMAL: + return mpi_length(key->key.elgamal.p) + +mpi_length(key->key.elgamal.g) + +mpi_length(key->key.elgamal.y) ; + + default: + fprintf(stderr,"Bad algorithm type in key: %d\n",key->algorithm) ; + assert(!"unknown key algorithm"); + } + /* not reached */ + return 0; +} + +static unsigned secret_key_length(const ops_secret_key_t *key) +{ + int l; + + switch(key->public_key.algorithm) + { + case OPS_PKA_RSA: l=mpi_length(key->key.rsa.d) + +mpi_length(key->key.rsa.p) + +mpi_length(key->key.rsa.q) + +mpi_length(key->key.rsa.u); + + case OPS_PKA_DSA: l=mpi_length(key->key.dsa.x); + break; + + default: + assert(!"unknown key algorithm"); + } + + return l+public_key_length(&key->public_key); +} + +/** + * \ingroup Core_Create + * \param key + * \param time + * \param n + * \param e +*/ +void ops_fast_create_rsa_public_key(ops_public_key_t *key,time_t time, + BIGNUM *n,BIGNUM *e) + { + key->version=4; + key->creation_time=time; + key->algorithm=OPS_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 ops_boolean_t write_public_key_body(const ops_public_key_t *key, + ops_create_info_t *info) +{ + if(!(ops_write_scalar(key->version,1,info) && ops_write_scalar(key->creation_time,4,info))) + return ops_false; + + if(key->version != 4 && !ops_write_scalar(key->days_valid,2,info)) + return ops_false; + + if(!ops_write_scalar(key->algorithm,1,info)) + return ops_false; + + switch(key->algorithm) + { + case OPS_PKA_DSA: + return ops_write_mpi(key->key.dsa.p,info) + && ops_write_mpi(key->key.dsa.q,info) + && ops_write_mpi(key->key.dsa.g,info) + && ops_write_mpi(key->key.dsa.y,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + return ops_write_mpi(key->key.rsa.n,info) + && ops_write_mpi(key->key.rsa.e,info); + + case OPS_PKA_ELGAMAL: + return ops_write_mpi(key->key.elgamal.p,info) + && ops_write_mpi(key->key.elgamal.g,info) + && ops_write_mpi(key->key.elgamal.y,info); + + default: + fprintf(stderr, "Unknown algorithm %d\n", key->algorithm); + assert(0); + break; + } + + /* not reached */ + return ops_false; +} + +/* Note that we support v3 keys here because they're needed for + * for verification - the writer doesn't allow them, though */ +static ops_boolean_t write_secret_key_body(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info) +{ + /* RFC4880 Section 5.5.3 Secret-Key Packet Formats */ + + ops_crypt_t crypt; + ops_hash_t hash; + unsigned char hashed[OPS_SHA1_HASH_SIZE]; + unsigned char session_key[CAST_KEY_LENGTH]; + unsigned int done=0; + unsigned int i=0; + + if(!write_public_key_body(&key->public_key,info)) + return ops_false; + + assert(key->s2k_usage==OPS_S2KU_ENCRYPTED_AND_HASHED); /* = 254 */ + if(!ops_write_scalar(key->s2k_usage,1,info)) + return ops_false; + + assert(key->algorithm==OPS_SA_CAST5); + if (!ops_write_scalar(key->algorithm,1,info)) + return ops_false; + + assert(key->s2k_specifier==OPS_S2KS_SIMPLE + || key->s2k_specifier==OPS_S2KS_SALTED); // = 1 \todo could also be iterated-and-salted + if (!ops_write_scalar(key->s2k_specifier,1,info)) + return ops_false; + + assert(key->hash_algorithm==OPS_HASH_SHA1); + if (!ops_write_scalar(key->hash_algorithm,1,info)) + return ops_false; + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + // nothing more to do + break; + + case OPS_S2KS_SALTED: + // 8-octet salt value + ops_random((void *)&key->salt[0],OPS_SALT_SIZE); + if (!ops_write(key->salt, OPS_SALT_SIZE, info)) + return ops_false; + break; + + /* \todo + case OPS_S2KS_ITERATED_AND_SALTED: + // 8-octet salt value + // 1-octet count + break; + */ + + default: + fprintf(stderr,"invalid/unsupported s2k specifier %d\n", + key->s2k_specifier); + assert(0); + } + + if (!ops_write(&key->iv[0],ops_block_size(key->algorithm),info)) + return ops_false; + + /* create the session key for encrypting the algorithm-specific fields */ + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + case OPS_S2KS_SALTED: + // RFC4880: section 3.7.1.1 and 3.7.1.2 + + done=0; + for (i=0; donehash_algorithm); + hash.init(&hash); + + // preload if iterating + for (j=0; js2k_specifier==OPS_S2KS_SALTED) + { hash.add(&hash, key->salt, OPS_SALT_SIZE); } + + hash.add(&hash, passphrase, pplen); + hash.finish(&hash, hashed); + + // if more in hash than is needed by session key, use the + // leftmost octets + memcpy(session_key+(i*SHA_DIGEST_LENGTH), hashed, use); + done += use; + assert(done<=CAST_KEY_LENGTH); + } + + break; + + /* \todo + case OPS_S2KS_ITERATED_AND_SALTED: + // 8-octet salt value + // 1-octet count + break; + */ + + default: + fprintf(stderr,"invalid/unsupported s2k specifier %d\n", + key->s2k_specifier); + assert(0); + } + + /* use this session key to encrypt */ + + ops_crypt_any(&crypt,key->algorithm); + crypt.set_iv(&crypt, key->iv); + crypt.set_key(&crypt, session_key); + ops_encrypt_init(&crypt); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\nWRITING:\niv="); + for (i=0; ialgorithm); i++) + { + fprintf(stderr, "%02x ", key->iv[i]); + } + fprintf(stderr,"\n"); + + fprintf(stderr,"key="); + for (i=0; ipublic_key.algorithm) + { + case OPS_PKA_DSA: + return ops_write_mpi(key->key.dsa.x,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + + if(!ops_write_mpi(key->key.rsa.d,info) + || !ops_write_mpi(key->key.rsa.p,info) + || !ops_write_mpi(key->key.rsa.q,info) + || !ops_write_mpi(key->key.rsa.u,info)) + { + if (debug) + { fprintf(stderr,"4 x mpi not written - problem\n"); } + return ops_false; + } + + break; + + // case OPS_PKA_ELGAMAL: + // return ops_write_mpi(key->key.elgamal.x,info); + + default: + assert(0); + break; + } + + if(!ops_write(key->checkhash, OPS_CHECKHASH_SIZE, info)) + return ops_false; + + ops_writer_pop(info); + + free(crypt.encrypt_key) ; + free(crypt.decrypt_key) ; + + return ops_true; +} + + +/** + \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 info Output stream + + Example code: + \code + void example(const ops_keydata_t* keydata) + { + ops_boolean_t armoured=ops_true; + char* filename="/tmp/testkey.asc"; + + int fd; + ops_boolean_t overwrite=ops_true; + ops_create_info_t* cinfo; + + fd=ops_setup_file_write(&cinfo, filename, overwrite); + ops_write_transferable_public_key(keydata,armoured,cinfo); + ops_teardown_file_write(cinfo,fd); + } + \endcode +*/ + +ops_boolean_t ops_write_transferable_public_key(const ops_keydata_t *keydata, + ops_boolean_t armoured, + ops_create_info_t *info) +{ + ops_boolean_t rtn; + unsigned int i=0,j=0; + + if (armoured) + { ops_writer_push_armoured(info, OPS_PGP_PUBLIC_KEY_BLOCK); } + + // public key + rtn=ops_write_struct_public_key(&keydata->key.skey.public_key,info); + if (rtn!=ops_true) + return rtn; + + // TODO: revocation signatures go here + + // user ids and corresponding signatures + for (i=0; inuids; i++) + { + ops_user_id_t* uid=&keydata->uids[i]; + + rtn=ops_write_struct_user_id(uid, info); + + if (!rtn) + return rtn; + + // find signature for this packet if it exists + for (j=0; jnsigs; j++) + { + sigpacket_t* sig=&keydata->sigs[i]; + if (!strcmp((char *)sig->userid->user_id, (char *)uid->user_id)) + { + rtn=ops_write(sig->packet->raw, sig->packet->length, info); + if (!rtn) + return !rtn; + } + } + } + + // TODO: user attributes and corresponding signatures + + // subkey packets and corresponding signatures and optional revocation + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; +} + +ops_boolean_t ops_write_transferable_public_key_from_packet_data(const ops_keydata_t *keydata, + ops_boolean_t armoured, + ops_create_info_t *info) +{ + ops_boolean_t rtn = ops_true; + unsigned int i=0,j=0; + + if (armoured) + { ops_writer_push_armoured(info, OPS_PGP_PUBLIC_KEY_BLOCK); } + + for(i=0;inpackets;++i) + if(!ops_write(keydata->packets[i].raw, keydata->packets[i].length, info)) + return ops_false ; + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; +} + +ops_boolean_t ops_write_transferable_secret_key_from_packet_data(const ops_keydata_t *keydata, + ops_boolean_t armoured, + ops_create_info_t *info) +{ + ops_boolean_t rtn = ops_true; + unsigned int i=0,j=0; + + if(keydata->type != OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + { + fprintf(stderr,"Can only output encrypted secret keys from raw packet data. Current type is %d\n",keydata->type) ; + return ops_false ; + } + if (armoured) + { ops_writer_push_armoured(info, OPS_PGP_PRIVATE_KEY_BLOCK); } + + for(i=0;inpackets;++i) + if(!ops_write(keydata->packets[i].raw, keydata->packets[i].length, info)) + return ops_false ; + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; +} +/** + \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 info Output stream + + Example code: + \code + void example(const ops_keydata_t* keydata) + { + const unsigned char* passphrase=NULL; + const size_t passphraselen=0; + ops_boolean_t armoured=ops_true; + + int fd; + char* filename="/tmp/testkey.asc"; + ops_boolean_t overwrite=ops_true; + ops_create_info_t* cinfo; + + fd=ops_setup_file_write(&cinfo, filename, overwrite); + ops_write_transferable_secret_key(keydata,passphrase,pplen,armoured,cinfo); + ops_teardown_file_write(cinfo,fd); + } + \endcode +*/ + +ops_boolean_t ops_write_transferable_secret_key(const ops_keydata_t *keydata, const unsigned char* passphrase, const size_t pplen, ops_boolean_t armoured, ops_create_info_t *info) + { + ops_boolean_t rtn; + unsigned int i=0,j=0; + + if (armoured) + { ops_writer_push_armoured(info,OPS_PGP_PRIVATE_KEY_BLOCK); } + + // public key + rtn=ops_write_struct_secret_key(&keydata->key.skey,passphrase,pplen,info); + if (rtn!=ops_true) + return rtn; + + // TODO: revocation signatures go here + + // user ids and corresponding signatures + for (i=0; inuids; i++) + { + ops_user_id_t* uid=&keydata->uids[i]; + + rtn=ops_write_struct_user_id(uid, info); + + if (!rtn) + return rtn; + + // find signature for this packet if it exists + for (j=0; jnsigs; j++) + { + sigpacket_t* sig=&keydata->sigs[i]; + if (!strcmp((char *)sig->userid->user_id, (char *)uid->user_id)) + { + rtn=ops_write(sig->packet->raw, sig->packet->length, info); + if (!rtn) + return !rtn; + } + } + } + + // TODO: user attributes and corresponding signatures + + // subkey packets and corresponding signatures and optional revocation + + if (armoured) + { + writer_info_finalise(&info->errors, &info->winfo); + ops_writer_pop(info); + } + + return rtn; + } + +/** + * \ingroup Core_WritePackets + * \brief Writes a Public Key packet + * \param key + * \param info + * \return ops_true if OK, otherwise ops_false + */ +ops_boolean_t ops_write_struct_public_key(const ops_public_key_t *key, + ops_create_info_t *info) +{ + assert(key->version == 4); + + if(key->algorithm != OPS_PKA_RSA) + { + fprintf(stderr,"\nUnhandled key algorithm %d\n",key->algorithm); + return ops_false ; + } + + return ops_write_ptag(OPS_PTAG_CT_PUBLIC_KEY,info) + && ops_write_length(1+4+1+public_key_length(key),info) + && write_public_key_body(key,info); +} + +/** + * \ingroup Core_WritePackets + * \brief Writes one RSA public key packet. + * \param time Creation time + * \param n RSA public modulus + * \param e RSA public encryption exponent + * \param info Writer settings + * + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_rsa_public_key(time_t time,const BIGNUM *n, + const BIGNUM *e, + ops_create_info_t *info) + { + ops_public_key_t key; + + ops_fast_create_rsa_public_key(&key,time,DECONST(BIGNUM,n), + DECONST(BIGNUM,e)); + return ops_write_struct_public_key(&key,info); + } + +/** + * \ingroup Core_Create + * \param out + * \param key + * \param make_packet + */ + +void ops_build_public_key(ops_memory_t *out,const ops_public_key_t *key, + ops_boolean_t make_packet) + { + ops_create_info_t *info; + + info=ops_create_info_new(); + + ops_memory_init(out,128); + ops_writer_set_memory(info,out); + + write_public_key_body(key,info); + + if(make_packet) + ops_memory_make_packet(out,OPS_PTAG_CT_PUBLIC_KEY); + + ops_create_info_delete(info); + } + +/** + * \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 parameters - 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 time + * \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 ops_fast_create_rsa_secret_key(ops_secret_key_t *key,time_t time, + BIGNUM *d,BIGNUM *p,BIGNUM *q,BIGNUM *u, + BIGNUM *n,BIGNUM *e) + { + ops_fast_create_rsa_public_key(&key->public_key,time,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=OPS_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 info + * \return ops_true if OK; else ops_false + */ +ops_boolean_t ops_write_struct_secret_key(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info) + { + int length=0; + + assert(key->public_key.version == 4); + + // Ref: RFC4880 Section 5.5.3 + + // public_key, excluding MPIs + length += 1+4+1+1; + + // s2k usage + length+=1; + + switch (key->s2k_usage) + { + case OPS_S2KU_NONE: + // nothing to add + break; + + case OPS_S2KU_ENCRYPTED_AND_HASHED: // 254 + case OPS_S2KU_ENCRYPTED: // 255 + + // Ref: RFC4880 Section 3.7 + length+=1; // s2k_specifier + + switch(key->s2k_specifier) + { + case OPS_S2KS_SIMPLE: + length+=1; // hash algorithm + break; + + case OPS_S2KS_SALTED: + length+=1+8; // hash algorithm + salt + break; + + case OPS_S2KS_ITERATED_AND_SALTED: + length+=1+8+1; // hash algorithm, salt + count + break; + + default: + assert(0); + } + break; + + default: + assert(0); + } + + // IV + if (key->s2k_usage != 0) + { + length += ops_block_size(key->algorithm); + } + + // checksum or hash + switch (key->s2k_usage) + { + case 0: + case 255: + length += 2; + break; + + case 254: + length += 20; + break; + + default: + assert(0); + } + + // secret key and public key MPIs + length += secret_key_length(key); + + return ops_write_ptag(OPS_PTAG_CT_SECRET_KEY,info) + // && ops_write_length(1+4+1+1+secret_key_length(key)+2,info) + && ops_write_length(length,info) + && write_secret_key_body(key,passphrase,pplen,info); + } + +/** + * \ingroup InternalAPI + * + * \brief Initialise a temporary info structure that can be used for + * writing to a writer's parent. + * + * This is used by writers who want to use the various ops_write functions + * in order to write to the parent writer. + * Example code: + * \code + * ops_boolean_t writer(const unsigned char *src, + * unsigned length, + * ops_error_t **errors, + * ops_writer_info_t *winfo) { + * ops_create_info_t parent; + * ops_prepare_parent_info(&parent, winfo, errors); + * + * // The ptag will be written to the parent writer + * ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, &parent); + * + * // The data is written to the parent. This line is + // equivalent to: + * // ops_stacked_write(src, length, errors, winfo); + * ops_boolean_t result = ops_write(src, length, info); + * ops_move_errors(&parent_info, errors); + * return result; + * \endcode + * + * \note It is the responsiblity of the caller to assign space for the parent + * structure, typically on the stack. IOn order to report errors correctly, + * use ops_move_errors() after the write operation. + * + * \see ops_move_errors + */ +void ops_prepare_parent_info(ops_create_info_t *parent_info, + ops_writer_info_t *winfo) + { + parent_info->winfo = *winfo->next; + parent_info->errors = NULL; + } + +/** + * \ingroup Core_Create + * + * \brief Create a new ops_create_info_t structure. + * + * \return the new structure. + * \note It is the responsiblity of the caller to call ops_create_info_delete(). + * \sa ops_create_info_delete() + */ +ops_create_info_t *ops_create_info_new(void) + { return ops_mallocz(sizeof(ops_create_info_t)); } + +/** + * \ingroup Core_Create + * \brief Delete an ops_create_info_t strucut and associated resources. + * + * Delete an ops_create_info_t structure. If a writer is active, then + * that is also deleted. + * + * \param info the structure to be deleted. + */ +void ops_create_info_delete(ops_create_info_t *info) + { + writer_info_delete(&info->winfo); + free(info); + } + +/** + \ingroup Core_Create + \brief Calculate the checksum for a session key + \param session_key Session Key to use + \param cs Checksum to be written + \return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_calc_session_key_checksum(ops_pk_session_key_t *session_key, + unsigned char cs[2]) + { + unsigned int i=0; + unsigned long checksum=0; + + if (!ops_is_sa_supported(session_key->symmetric_algorithm)) + return ops_false; + + for (i=0; isymmetric_algorithm); i++) + { + checksum+=session_key->key[i]; + } + checksum = checksum % 65536; + + cs[0]=checksum >> 8; + cs[1]=checksum & 0xFF; + + return ops_true; + // fprintf(stderr,"\nm buf checksum: "); + // fprintf(stderr," %2x",cs[0]); + // fprintf(stderr," %2x\n",cs[1]); + } + +static ops_boolean_t create_unencoded_m_buf(ops_pk_session_key_t *session_key, + unsigned char *m_buf) + { + int i=0; + // unsigned long checksum=0; + + // 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]=session_key->symmetric_algorithm; + + assert(session_key->symmetric_algorithm == OPS_SA_CAST5); + for (i=0; ikey[i]; + } + + return(ops_calc_session_key_checksum(session_key, m_buf+1+CAST_KEY_LENGTH)); + } + +/** +\ingroup Core_Create +\brief implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC +\param M +\param mLen +\param pkey +\param EM +\return ops_true if OK; else ops_false +*/ +ops_boolean_t encode_m_buf(const unsigned char *M, size_t mLen, + const ops_public_key_t *pkey, + unsigned char* EM +) + { + unsigned int k; + unsigned i; + + // implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC + + if(pkey->algorithm != OPS_PKA_RSA) + { + fprintf(stderr,"\nUnhandled key algorithm %d\n",pkey->algorithm); + return ops_false ; + } + + k=BN_num_bytes(pkey->key.rsa.n); + assert(mLen <= k-11); + if (mLen > k-11) + { + fprintf(stderr,"message too long\n"); + return ops_false; + } + + // 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 + ops_random(EM+i, 1); + while(EM[i] == 0); + + assert (i >= 8+2); + + EM[i++]=0; + + memcpy(EM+i, M, mLen); + + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"Encoded Message: \n"); + for (i=0; ikey.rsa.n); + unsigned char* encoded_m_buf = ops_mallocz(sz_encoded_m_buf); + + ops_pk_session_key_t *session_key=ops_mallocz(sizeof *session_key); + + assert(key->type == OPS_PTAG_CT_PUBLIC_KEY); + session_key->version=OPS_PKSK_V3; + memcpy(session_key->key_id, key->key_id, sizeof session_key->key_id); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"Encrypting for RSA key id : "); + for (i=0; ikey_id; i++) + fprintf(stderr,"%2x ", key->key_id[i]); + fprintf(stderr,"\n"); + } + + assert(key->key.pkey.algorithm == OPS_PKA_RSA); + session_key->algorithm=key->key.pkey.algorithm; + + // \todo allow user to specify other algorithm + session_key->symmetric_algorithm=OPS_SA_CAST5; + ops_random(session_key->key, CAST_KEY_LENGTH); + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"CAST5 session key created (len=%d):\n ", + CAST_KEY_LENGTH); + for (i=0; ikey[i]); + fprintf(stderr,"\n"); + } + + if (create_unencoded_m_buf(session_key, &unencoded_m_buf[0])==ops_false) + { + free(encoded_m_buf); + return NULL; + } + + if (debug) + { + unsigned int i=0; + printf("unencoded m buf:\n"); + for (i=0; iparameters)) + { + free (encoded_m_buf); + return NULL; + } + + free(encoded_m_buf); + return session_key; + } + +/** +\ingroup Core_WritePackets +\brief Writes Public Key Session Key packet +\param info Write settings +\param pksk Public Key Session Key to write out +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_pk_session_key(ops_create_info_t *info, + ops_pk_session_key_t *pksk) + { + assert(pksk); + assert(pksk->algorithm == OPS_PKA_RSA); + + return ops_write_ptag(OPS_PTAG_CT_PK_SESSION_KEY, info) + && ops_write_length(1 + 8 + 1 + + BN_num_bytes(pksk->parameters.rsa.encrypted_m) + + 2, info) + && ops_write_scalar(pksk->version, 1, info) + && ops_write(pksk->key_id, 8, info) + && ops_write_scalar(pksk->algorithm, 1, info) + && ops_write_mpi(pksk->parameters.rsa.encrypted_m, info) + //?? && ops_write_scalar(0, 2, info); + ; + } + +/** +\ingroup Core_WritePackets +\brief Writes MDC packet +\param hashed Hash for MDC +\param info Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t ops_write_mdc(const unsigned char *hashed, + ops_create_info_t* info) + { + // write it out + return ops_write_ptag(OPS_PTAG_CT_MDC, info) + && ops_write_length(OPS_SHA1_HASH_SIZE,info) + && ops_write(hashed, OPS_SHA1_HASH_SIZE, info); + } + +/** +\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 info Write settings +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_literal_data_from_buf(const unsigned char *data, + const int maxlen, + const ops_literal_data_type_t type, + ops_create_info_t *info) + { + /* + * 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 line endings ? + return ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info) + && ops_write_length(1+1+4+maxlen,info) + && ops_write_scalar(type, 1, info) + && ops_write_scalar(0, 1, info) + && ops_write_scalar(0, 4, info) + && ops_write(data, maxlen, info); + } + +/** +\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 info Write settings +\return ops_true if OK; else ops_false +*/ + +ops_boolean_t +ops_write_literal_data_from_file(const char *filename, + const ops_literal_data_type_t type, + ops_create_info_t *info) + { + size_t initial_size=1024; + int fd=0; + ops_boolean_t rtn; + unsigned char buf[1024]; + ops_memory_t* mem=NULL; + size_t len=0; + + fd=open(filename,O_RDONLY | O_BINARY); + + if (fd < 0) + return ops_false; + + mem=ops_memory_new(); + ops_memory_init(mem,initial_size); + for (;;) + { + ssize_t n=0; + n=read(fd,buf,1024); + if (!n) + break; + ops_memory_add(mem, &buf[0], n); + } + close(fd); + + // \todo do we need to check text data for line endings ? + len=ops_memory_get_length(mem); + rtn=ops_write_ptag(OPS_PTAG_CT_LITERAL_DATA, info) + && ops_write_length(1+1+4+len,info) + && ops_write_scalar(type, 1, info) + && ops_write_scalar(0, 1, info) // filename + && ops_write_scalar(0, 4, info) // date + && ops_write(ops_memory_get_data(mem), len, info); + + ops_memory_free(mem); + return rtn; + } + +/** + \ingroup HighLevel_General + + \brief Reads contents of file into new ops_memory_t struct. + + \param filename Filename to read from + \param errnum Pointer to error + \return new ops_memory_t pointer containing the contents of the file + + \note If there was an error opening the file or reading from it, + errnum is set to the cause + + \note It is the caller's responsibility to call ops_memory_free(mem) +*/ + +ops_memory_t* ops_write_mem_from_file(const char *filename, int* errnum) + { + size_t initial_size=1024; + int fd=0; + unsigned char buf[1024]; + ops_memory_t* mem=NULL; + + *errnum=0; + + fd=open(filename,O_RDONLY | O_BINARY); + + if (fd < 0) + { + *errnum=errno; + return ops_false; + } + + mem=ops_memory_new(); + ops_memory_init(mem,initial_size); + for (;;) + { + ssize_t n=0; + n=read(fd,buf,1024); + if (n<0) + { + *errnum=errno; + break; + } + if (!n) + break; + ops_memory_add(mem, &buf[0], n); + } + close(fd); + return mem; + } + +/** + \ingroup HighLevel_General + + \brief Reads 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 ops_write_file_from_buf(const char *filename, const char* buf, + const size_t len, const ops_boolean_t overwrite) + { + int fd=0; + size_t n=0; + int flags=0; + + flags=O_WRONLY | O_CREAT; + if (overwrite==ops_true) + flags |= O_TRUNC; + else + flags |= O_EXCL; + + flags |= O_BINARY; + + fd=open(filename,flags, 0600); + if (fd < 0) + { + perror(NULL); + return 0; + } + + n=write(fd,buf,len); + if (n!=len) + return 0; + + if(!close(fd)) + return 1; + + return 0; + } + +/** +\ingroup Core_WritePackets +\brief Write Symmetrically Encrypted packet +\param data Data to encrypt +\param len Length of data +\param info Write settings +\return ops_true if OK; else ops_false +\note Hard-coded to use AES256 +*/ +ops_boolean_t ops_write_symmetrically_encrypted_data(const unsigned char *data, + const int len, + ops_create_info_t *info) + { + int done=0; + ops_crypt_t crypt_info; + int encrypted_sz=0;// size of encrypted data + unsigned char *encrypted=NULL; // buffer to write encrypted data to + + // \todo assume AES256 for now + ops_crypt_any(&crypt_info, OPS_SA_AES_256); + ops_encrypt_init(&crypt_info); + + encrypted_sz=len+crypt_info.blocksize+2; + encrypted=ops_mallocz(encrypted_sz); + + done=ops_encrypt_se(&crypt_info, encrypted, data, len); + assert(done==len); + // printf("len=%d, done: %d\n", len, done); + + return ops_write_ptag(OPS_PTAG_CT_SE_DATA, info) + && ops_write_length(1+encrypted_sz,info) + && ops_write(data, len, info); + } + +/** +\ingroup Core_WritePackets +\brief Write a One Pass Signature packet +\param skey Secret Key to use +\param hash_alg Hash Algorithm to use +\param sig_type Signature type +\param info Write settings +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_write_one_pass_sig(const ops_secret_key_t* skey, + const ops_hash_algorithm_t hash_alg, + const ops_sig_type_t sig_type, + ops_create_info_t* info) + { + unsigned char keyid[OPS_KEY_ID_SIZE]; + if (debug) + fprintf(stderr, "calling ops_keyid in write_one_pass_sig: " + "this calls sha1_init\n"); + ops_keyid(keyid,&skey->public_key); + + return ops_write_ptag(OPS_PTAG_CT_ONE_PASS_SIGNATURE, info) + && ops_write_length(1+1+1+1+8+1, info) + && ops_write_scalar (3, 1, info) // version + && ops_write_scalar (sig_type, 1, info) + && ops_write_scalar (hash_alg, 1, info) + && ops_write_scalar (skey->public_key.algorithm, 1, info) + && ops_write(keyid, 8, info) + && ops_write_scalar (1, 1, info); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/create.h b/openpgpsdk/src/openpgpsdk/create.h new file mode 100644 index 000000000..aaeec0d51 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/create.h @@ -0,0 +1,87 @@ +/* + * 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 OPS_CREATE_H +#define OPS_CREATE_H + +#include +#include +#include +#include +#include +#include +#include + +/** + * \ingroup Create + * This struct contains the required information about how to write this stream + */ +struct ops_create_info + { + ops_writer_info_t winfo; + ops_error_t *errors; /*!< an error stack */ + }; + +void ops_prepare_parent_info(ops_create_info_t *parent_info, + ops_writer_info_t *winfo); +ops_create_info_t *ops_create_info_new(void); +void ops_create_info_delete(ops_create_info_t *info); + +ops_memory_t* ops_write_mem_from_file(const char *filename, int* errnum); +int ops_write_file_from_buf(const char *filename, const char* buf, const size_t len, const ops_boolean_t overwrite); + +ops_boolean_t ops_calc_session_key_checksum(ops_pk_session_key_t *session_key, unsigned char *cs); +void ops_build_public_key(ops_memory_t *out,const ops_public_key_t *key, + ops_boolean_t make_packet); +ops_boolean_t ops_write_struct_user_id(ops_user_id_t *id, + ops_create_info_t *info); +ops_boolean_t ops_write_struct_public_key(const ops_public_key_t *key, + ops_create_info_t *info); + +ops_boolean_t ops_write_ss_header(unsigned length,ops_content_tag_t type, + ops_create_info_t *info); +ops_boolean_t ops_write_struct_secret_key(const ops_secret_key_t *key, + const unsigned char* passphrase, + const size_t pplen, + ops_create_info_t *info); +ops_boolean_t ops_write_one_pass_sig(const ops_secret_key_t* skey, + const ops_hash_algorithm_t hash_alg, + const ops_sig_type_t sig_type, + ops_create_info_t* info); +ops_boolean_t ops_write_literal_data_from_buf(const unsigned char *data, + const int maxlen, + const ops_literal_data_type_t type, + ops_create_info_t *info); +ops_pk_session_key_t *ops_create_pk_session_key(const ops_keydata_t *key); +ops_boolean_t ops_write_pk_session_key(ops_create_info_t *info, + ops_pk_session_key_t *pksk); +ops_boolean_t ops_write_transferable_public_key(const ops_keydata_t *key, ops_boolean_t armoured, ops_create_info_t *info); +ops_boolean_t ops_write_transferable_secret_key(const ops_keydata_t *key, const unsigned char* passphrase, const size_t pplen, ops_boolean_t armoured, ops_create_info_t *info); +ops_boolean_t ops_write_transferable_public_key_from_packet_data(const ops_keydata_t *keydata, ops_boolean_t armoured, ops_create_info_t *info); +ops_boolean_t ops_write_transferable_secret_key_from_packet_data(const ops_keydata_t *keydata, ops_boolean_t armoured, ops_create_info_t *info); + + +#endif /*OPS_CREATE_H*/ + +// eof diff --git a/openpgpsdk/src/openpgpsdk/crypto.c b/openpgpsdk/src/openpgpsdk/crypto.c new file mode 100644 index 000000000..199122893 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/crypto.c @@ -0,0 +1,544 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include "parse_local.h" + +#include +#include +#include + +#include + +/** +\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 skey +\return length of MPI +\note only RSA at present +*/ +int ops_decrypt_and_unencode_mpi(unsigned char *buf, unsigned buflen, + const BIGNUM *encmpi, + const ops_secret_key_t *skey) + { + unsigned char encmpibuf[8192]; + unsigned char mpibuf[8192]; + unsigned mpisize; + int n; + int i; + + mpisize=BN_num_bytes(encmpi); + /* MPI can't be more than 65,536 */ + assert(mpisize <= sizeof encmpibuf); + BN_bn2bin(encmpi, encmpibuf); + + assert(skey->public_key.algorithm == OPS_PKA_RSA); + + /* + fprintf(stderr,"\nDECRYPTING\n"); + fprintf(stderr,"encrypted data : "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encmpibuf[i]); + fprintf(stderr,"\n"); + */ + + n=ops_rsa_private_decrypt(mpibuf, encmpibuf, (BN_num_bits(encmpi)+7)/8, + &skey->key.rsa, &skey->public_key.key.rsa); + assert(n != -1); + + /* + fprintf(stderr,"decrypted encoded m buf : "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", mpibuf[i]); + fprintf(stderr,"\n"); + */ + + if(n <= 0) + return -1; + + /* + printf(" decrypted=%d ",n); + hexdump(mpibuf,n); + printf("\n"); + */ + + // Decode EME-PKCS1_V1_5 (RFC 2437). + + if(mpibuf[0] != 0 || mpibuf[1] != 2) + return ops_false; + + // Skip the random bytes. + for(i=2 ; i < n && mpibuf[i] ; ++i) + ; + + if(i == n || i < 10) + return ops_false; + + // Skip the zero + ++i; + + // this is the unencoded m buf + if((unsigned)(n-i) <= buflen) + memcpy(buf, mpibuf+i, n-i); + + /* + printf("decoded m buf:\n"); + int j; + for (j=0; jkey.rsa.n)); + + unsigned char encmpibuf[8192]; + int n=0; + + n=ops_rsa_public_encrypt(encmpibuf, encoded_m_buf, sz_encoded_m_buf, + &pkey->key.rsa); + assert(n!=-1); + + if(n <= 0) + return ops_false; + + skp->rsa.encrypted_m=BN_bin2bn(encmpibuf, n, NULL); + + /* + fprintf(stderr,"encrypted mpi buf : "); + int i; + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encmpibuf[i]); + fprintf(stderr,"\n"); + */ + + return ops_true; + } + +#define MAXBUF 1024 + +static ops_parse_cb_return_t +callback_write_parsed(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo); + +/** +\ingroup HighLevel_Crypto +Encrypt a file +\param input_filename Name of file to be encrypted +\param output_filename Name of file to write to. If NULL, name is constructed from input_filename +\param pub_key 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 ops_true if OK; else ops_false +*/ +ops_boolean_t ops_encrypt_file(const char* input_filename, + const char* output_filename, + const ops_keydata_t *pub_key, + const ops_boolean_t use_armour, + const ops_boolean_t allow_overwrite) + { + int fd_in=0; + int fd_out=0; + + ops_create_info_t *cinfo; +#ifdef WINDOWS_SYS + fd_in=open(input_filename, O_RDONLY | O_BINARY); +#else + fd_in=open(input_filename, O_RDONLY ); +#endif + if(fd_in < 0) + { + perror(input_filename); + return ops_false; + } + + fd_out=ops_setup_file_write(&cinfo, output_filename, allow_overwrite); + if (fd_out < 0) + return ops_false; + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + // Push the encrypted writer + ops_writer_push_stream_encrypt_se_ip(cinfo, pub_key); + ops_writer_push_literal(cinfo); + + // Do the writing + + unsigned buffer[10240]; + for (;;) + { + int n=0; + + n=read(fd_in, buffer, sizeof buffer); + if (!n) + break; + assert(n >= 0); + + // FIXME: apparently writing can't fail. + ops_write(buffer, n, cinfo); + } + + + // tidy up + close(fd_in); + ops_teardown_file_write(cinfo, fd_out); + + return ops_true; + } + +/** + \ingroup HighLevel_Crypto + Encrypt a compressed, signed stream. + \param cinfo the structure describing where the output will be written. + \param public_key the key used to encrypt the data + \param secret_key the key used to sign the data. If NULL, the data + will not be signed + \param compress If true, compress the stream before encrypting + \param use_armour Write armoured text, if set + \see ops_setup_file_write + + Example Code: + \code + const char* filename = "armour_nocompress_sign.asc"; + ops_create_info_t *info; + int fd = ops_setup_file_write(&info, filename, ops_true); + if (fd < 0) { + fprintf(stderr, "Cannot write to %s\n", filename); + return -1; + } + ops_encrypt_stream(info, public_key, secret_key, ops_false, ops_true); + ops_write(cleartext, strlen(cleartext), info); + ops_writer_close(info); + ops_create_info_delete(info); + \endcode +*/ +extern void ops_encrypt_stream(ops_create_info_t* cinfo, + const ops_keydata_t* public_key, + const ops_secret_key_t* secret_key, + const ops_boolean_t compress, + const ops_boolean_t use_armour) + { + if (use_armour) + ops_writer_push_armoured_message(cinfo); + ops_writer_push_stream_encrypt_se_ip(cinfo, public_key); + if (compress) + ops_writer_push_compressed(cinfo); + if (secret_key != NULL) + ops_writer_push_signed(cinfo, OPS_SIG_BINARY, secret_key); + else + ops_writer_push_literal(cinfo); + } + +/** + \ingroup HighLevel_Crypto + \brief Decrypt a chunk of memory, containing a encrypted stream. + \param input_filename Name of file to be decrypted + \param output_filename 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 cb_get_passphrase Callback to use to get passphrase +*/ + +ops_boolean_t ops_decrypt_memory(const unsigned char *encrypted_memory,int em_length, + unsigned char **decrypted_memory,int *out_length, + ops_keyring_t* keyring, + const ops_boolean_t use_armour, + ops_parse_cb_t* cb_get_passphrase) +{ + int fd_in=0; + int fd_out=0; + char* myfilename=NULL; + + // + ops_parse_info_t *pinfo=NULL; + + // setup for reading from given input file + + ops_memory_t *input_mem = ops_memory_new() ; + ops_memory_add(input_mem,encrypted_memory,em_length) ; + + ops_setup_memory_read(&pinfo, input_mem, NULL, callback_write_parsed, ops_false); + + if (pinfo == NULL) + { + perror("cannot create memory read"); + return ops_false; + } + + // setup memory chunk + + ops_memory_t *output_mem; + + ops_setup_memory_write(&pinfo->cbinfo.cinfo, &output_mem,0) ; + + if (output_mem == NULL) + { + perror("Cannot create output memory"); + ops_teardown_memory_read(pinfo, input_mem); + return ops_false; + } + + // \todo check for suffix matching armour param + + // setup keyring and passphrase callback + pinfo->cbinfo.cryptinfo.keyring=keyring; + pinfo->cbinfo.cryptinfo.cb_get_passphrase=cb_get_passphrase; + + // Set up armour/passphrase options + + if (use_armour) + ops_reader_push_dearmour(pinfo); + + // Do it + + ops_parse_and_print_errors(pinfo); + + // Unsetup + + if (use_armour) + ops_reader_pop_dearmour(pinfo); + + ops_boolean_t res = ops_true ; + + // copy output memory to supplied buffer. + // + *out_length = ops_memory_get_length(output_mem) ; + *decrypted_memory = ops_mallocz(*out_length) ; + memcpy(*decrypted_memory,ops_memory_get_data(output_mem),*out_length) ; + +ops_decrypt_memory_ABORT: + ops_teardown_memory_write(pinfo->cbinfo.cinfo, output_mem); + ops_teardown_memory_read(pinfo, input_mem); + + return res ; +} + +/** + \ingroup HighLevel_Crypto + \brief Decrypt a file. + \param input_filename Name of file to be decrypted + \param output_filename 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 cb_get_passphrase Callback to use to get passphrase +*/ + +ops_boolean_t ops_decrypt_file(const char* input_filename, + const char* output_filename, + ops_keyring_t* keyring, + const ops_boolean_t use_armour, + const ops_boolean_t allow_overwrite, + ops_parse_cb_t* cb_get_passphrase) +{ + int fd_in=0; + int fd_out=0; + char* myfilename=NULL; + + // + ops_parse_info_t *pinfo=NULL; + + // setup for reading from given input file + fd_in=ops_setup_file_read(&pinfo, input_filename, + NULL, + callback_write_parsed, + ops_false); + if (fd_in < 0) + { + perror(input_filename); + return ops_false; + } + + // setup output filename + + if (output_filename) + { + fd_out=ops_setup_file_write(&pinfo->cbinfo.cinfo, output_filename, + allow_overwrite); + + if (fd_out < 0) + { + perror(output_filename); + ops_teardown_file_read(pinfo, fd_in); + return ops_false; + } + } + else + { + int suffixlen=4; + char *defaultsuffix=".decrypted"; + const char *suffix=input_filename+strlen(input_filename)-suffixlen; + if (!strcmp(suffix, ".gpg") || !strcmp(suffix, ".asc")) + { + myfilename=ops_mallocz(strlen(input_filename)-suffixlen+1); + strncpy(myfilename, input_filename, + strlen(input_filename)-suffixlen); + } + else + { + unsigned filenamelen=strlen(input_filename)+strlen(defaultsuffix)+1; + myfilename=ops_mallocz(filenamelen); + snprintf(myfilename, filenamelen, "%s%s", input_filename, + defaultsuffix); + } + + fd_out=ops_setup_file_write(&pinfo->cbinfo.cinfo, myfilename, + allow_overwrite); + + if (fd_out < 0) + { + perror(myfilename); + free(myfilename); + ops_teardown_file_read(pinfo, fd_in); + return ops_false; + } + + free (myfilename); + } + + // \todo check for suffix matching armour param + + // setup for writing decrypted contents to given output file + + // setup keyring and passphrase callback + pinfo->cbinfo.cryptinfo.keyring=keyring; + pinfo->cbinfo.cryptinfo.cb_get_passphrase=cb_get_passphrase; + + // Set up armour/passphrase options + + if (use_armour) + ops_reader_push_dearmour(pinfo); + + // Do it + + ops_parse_and_print_errors(pinfo); + + // Unsetup + + if (use_armour) + ops_reader_pop_dearmour(pinfo); + + ops_teardown_file_write(pinfo->cbinfo.cinfo, fd_out); + ops_teardown_file_read(pinfo, fd_in); + // \todo cleardown crypt + + return ops_true; +} +static ops_parse_cb_return_t +callback_write_parsed(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content + =(ops_parser_content_union_t *)&content_->content; + static ops_boolean_t skipping; + // ops_boolean_t write=ops_true; + + OPS_USED(cbinfo); + +// ops_print_packet(content_); + + if(content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT && skipping) + { + puts("...end of skip"); + skipping=ops_false; + } + + switch(content_->tag) + { + case OPS_PTAG_CT_UNARMOURED_TEXT: + printf("OPS_PTAG_CT_UNARMOURED_TEXT\n"); + if(!skipping) + { + puts("Skipping..."); + skipping=ops_true; + } + fwrite(content->unarmoured_text.data, 1, + content->unarmoured_text.length, stdout); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + return callback_pk_session_key(content_, cbinfo); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + return callback_cmd_get_secret_key(content_, cbinfo); + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + case OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD: + // return callback_cmd_get_secret_key_passphrase(content_,cbinfo); + return cbinfo->cryptinfo.cb_get_passphrase(content_, cbinfo); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + return callback_literal_data(content_, cbinfo); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + case OPS_PTAG_CT_COMPRESSED: + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_BODY: + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + case OPS_PTAG_CT_SE_DATA_BODY: + case OPS_PTAG_CT_SE_DATA_HEADER: + + // Ignore these packets + // They're handled in ops_parse_one_packet() + // and nothing else needs to be done + break; + + default: + // return callback_general(content_,cbinfo); + break; + // fprintf(stderr,"Unexpected packet tag=%d (0x%x)\n",content_->tag, + // content_->tag); + // assert(0); + } + + return OPS_RELEASE_MEMORY; + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/crypto.h b/openpgpsdk/src/openpgpsdk/crypto.h new file mode 100644 index 000000000..12a4cf0fb --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/crypto.h @@ -0,0 +1,183 @@ +/* + * 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 OPS_CRYPTO_H +#define OPS_CRYPTO_H + +#include "keyring.h" +#include "util.h" +#include "packet.h" +#include "packet-parse.h" +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x00908030L +# define OPENSSL_NO_CAMELLIA +#endif + +#define OPS_MIN_HASH_SIZE 16 + +typedef void ops_hash_init_t(ops_hash_t *hash); +typedef void ops_hash_add_t(ops_hash_t *hash,const unsigned char *data, + unsigned length); +typedef unsigned ops_hash_finish_t(ops_hash_t *hash,unsigned char *out); + +/** _ops_hash_t */ +struct _ops_hash_t + { + ops_hash_algorithm_t algorithm; + size_t size; + const char *name; + ops_hash_init_t *init; + ops_hash_add_t *add; + ops_hash_finish_t *finish; + void *data; + }; + +typedef void ops_crypt_set_iv_t(ops_crypt_t *crypt, + const unsigned char *iv); +typedef void ops_crypt_set_key_t(ops_crypt_t *crypt, + const unsigned char *key); +typedef void ops_crypt_init_t(ops_crypt_t *crypt); +typedef void ops_crypt_resync_t(ops_crypt_t *crypt); +typedef void ops_crypt_block_encrypt_t(ops_crypt_t *crypt,void *out, + const void *in); +typedef void ops_crypt_block_decrypt_t(ops_crypt_t *crypt,void *out, + const void *in); +typedef void ops_crypt_cfb_encrypt_t(ops_crypt_t *crypt,void *out, + const void *in, size_t count); +typedef void ops_crypt_cfb_decrypt_t(ops_crypt_t *crypt,void *out, + const void *in, size_t count); +typedef void ops_crypt_finish_t(ops_crypt_t *crypt); + +/** _ops_crypt_t */ +struct _ops_crypt_t + { + ops_symmetric_algorithm_t algorithm; + size_t blocksize; + size_t keysize; + ops_crypt_set_iv_t *set_iv; /* Call this before decrypt init! */ + ops_crypt_set_key_t *set_key; /* Call this before init! */ + ops_crypt_init_t *base_init; + ops_crypt_resync_t *decrypt_resync; + // encrypt/decrypt one block + ops_crypt_block_encrypt_t *block_encrypt; + ops_crypt_block_decrypt_t *block_decrypt; + + // Standard CFB encrypt/decrypt (as used by Sym Enc Int Prot packets) + ops_crypt_cfb_encrypt_t *cfb_encrypt; + ops_crypt_cfb_decrypt_t *cfb_decrypt; + + ops_crypt_finish_t *decrypt_finish; + unsigned char iv[OPS_MAX_BLOCK_SIZE]; + unsigned char civ[OPS_MAX_BLOCK_SIZE]; + unsigned char siv[OPS_MAX_BLOCK_SIZE]; /* Needed for weird v3 resync */ + unsigned char key[OPS_MAX_KEY_SIZE]; + size_t num; /* Offset - see openssl _encrypt doco */ + void *encrypt_key; + void *decrypt_key; + }; + +void ops_crypto_init(void); +void ops_crypto_finish(void); +void ops_hash_md5(ops_hash_t *hash); +void ops_hash_sha1(ops_hash_t *hash); +void ops_hash_sha256(ops_hash_t *hash); +void ops_hash_sha512(ops_hash_t *hash); +void ops_hash_sha384(ops_hash_t *hash); +void ops_hash_sha224(ops_hash_t *hash); +void ops_hash_any(ops_hash_t *hash,ops_hash_algorithm_t alg); +ops_hash_algorithm_t ops_hash_algorithm_from_text(const char *hash); +const char *ops_text_from_hash(ops_hash_t *hash); +unsigned ops_hash_size(ops_hash_algorithm_t alg); +unsigned ops_hash(unsigned char *out,ops_hash_algorithm_t alg,const void *in, + size_t length); + +void ops_hash_add_int(ops_hash_t *hash,unsigned n,unsigned length); + +ops_boolean_t ops_dsa_verify(const unsigned char *hash,size_t hash_length, + const ops_dsa_signature_t *sig, + const ops_dsa_public_key_t *dsa); +int ops_rsa_public_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa); +int ops_rsa_public_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa); +int ops_rsa_private_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa); +int ops_rsa_private_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa); + +unsigned ops_block_size(ops_symmetric_algorithm_t alg); +unsigned ops_key_size(ops_symmetric_algorithm_t alg); + +int ops_decrypt_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *parse_info); + +int ops_crypt_any(ops_crypt_t *decrypt,ops_symmetric_algorithm_t alg); +void ops_decrypt_init(ops_crypt_t *decrypt); +void ops_encrypt_init(ops_crypt_t *encrypt); +size_t ops_decrypt_se(ops_crypt_t *decrypt,void *out,const void *in, + size_t count); +size_t ops_encrypt_se(ops_crypt_t *encrypt,void *out,const void *in, + size_t count); +size_t ops_decrypt_se_ip(ops_crypt_t *decrypt,void *out,const void *in, + size_t count); +size_t ops_encrypt_se_ip(ops_crypt_t *encrypt,void *out,const void *in, + size_t count); +ops_boolean_t ops_is_sa_supported(ops_symmetric_algorithm_t alg); + +void ops_reader_push_decrypt(ops_parse_info_t *pinfo,ops_crypt_t *decrypt, + ops_region_t *region); +void ops_reader_pop_decrypt(ops_parse_info_t *pinfo); + +// Hash everything that's read +void ops_reader_push_hash(ops_parse_info_t *pinfo,ops_hash_t *hash); +void ops_reader_pop_hash(ops_parse_info_t *pinfo); + +int ops_decrypt_and_unencode_mpi(unsigned char *buf,unsigned buflen,const BIGNUM *encmpi, + const ops_secret_key_t *skey); +ops_boolean_t ops_rsa_encrypt_mpi(const unsigned char *buf, const size_t buflen, + const ops_public_key_t *pkey, + ops_pk_session_key_parameters_t *spk); + + +// Encrypt everything that's written +struct ops_key_data; +void ops_writer_push_encrypt(ops_create_info_t *info, + const struct ops_key_data *key); + +ops_boolean_t ops_encrypt_file(const char* input_filename, const char* output_filename, const ops_keydata_t *pub_key, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite); +ops_boolean_t ops_decrypt_file(const char* input_filename, const char* output_filename, ops_keyring_t *keyring, const ops_boolean_t use_armour, const ops_boolean_t allow_overwrite,ops_parse_cb_t* cb_get_passphrase); +extern void ops_encrypt_stream(ops_create_info_t* cinfo, const ops_keydata_t* public_key, const ops_secret_key_t* secret_key, const ops_boolean_t compress, const ops_boolean_t use_armour); +ops_boolean_t ops_decrypt_memory(const unsigned char *encrypted_memory,int em_length, unsigned char **decrypted_memory,int *out_length, ops_keyring_t* keyring, const ops_boolean_t use_armour, ops_parse_cb_t* cb_get_passphrase) ; +// Keys +ops_boolean_t ops_rsa_generate_keypair(const int numbits, const unsigned long e, ops_keydata_t* keydata); +ops_keydata_t* ops_rsa_create_selfsigned_keypair(const int numbits, const unsigned long e, ops_user_id_t * userid); + +int ops_dsa_size(const ops_dsa_public_key_t *dsa); +DSA_SIG* ops_dsa_sign(unsigned char* hashbuf, unsigned hashsize, const ops_dsa_secret_key_t *sdsa, const ops_dsa_public_key_t *dsa); +#endif diff --git a/openpgpsdk/src/openpgpsdk/defs.h b/openpgpsdk/src/openpgpsdk/defs.h new file mode 100644 index 000000000..e4f8b48ba --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/defs.h @@ -0,0 +1,37 @@ +/* + * 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 OPS_DEFS_H +#define OPS_DEFS_H + +#define OPS_ARMOURED ops_true +#define OPS_UNARMOURED ops_false + +#define OPS_OVERWRITE_YES ops_true +#define OPS_OVERWRITE_NO ops_false + +#define OPS_ACCUMULATE_YES ops_true +#define OPS_ACCUMULATE_NO ops_false + +#endif /* OPS_DEFS_H */ diff --git a/openpgpsdk/src/openpgpsdk/errors.c b/openpgpsdk/src/openpgpsdk/errors.c new file mode 100644 index 000000000..2de42341b --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/errors.c @@ -0,0 +1,233 @@ +/* + * 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 Error Handling + */ + +#include +#include + +#include +#include +#include +#include + +#ifdef WIN32 +#define vsnprintf _vsnprintf +#endif + +#include +#include + +#define ERRNAME(code) { code, #code } + +static ops_errcode_name_map_t errcode_name_map[] = + { + ERRNAME(OPS_E_OK), + ERRNAME(OPS_E_FAIL), + ERRNAME(OPS_E_SYSTEM_ERROR), + ERRNAME(OPS_E_UNIMPLEMENTED), + + ERRNAME(OPS_E_R), + ERRNAME(OPS_E_R_READ_FAILED), + ERRNAME(OPS_E_R_EARLY_EOF), + ERRNAME(OPS_E_R_BAD_FORMAT), + ERRNAME(OPS_E_R_UNCONSUMED_DATA), + + ERRNAME(OPS_E_W), + ERRNAME(OPS_E_W_WRITE_FAILED), + ERRNAME(OPS_E_W_WRITE_TOO_SHORT), + + ERRNAME(OPS_E_P), + ERRNAME(OPS_E_P_NOT_ENOUGH_DATA), + ERRNAME(OPS_E_P_UNKNOWN_TAG), + ERRNAME(OPS_E_P_PACKET_CONSUMED), + ERRNAME(OPS_E_P_MPI_FORMAT_ERROR), + + ERRNAME(OPS_E_C), + + ERRNAME(OPS_E_V), + ERRNAME(OPS_E_V_BAD_SIGNATURE), + ERRNAME(OPS_E_V_NO_SIGNATURE), + ERRNAME(OPS_E_V_UNKNOWN_SIGNER), + + ERRNAME(OPS_E_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG), + ERRNAME(OPS_E_ALG_UNSUPPORTED_HASH_ALG), + + ERRNAME(OPS_E_PROTO), + ERRNAME(OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT), + ERRNAME(OPS_E_PROTO_UNKNOWN_SS), + ERRNAME(OPS_E_PROTO_CRITICAL_SS_IGNORED), + ERRNAME(OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN), + ERRNAME(OPS_E_PROTO_BAD_SIGNATURE_VRSN), + ERRNAME(OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN), + ERRNAME(OPS_E_PROTO_BAD_PKSK_VRSN), + ERRNAME(OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN), + ERRNAME(OPS_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" + */ +char *ops_errcode(const ops_errcode_t errcode) + { + return(ops_str_from_map((int) errcode, (ops_map_t *) errcode_name_map)); + } + +/** + * \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=OPS_E_SYSTEM_ERROR) + * \param file Source filename where error occurred + * \param line Line in source file where error occurred + * \param fmt Comment + * + */ + +void ops_push_error(ops_error_t **errstack,ops_errcode_t errcode,int sys_errno, + const char *file,int line,const char *fmt,...) + { + // first get the varargs and generate the comment + char *comment; + int maxbuf=128; + va_list args; + ops_error_t *err; + + comment=malloc(maxbuf+1); + assert(comment); + + 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 + + err=malloc(sizeof(ops_error_t)); + assert(err); + + 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 ops_print_error(ops_error_t *err) + { + printf("%s:%d: ",err->file,err->line); + if(err->errcode==OPS_E_SYSTEM_ERROR) + printf("system error %d returned from %s()\n",err->sys_errno, + err->comment); + else + printf("%s, %s\n",ops_errcode(err->errcode),err->comment); + } + +/** +\ingroup Core_Errors +\brief Print all errors on stack +\param errstack Error stack to print +*/ +void ops_print_errors(ops_error_t *errstack) + { + ops_error_t *err; + + for(err=errstack ; err!=NULL ; err=err->next) + ops_print_error(err); + } + +/** +\ingroup Core_Errors +\brief Return true 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 ops_has_error(ops_error_t *errstack, ops_errcode_t errcode) + { + ops_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 ops_free_errors(ops_error_t *errstack) +{ + ops_error_t *next; + while(errstack!=NULL) { + next=errstack->next; + free(errstack->comment); + free(errstack); + errstack=next; + } +} + +/** +\ingroup InternalAPI +\brief Moves errors from a create info structure to another error stack. + +The error stack wil be moved from the source structure to the destination +stack. If the destination already has errors defined, the errors will +be appended. +*/ +void ops_move_errors(ops_create_info_t *source, ops_error_t **dest) + { + if (*dest == NULL) + *dest = source->errors; + else + { + ops_error_t *last = *dest; + while(last->next != NULL) + last = last->next; + last->next = source->errors; + } + source->errors = NULL; + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/errors.h b/openpgpsdk/src/openpgpsdk/errors.h new file mode 100644 index 000000000..6ff958c2d --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/errors.h @@ -0,0 +1,126 @@ +/* + * 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 OPS_ERRORS +#define OPS_ERRORS + +#include "openpgpsdk/types.h" +#include + +/** error codes */ +// Remember to add names to map in errors.c +typedef enum + { + OPS_E_OK=0x0000, /* no error */ + OPS_E_FAIL=0x0001, /* general error */ + OPS_E_SYSTEM_ERROR=0x0002, /* system error, look at errno for details */ + OPS_E_UNIMPLEMENTED=0x0003, /* feature not yet implemented */ + + /* reader errors */ + OPS_E_R=0x1000, /* general reader error */ + OPS_E_R_READ_FAILED =OPS_E_R+1, + OPS_E_R_EARLY_EOF =OPS_E_R+2, + OPS_E_R_BAD_FORMAT =OPS_E_R+3, // For example, malformed armour + OPS_E_R_UNSUPPORTED =OPS_E_R+4, + OPS_E_R_UNCONSUMED_DATA =OPS_E_R+5, + + /* writer errors */ + OPS_E_W=0x2000, /* general writer error */ + OPS_E_W_WRITE_FAILED = OPS_E_W+1, + OPS_E_W_WRITE_TOO_SHORT = OPS_E_W+2, + + /* parser errors */ + OPS_E_P=0x3000, /* general parser error */ + OPS_E_P_NOT_ENOUGH_DATA =OPS_E_P+1, + OPS_E_P_UNKNOWN_TAG =OPS_E_P+2, + OPS_E_P_PACKET_CONSUMED =OPS_E_P+3, + OPS_E_P_MPI_FORMAT_ERROR =OPS_E_P+4, + OPS_E_P_PACKET_NOT_CONSUMED =OPS_E_P+5, + OPS_E_P_DECOMPRESSION_ERROR =OPS_E_P+6, + OPS_E_P_NO_USERID =OPS_E_P+7, + + /* creator errors */ + OPS_E_C=0x4000, /* general creator error */ + + /* validation errors */ + OPS_E_V=0x5000, /* general validation error */ + OPS_E_V_BAD_SIGNATURE =OPS_E_V+1, + OPS_E_V_NO_SIGNATURE =OPS_E_V+2, + OPS_E_V_UNKNOWN_SIGNER =OPS_E_V+3, + OPS_E_V_BAD_HASH =OPS_E_V+4, + + /* Algorithm support errors */ + OPS_E_ALG=0x6000, /* general algorithm error */ + OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG =OPS_E_ALG+1, + OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG =OPS_E_ALG+2, + OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG =OPS_E_ALG+3, + OPS_E_ALG_UNSUPPORTED_HASH_ALG =OPS_E_ALG+4, + OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG =OPS_E_ALG+5, + + /* Protocol errors */ + OPS_E_PROTO=0x7000, /* general protocol error */ + OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT =OPS_E_PROTO+2, + OPS_E_PROTO_UNKNOWN_SS =OPS_E_PROTO+3, + OPS_E_PROTO_CRITICAL_SS_IGNORED =OPS_E_PROTO+4, + OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN =OPS_E_PROTO+5, + OPS_E_PROTO_BAD_SIGNATURE_VRSN =OPS_E_PROTO+6, + OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN =OPS_E_PROTO+7, + OPS_E_PROTO_BAD_PKSK_VRSN =OPS_E_PROTO+8, + OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN =OPS_E_PROTO+9, + OPS_E_PROTO_BAD_SK_CHECKSUM =OPS_E_PROTO+10, + } ops_errcode_t; + +/** ops_errcode_name_map_t */ +typedef ops_map_t ops_errcode_name_map_t; + +/** one entry in a linked list of errors */ +typedef struct ops_error + { + ops_errcode_t errcode; + int sys_errno; /*!< irrelevent unless errcode == OPS_E_SYSTEM_ERROR */ + char *comment; + const char *file; + int line; + struct ops_error *next; + } ops_error_t; + +char *ops_errcode(const ops_errcode_t errcode); + +void ops_push_error(ops_error_t **errstack,ops_errcode_t errcode,int sys_errno, + const char *file,int line,const char *comment,...); +void ops_print_error(ops_error_t *err); +void ops_print_errors(ops_error_t *errstack); +void ops_free_errors(ops_error_t *errstack); +int ops_has_error(ops_error_t *errstack, ops_errcode_t errcode); +void ops_move_errors(ops_create_info_t *source, ops_error_t **errstack); + +#define OPS_SYSTEM_ERROR_1(err,code,syscall,fmt,arg) do { ops_push_error(err,OPS_E_SYSTEM_ERROR,errno,__FILE__,__LINE__,syscall); ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg); } while(0) +#define OPS_MEMORY_ERROR(err) {fprintf(stderr, "Memory error\n");} // \todo placeholder for better error handling +#define OPS_ERROR(err,code,fmt) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt); } while(0) +#define OPS_ERROR_1(err,code,fmt,arg) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg); } while(0) +#define OPS_ERROR_2(err,code,fmt,arg,arg2) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2); } while(0) +#define OPS_ERROR_3(err,code,fmt,arg,arg2,arg3) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3); } while(0) +#define OPS_ERROR_4(err,code,fmt,arg,arg2,arg3,arg4) do { ops_push_error(err,code,0,__FILE__,__LINE__,fmt,arg,arg2,arg3,arg4); } while(0) + +#endif /* OPS_ERRORS */ diff --git a/openpgpsdk/src/openpgpsdk/final.h b/openpgpsdk/src/openpgpsdk/final.h new file mode 100644 index 000000000..404391d2f --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/final.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/* A header that is always included last, for things that need to come last */ + +#ifdef DMALLOC +# include +#endif + diff --git a/openpgpsdk/src/openpgpsdk/fingerprint.c b/openpgpsdk/src/openpgpsdk/fingerprint.c new file mode 100644 index 000000000..61702f292 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/fingerprint.c @@ -0,0 +1,139 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#if HAVE_ALLOCA_H +# include +#endif + +#ifdef WIN32 +#define alloca _alloca +#endif + +#include + +static int debug=0; + +/** + * \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 + */ + +void ops_fingerprint(ops_fingerprint_t *fp,const ops_public_key_t *key) + { + if(key->version == 2 || key->version == 3) + { + unsigned char *bn; + int n; + ops_hash_t md5; + + assert(key->algorithm == OPS_PKA_RSA + || key->algorithm == OPS_PKA_RSA_ENCRYPT_ONLY + || key->algorithm == OPS_PKA_RSA_SIGN_ONLY ); + + ops_hash_md5(&md5); + md5.init(&md5); + + n=BN_num_bytes(key->key.rsa.n); + bn=alloca(n); + BN_bn2bin(key->key.rsa.n,bn); + md5.add(&md5,bn,n); + + n=BN_num_bytes(key->key.rsa.e); + bn=alloca(n); + BN_bn2bin(key->key.rsa.e,bn); + md5.add(&md5,bn,n); + + md5.finish(&md5,fp->fingerprint); + fp->length=16; + } + else + { + ops_memory_t *mem=ops_memory_new(); + ops_hash_t sha1; + size_t l; + + ops_build_public_key(mem,key,ops_false); + + if (debug) + { fprintf(stderr,"--- creating key fingerprint\n"); } + + ops_hash_sha1(&sha1); + sha1.init(&sha1); + + l=ops_memory_get_length(mem); + + ops_hash_add_int(&sha1,0x99,1); + ops_hash_add_int(&sha1,l,2); + sha1.add(&sha1,ops_memory_get_data(mem),l); + sha1.finish(&sha1,fp->fingerprint); + + if (debug) + { fprintf(stderr,"--- finished creating key fingerprint\n"); } + + fp->length=20; + + ops_memory_free(mem); + } + } + +/** + * \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 + */ + +void ops_keyid(unsigned char keyid[8],const ops_public_key_t *key) + { + if(key->version == 2 || key->version == 3) + { + unsigned char bn[8192]; + unsigned n=BN_num_bytes(key->key.rsa.n); + + assert(n <= sizeof bn); + assert(key->algorithm == OPS_PKA_RSA + || key->algorithm == OPS_PKA_RSA_ENCRYPT_ONLY + || key->algorithm == OPS_PKA_RSA_SIGN_ONLY ); + BN_bn2bin(key->key.rsa.n,bn); + memcpy(keyid,bn+n-8,8); + } + else + { + ops_fingerprint_t fingerprint; + + ops_fingerprint(&fingerprint,key); + memcpy(keyid,fingerprint.fingerprint+fingerprint.length-8,8); + } + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/hash.c b/openpgpsdk/src/openpgpsdk/hash.c new file mode 100644 index 000000000..5154f46ac --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/hash.c @@ -0,0 +1,249 @@ +/* + * 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 +#include +#include + +#include + +static int debug=0; + +/** +\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 ops_hash_add_int(ops_hash_t *hash,unsigned n,unsigned length) + { + while(length--) + { + unsigned char c[1]; + + c[0]=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 +*/ +void ops_hash_any(ops_hash_t *hash,ops_hash_algorithm_t alg) + { + switch(alg) + { + case OPS_HASH_MD5: + ops_hash_md5(hash); + break; + + case OPS_HASH_SHA1: + ops_hash_sha1(hash); + break; + + case OPS_HASH_SHA256: + ops_hash_sha256(hash); + break; + + case OPS_HASH_SHA384: + ops_hash_sha384(hash); + break; + + case OPS_HASH_SHA512: + ops_hash_sha512(hash); + break; + + case OPS_HASH_SHA224: + ops_hash_sha224(hash); + break; + + default: + assert(0); + } + } + +/** +\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 ops_hash_size(ops_hash_algorithm_t alg) + { + switch(alg) + { + case OPS_HASH_MD5: + return 16; + + case OPS_HASH_SHA1: + return 20; + + case OPS_HASH_SHA256: + return 32; + + case OPS_HASH_SHA224: + return 28; + + case OPS_HASH_SHA512: + return 64; + + case OPS_HASH_SHA384: + return 48; + + default: + assert(0); + } + + 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. OPS_HASH_SHA1 +*/ +ops_hash_algorithm_t ops_hash_algorithm_from_text(const char *hash) + { + if(!strcmp(hash,"SHA1")) + return OPS_HASH_SHA1; + else if(!strcmp(hash,"MD5")) + return OPS_HASH_MD5; + else if (!strcmp(hash,"SHA256")) + return OPS_HASH_SHA256; + /* + else if (!strcmp(hash,"SHA224")) + return OPS_HASH_SHA224; + */ + else if (!strcmp(hash,"SHA512")) + return OPS_HASH_SHA512; + else if (!strcmp(hash,"SHA384")) + return OPS_HASH_SHA384; + + return OPS_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 ops_hash(unsigned char *out,ops_hash_algorithm_t alg,const void *in, + size_t length) + { + ops_hash_t hash; + + ops_hash_any(&hash,alg); + hash.init(&hash); + hash.add(&hash,in,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 ops_calc_mdc_hash(const unsigned char* preamble, const size_t sz_preamble, const unsigned char* plaintext, const unsigned int sz_plaintext, unsigned char *hashed) + { + ops_hash_t hash; + unsigned char c[1]; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"ops_calc_mdc_hash():\n"); + + fprintf(stderr,"\npreamble: "); + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keyring_local.h" +#include "parse_local.h" + +#include +#include +#ifndef WIN32 +#include +#include +#endif +#include +#include + +#include + +/** + \ingroup HighLevel_Keyring + + \brief Creates a new ops_keydata_t struct + + \return A new ops_keydata_t struct, initialised to zero. + + \note The returned ops_keydata_t struct must be freed after use with ops_keydata_free. +*/ + +ops_keydata_t *ops_keydata_new(void) + { return ops_mallocz(sizeof(ops_keydata_t)); } + + +// Frees the content of a keydata structure, but not the keydata itself. +static void keydata_internal_free(ops_keydata_t *keydata) + { + unsigned n; + + for(n=0 ; n < keydata->nuids ; ++n) + ops_user_id_free(&keydata->uids[n]); + free(keydata->uids); + keydata->uids=NULL; + keydata->nuids=0; + + for(n=0 ; n < keydata->npackets ; ++n) + ops_packet_free(&keydata->packets[n]); + free(keydata->packets); + keydata->packets=NULL; + keydata->npackets=0; + +/* for(n=0;nnsigs;++n) + { + ops_user_id_free(keydata->sigs[n].userid) ; + ops_packet_free(keydata->sigs[n].packet) ; + }*/ + free(keydata->sigs) ; + + if(keydata->type == OPS_PTAG_CT_PUBLIC_KEY) + ops_public_key_free(&keydata->key.pkey); + else + ops_secret_key_free(&keydata->key.skey); + + } + +/** + \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 ops_keydata_free(ops_keydata_t *keydata) + { + keydata_internal_free(keydata); + free(keydata); + } + +// \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 user_id, it will be freed. +*/ +void ops_copy_userid(ops_user_id_t* dst, const ops_user_id_t* src) + { + int len=strlen((char *)src->user_id); + if (dst->user_id) + free(dst->user_id); + dst->user_id=ops_mallocz(len+1); + + memcpy(dst->user_id, src->user_id, len); + } +// \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. +*/ +void ops_copy_packet(ops_packet_t* dst, const ops_packet_t* src) + { + if (dst->raw) + free(dst->raw); + dst->raw=ops_mallocz(src->length); + + dst->length=src->length; + memcpy(dst->raw, src->raw, src->length); + } + + + +/** +\ingroup Core_Keys +\brief Copies entire key data +\param dst Destination key where to copy +\param src Source key to copy +*/ +void ops_keydata_copy(ops_keydata_t *dst,const ops_keydata_t *src) +{ + unsigned n; + + memset(dst,0,sizeof(ops_keydata_t)) ; + + dst->uids = (ops_user_id_t*)ops_mallocz(src->nuids * sizeof(ops_user_id_t)) ; + dst->nuids = src->nuids ; + dst->nuids_allocated = src->nuids ; + + for(n=0 ; n < src->nuids ; ++n) + ops_copy_userid(&dst->uids[n],&src->uids[n]) ; + + dst->packets = (ops_packet_t*)ops_mallocz(src->npackets * sizeof(ops_packet_t)) ; + dst->npackets = src->npackets ; + dst->npackets_allocated = src->npackets ; + + for(n=0 ; n < src->npackets ; ++n) + ops_copy_packet(&(dst->packets[n]),&(src->packets[n])); + + dst->nsigs = src->nsigs ; + dst->sigs = (sigpacket_t*)ops_mallocz(src->nsigs * sizeof(sigpacket_t)) ; + dst->nsigs_allocated = src->nsigs ; + + for(n=0 ; n < src->nsigs ; ++n) + { + dst->sigs[n].userid = (ops_user_id_t*)ops_mallocz(sizeof(ops_user_id_t)) ; + dst->sigs[n].packet = (ops_packet_t*)ops_mallocz(sizeof(ops_packet_t)) ; + + ops_copy_userid(dst->sigs[n].userid,src->sigs[n].userid) ; + ops_copy_packet(dst->sigs[n].packet,src->sigs[n].packet) ; + } + + dst->key_id[0] = src->key_id[0] ; + dst->key_id[1] = src->key_id[1] ; + dst->key_id[2] = src->key_id[2] ; + dst->key_id[3] = src->key_id[3] ; + dst->key_id[4] = src->key_id[4] ; + dst->key_id[5] = src->key_id[5] ; + dst->key_id[6] = src->key_id[6] ; + dst->key_id[7] = src->key_id[7] ; + dst->type = src->type ; + dst->key = src->key ; + dst->fingerprint = src->fingerprint ; + + if(src->type == OPS_PTAG_CT_PUBLIC_KEY) + ops_public_key_copy(&dst->key.pkey,&src->key.pkey); + else + ops_secret_key_copy(&dst->key.skey,&src->key.skey); +} + + +/** + \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. +*/ + +const ops_public_key_t * +ops_get_public_key_from_data(const ops_keydata_t *keydata) + { + if(keydata->type == OPS_PTAG_CT_PUBLIC_KEY) + return &keydata->key.pkey; + return &keydata->key.skey.public_key; + } + +/** +\ingroup HighLevel_KeyGeneral + +\brief Check whether this is a secret key or not. +*/ + +ops_boolean_t ops_is_key_secret(const ops_keydata_t *data) + { return data->type != OPS_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 ops_get_writable_secret_key_from_data +*/ + +const ops_secret_key_t * +ops_get_secret_key_from_data(const ops_keydata_t *data) + { + if(data->type != OPS_PTAG_CT_SECRET_KEY) + return NULL; + + return &data->key.skey; + } + +/** + \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 ops_get_secret_key_from_data. +*/ + +ops_secret_key_t * +ops_get_writable_secret_key_from_data(ops_keydata_t *data) + { + if (data->type != OPS_PTAG_CT_SECRET_KEY) + return NULL; + + return &data->key.skey; + } + +typedef struct + { + const ops_keydata_t *key; + char *pphrase; + ops_secret_key_t *skey; + } decrypt_arg_t; + +static ops_parse_cb_return_t decrypt_cb(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo) + { + const ops_parser_content_union_t *content=&content_->content; + decrypt_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + + OPS_USED(cbinfo); + + switch(content_->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_USER_ID: + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + case OPS_PTAG_CT_TRUST: + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + *content->secret_key_passphrase.passphrase=arg->pphrase; + return OPS_KEEP_MEMORY; + + case OPS_PARSER_ERRCODE: + switch(content->errcode.errcode) + { + case OPS_E_P_MPI_FORMAT_ERROR: + /* Generally this means a bad passphrase */ + fprintf(stderr,"Bad passphrase!\n"); + goto done; + + case OPS_E_P_PACKET_CONSUMED: + /* And this is because of an error we've accepted */ + goto done; + + default: + fprintf(stderr,"parse error: %s\n", + ops_errcode(content->errcode.errcode)); + assert(0); + break; + } + + break; + + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + assert(0); + break; + + case OPS_PTAG_CT_SECRET_KEY: + arg->skey=malloc(sizeof *arg->skey); + *arg->skey=content->secret_key; + return OPS_KEEP_MEMORY; + + case OPS_PARSER_PACKET_END: + // nothing to do + break; + + default: + fprintf(stderr,"Unexpected tag %d (0x%x)\n",content_->tag, + content_->tag); + assert(0); + } + + done: + return OPS_RELEASE_MEMORY; + } + +/** +\ingroup Core_Keys +\brief Decrypts secret key from given keydata with given passphrase +\param key Key from which to get secret key +\param pphrase Passphrase to use to decrypt secret key +\return secret key +*/ +ops_secret_key_t *ops_decrypt_secret_key_from_data(const ops_keydata_t *key, + const char *pphrase) + { + ops_parse_info_t *pinfo; + decrypt_arg_t arg; + + memset(&arg,'\0',sizeof arg); + arg.key=key; + arg.pphrase=strdup(pphrase); + + pinfo=ops_parse_info_new(); + + ops_keydata_reader_set(pinfo,key); + ops_parse_cb_set(pinfo,decrypt_cb,&arg); + pinfo->rinfo.accumulate=ops_true; + + ops_parse(pinfo); + + ops_parse_info_delete(pinfo); + + return arg.skey; + } + +/** +\ingroup Core_Keys +\brief Set secret key in content +\param content Content to be set +\param key Keydata to get secret key from +*/ +void ops_set_secret_key(ops_parser_content_union_t* content,const ops_keydata_t *key) + { + *content->get_secret_key.secret_key=&key->key.skey; + } + +/** +\ingroup Core_Keys +\brief Get Key ID from keydata +\param key Keydata to get Key ID from +\return Pointer to Key ID inside keydata +*/ +const unsigned char* ops_get_key_id(const ops_keydata_t *key) + { + return key->key_id; + } + +/** +\ingroup Core_Keys +\brief How many User IDs in this key? +\param key Keydata to check +\return Num of user ids +*/ +unsigned ops_get_user_id_count(const ops_keydata_t *key) + { + return key->nuids; + } + +/** +\ingroup Core_Keys +\brief Get indexed user id from key +\param key Key to get user id from +\param index Which key to get +\return Pointer to requested user id +*/ +const unsigned char* ops_get_user_id(const ops_keydata_t *key, unsigned index) + { + return key->uids[index].user_id; + } + +/** + \ingroup HighLevel_Supported + \brief Checks whether key's algorithm and type are supported by OpenPGP::SDK + \param keydata Key to be checked + \return ops_true if key algorithm and type are supported by OpenPGP::SDK; ops_false if not +*/ + +ops_boolean_t ops_is_key_supported(const ops_keydata_t *keydata) +{ + if(keydata->type == OPS_PTAG_CT_PUBLIC_KEY) + { + if(keydata->key.pkey.algorithm == OPS_PKA_RSA) + return ops_true; + } + return ops_false; +} + + +/** + \ingroup HighLevel_KeyringFind + + \brief Returns key inside a keyring, chosen by index + + \param keyring Pointer to existing keyring + \param index Index of required key + + \note Index starts at 0 + + \note This returns a pointer to the original key, not a copy. You do not need to free the key after use. + + \return Pointer to the required key; or NULL if index too large. + + Example code: + \code + void example(const ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + keydata=ops_keyring_get_key_by_index(keyring, 0); + ... + } + \endcode +*/ + +const ops_keydata_t* ops_keyring_get_key_by_index(const ops_keyring_t *keyring, int index) + { + if (index >= keyring->nkeys) + return NULL; + return &keyring->keys[index]; + } + +/** +\ingroup Core_Keys +\brief Add User ID to keydata +\param keydata Key to which to add User ID +\param userid User ID to add +\return Pointer to new User ID +*/ +ops_user_id_t* ops_add_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid) + { + ops_user_id_t* new_uid=NULL; + + EXPAND_ARRAY(keydata, uids); + + // initialise new entry in array + new_uid=&keydata->uids[keydata->nuids]; + + new_uid->user_id=NULL; + + // now copy it + ops_copy_userid(new_uid,userid); + keydata->nuids++; + + return new_uid; + } + +/** +\ingroup Core_Keys +\brief Add packet to key +\param keydata Key to which to add packet +\param packet Packet to add +\return Pointer to new packet +*/ +ops_packet_t* ops_add_packet_to_keydata(ops_keydata_t* keydata, const ops_packet_t* packet) + { + ops_packet_t* new_pkt=NULL; + + EXPAND_ARRAY(keydata, packets); + + // initialise new entry in array + new_pkt=&keydata->packets[keydata->npackets]; + new_pkt->length=0; + new_pkt->raw=NULL; + + // now copy it + ops_copy_packet(new_pkt, packet); + keydata->npackets++; + + return new_pkt; + } + +/** +\ingroup Core_Keys +\brief Add signed User ID to key +\param keydata Key to which to add signed User ID +\param user_id User ID to add +\param sigpacket Packet to add +*/ +void ops_add_signed_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* user_id, const ops_packet_t* sigpacket) + { + //int i=0; + ops_user_id_t * uid=NULL; + ops_packet_t * pkt=NULL; + + uid=ops_add_userid_to_keydata(keydata, user_id); + pkt=ops_add_packet_to_keydata(keydata, sigpacket); + + /* + * add entry in sigs array to link the userid and sigpacket + */ + + // and add ptr to it from the sigs array + EXPAND_ARRAY(keydata, sigs); + + // setup new entry in array + + keydata->sigs[keydata->nsigs].userid=uid; + keydata->sigs[keydata->nsigs].packet=pkt; + + keydata->nsigs++; + } + +/** +\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 ops_true if OK; else ops_false +*/ +ops_boolean_t ops_add_selfsigned_userid_to_keydata(ops_keydata_t* keydata, ops_user_id_t* userid) + { + ops_packet_t sigpacket; + + ops_memory_t* mem_userid=NULL; + ops_create_info_t* cinfo_userid=NULL; + + ops_memory_t* mem_sig=NULL; + ops_create_info_t* cinfo_sig=NULL; + + ops_create_signature_t *sig=NULL; + + /* + * create signature packet for this userid + */ + + // create userid pkt + ops_setup_memory_write(&cinfo_userid, &mem_userid, 128); + ops_write_struct_user_id(userid, cinfo_userid); + + // create sig for this pkt + + sig=ops_create_signature_new(); + ops_signature_start_key_signature(sig, &keydata->key.skey.public_key, userid, OPS_CERT_POSITIVE); + ops_signature_add_creation_time(sig,time(NULL)); + ops_signature_add_issuer_key_id(sig,keydata->key_id); + ops_signature_add_primary_user_id(sig, ops_true); + ops_signature_hashed_subpackets_end(sig); + + ops_setup_memory_write(&cinfo_sig, &mem_sig, 128); + ops_write_signature(sig,&keydata->key.skey.public_key,&keydata->key.skey, cinfo_sig); + + // add this packet to keydata + + sigpacket.length=ops_memory_get_length(mem_sig); + sigpacket.raw=ops_memory_get_data(mem_sig); + + // add userid to keydata + ops_add_signed_userid_to_keydata(keydata, userid, &sigpacket); + + // cleanup + ops_create_signature_delete(sig); + ops_create_info_delete(cinfo_userid); + ops_create_info_delete(cinfo_sig); + ops_memory_free(mem_userid); + ops_memory_free(mem_sig); + + return ops_true; + } + +/** +\ingroup Core_Keys +\brief Add signature to given key +\return ops_true if OK; else ops_false +*/ +ops_boolean_t ops_sign_key(ops_keydata_t* keydata, const unsigned char *signers_key_id,ops_secret_key_t *signers_key) +{ +/* ops_memory_t* mem_userid=NULL; */ + ops_create_info_t* cinfo_userid=NULL; + + ops_memory_t* mem_sig=NULL; + ops_create_info_t* cinfo_sig=NULL; + + ops_create_signature_t *sig=NULL; + + /* + * create signature packet for this userid + */ + + // create sig for this pkt + + sig=ops_create_signature_new(); + ops_signature_start_key_signature(sig, &keydata->key.skey.public_key, &keydata->uids[0], OPS_CERT_GENERIC); + ops_signature_add_creation_time(sig,time(NULL)); + ops_signature_add_issuer_key_id(sig,signers_key_id); + ops_signature_hashed_subpackets_end(sig); + + ops_setup_memory_write(&cinfo_sig, &mem_sig, 128); + ops_write_signature(sig,&signers_key->public_key,signers_key, cinfo_sig); + + // add this packet to keydata + + ops_packet_t sigpacket; + sigpacket.length=ops_memory_get_length(mem_sig); + sigpacket.raw=ops_memory_get_data(mem_sig); + + // add userid to keydata + ops_add_packet_to_keydata(keydata, &sigpacket); + + // cleanup + ops_create_signature_delete(sig); + ops_create_info_delete(cinfo_sig); + ops_memory_free(mem_sig); + + return ops_true; +} +/** +\ingroup Core_Keys +\brief Initialise ops_keydata_t +\param keydata Keydata to initialise +\param type OPS_PTAG_CT_PUBLIC_KEY or OPS_PTAG_CT_SECRET_KEY +*/ +void ops_keydata_init(ops_keydata_t* keydata, const ops_content_tag_t type) + { + assert(keydata->type==OPS_PTAG_CT_RESERVED); + assert(type==OPS_PTAG_CT_PUBLIC_KEY || type==OPS_PTAG_CT_SECRET_KEY); + + keydata->type=type; + } + +/** + Example Usage: + \code + + // definition of variables + ops_keyring_t keyring; + char* filename="~/.gnupg/pubring.gpg"; + + // Read keyring from file + ops_keyring_read_from_file(&keyring,filename); + + // do actions using keyring + ... + + // Free memory alloc-ed in ops_keyring_read_from_file() + ops_keyring_free(keyring); + \endcode +*/ + +static ops_parse_cb_return_t +cb_keyring_read(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo); + +/** + \ingroup HighLevel_KeyringRead + + \brief Reads a keyring from a file + + \param keyring Pointer to an existing ops_keyring_t struct + \param armour ops_true if file is armoured; else ops_false + \param filename Filename of keyring to be read + + \return ops true if OK; ops_false on error + + \note Keyring struct must already exist. + + \note Can be used with either a public or secret keyring. + + \note You must call ops_keyring_free() after usage to free alloc-ed memory. + + \note If you call this twice on the same keyring struct, without calling + ops_keyring_free() between these calls, you will introduce a memory leak. + + \sa ops_keyring_read_from_mem() + \sa ops_keyring_free() + + Example code: + \code + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_file(keyring, armoured, "~/.gnupg/pubring.gpg"); + ... + ops_keyring_free(keyring); + free (keyring); + + \endcode +*/ + +ops_boolean_t ops_keyring_read_from_file(ops_keyring_t *keyring, const ops_boolean_t armour, const char *filename) + { + ops_parse_info_t *pinfo; + int fd; + ops_boolean_t res = ops_true; + + pinfo=ops_parse_info_new(); + + // add this for the moment, + // \todo need to fix the problems with reading signature subpackets later + + // ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_RAW); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + + fd=open(filename,O_RDONLY | O_BINARY); + + if(fd < 0) + { + ops_parse_info_delete(pinfo); + perror(filename); + return ops_false; + } + + ops_reader_set_fd(pinfo,fd); + + ops_parse_cb_set(pinfo,cb_keyring_read,NULL); + + if (armour) + { ops_reader_push_dearmour(pinfo); } + + if ( ops_parse_and_accumulate(keyring,pinfo) == 0 ) { + res = ops_false; + } + else + { + res = ops_true; + } + ops_print_errors(ops_parse_info_get_errors(pinfo)); + + if (armour) + ops_reader_pop_dearmour(pinfo); + + close(fd); + + ops_parse_info_delete(pinfo); + + return res; + } + +/** + \ingroup HighLevel_KeyringRead + + \brief Reads a keyring from memory + + \param keyring Pointer to existing ops_keyring_t struct + \param armour ops_true if file is armoured; else ops_false + \param mem Pointer to a ops_memory_t struct containing keyring to be read + + \return ops true if OK; ops_false on error + + \note Keyring struct must already exist. + + \note Can be used with either a public or secret keyring. + + \note You must call ops_keyring_free() after usage to free alloc-ed memory. + + \note If you call this twice on the same keyring struct, without calling + ops_keyring_free() between these calls, you will introduce a memory leak. + + \sa ops_keyring_read_from_file + \sa ops_keyring_free + + Example code: + \code + ops_memory_t* mem; // Filled with keyring packets + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_mem(keyring, armoured, mem); + ... + ops_keyring_free(keyring); + free (keyring); + \endcode +*/ +ops_boolean_t ops_keyring_read_from_mem(ops_keyring_t *keyring, const ops_boolean_t armour, ops_memory_t* mem) + { + ops_parse_info_t *pinfo=NULL; + ops_boolean_t res = ops_true; + + ops_setup_memory_read(&pinfo, mem, NULL, cb_keyring_read, + OPS_ACCUMULATE_NO); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + + if (armour) + { ops_reader_push_dearmour(pinfo); } + + if ( ops_parse_and_accumulate(keyring,pinfo) == 0 ) + { + res = ops_false; + } + else + { + res = ops_true; + } + ops_print_errors(ops_parse_info_get_errors(pinfo)); + + if (armour) + ops_reader_pop_dearmour(pinfo); + + // don't call teardown_memory_read because memory was passed + // in. But we need to free the parse_info object allocated by + // ops_setup_memory_read(). + ops_parse_info_delete(pinfo); + + return res; + } + +/** + \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 ops_keyring_free(ops_keyring_t *keyring) + { + int i; + + for (i = 0; i < keyring->nkeys; i++) + keydata_internal_free(&keyring->keys[i]); + + free(keyring->keys); + keyring->keys=NULL; + keyring->nkeys=0; + keyring->nkeys_allocated=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. + + Example code: + \code + void example(ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + unsigned char keyid[OPS_KEY_ID_SIZE]; // value set elsewhere + keydata=ops_keyring_find_key_by_id(keyring,keyid); + ... + } + \endcode +*/ +const ops_keydata_t * +ops_keyring_find_key_by_id(const ops_keyring_t *keyring, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + int n; + + if (!keyring) + return NULL; + + for(n=0 ; n < keyring->nkeys ; ++n) + { + if(!memcmp(keyring->keys[n].key_id,keyid,OPS_KEY_ID_SIZE)) + return &keyring->keys[n]; + } + + return NULL; + } + +/** + \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. + + Example code: + \code + void example(ops_keyring_t* keyring) + { + ops_keydata_t* keydata=NULL; + keydata=ops_keyring_find_key_by_userid(keyring,"user@domain.com"); + ... + } + \endcode +*/ +const ops_keydata_t * +ops_keyring_find_key_by_userid(const ops_keyring_t *keyring, + const char *userid) + { + int n=0; + unsigned int i=0; + + if (!keyring) + return NULL; + + for(n=0 ; n < keyring->nkeys ; ++n) + { + for(i=0; ikeys[n].nuids; i++) + { + //printf("[%d][%d] userid %s\n",n,i,keyring->keys[n].uids[i].user_id); + if(!strncmp((char *)keyring->keys[n].uids[i].user_id,userid,strlen(userid))) + return &keyring->keys[n]; + } + } + + //printf("end: n=%d,i=%d\n",n,i); + return NULL; + } + +/** + \ingroup HighLevel_KeyringList + + \brief Prints all keys in keyring to stdout. + + \param keyring Keyring to use + + \return none + + Example code: + \code + void example() + { + ops_keyring_t* keyring=ops_mallocz(sizeof *keyring); + ops_boolean_t armoured=ops_false; + ops_keyring_read_from_file(keyring, armoured, "~/.gnupg/pubring.gpg"); + + ops_keyring_list(keyring); + + ops_keyring_free(keyring); + free (keyring); + } + \endcode +*/ + +void +ops_keyring_list(const ops_keyring_t* keyring) + { + int n; + unsigned int i; + ops_keydata_t* key; + + printf ("%d keys\n", keyring->nkeys); + for(n=0,key=&keyring->keys[n] ; n < keyring->nkeys ; ++n,++key) + { + for(i=0; inuids; i++) + { + if (ops_is_key_secret(key)) + ops_print_secret_keydata(key); + else + ops_print_public_keydata(key); + } + + } + } + +/* Static functions */ + +static ops_parse_cb_return_t +cb_keyring_read(const ops_parser_content_t *content_, + ops_parse_cb_info_t *cbinfo) +{ + OPS_USED(cbinfo); + + switch(content_->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: // we get these because we didn't prompt + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_TRUST: + case OPS_PARSER_ERRCODE: + break; + + default: + ; + } + + return OPS_RELEASE_MEMORY; +} + +/** + \ingroup HighLevel_KeyringList + + \brief Saves keyring to specified file + + \param keyring Keyring to save + \param armoured Save in ascii armoured format + \param output filename + + \return ops_true is anything when ok +*/ + +ops_boolean_t ops_write_keyring_to_file(const ops_keyring_t *keyring,ops_boolean_t armoured,const char *filename,ops_boolean_t write_all_packets) +{ + ops_create_info_t *info; + int fd = ops_setup_file_write(&info, filename, ops_true); + + if (fd < 0) + { + fprintf(stderr,"ops_write_keyring(): ERROR: Cannot write to %s\n",filename ) ; + return ops_false ; + } + + int i; + for(i=0;inkeys;++i) +// if(keyring->keys[i].key.pkey.algorithm == OPS_PKA_RSA) + if(write_all_packets) + ops_write_transferable_public_key_from_packet_data(&keyring->keys[i],armoured,info) ; + else + ops_write_transferable_public_key(&keyring->keys[i],armoured,info) ; +// else +// { +// fprintf(stdout, "ops_write_keyring: not writing key. Algorithm not handled: ") ; +// ops_print_public_keydata(&keyring->keys[i]); +// fprintf(stdout, "\n") ; +// } + + ops_writer_close(info); + ops_create_info_delete(info); + + return ops_true ; +} + +/*\@}*/ + +// eof diff --git a/openpgpsdk/src/openpgpsdk/keyring.h b/openpgpsdk/src/openpgpsdk/keyring.h new file mode 100644 index 000000000..09f10a0ad --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/keyring.h @@ -0,0 +1,91 @@ +/* + * 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 OPS_KEYRING_H +#define OPS_KEYRING_H + +#include "packet.h" +#include "memory.h" + +typedef struct ops_keydata ops_keydata_t; + +/** \struct ops_keyring_t + * A keyring + */ + +typedef struct + { + int nkeys; // while we are constructing a key, this is the offset + int nkeys_allocated; + ops_keydata_t *keys; + } ops_keyring_t; + +const ops_keydata_t * +ops_keyring_find_key_by_id(const ops_keyring_t *keyring, + const unsigned char keyid[OPS_KEY_ID_SIZE]); +const ops_keydata_t * +ops_keyring_find_key_by_userid(const ops_keyring_t *keyring, + const char* userid); +void ops_keydata_free(ops_keydata_t *key); +void ops_keydata_copy(ops_keydata_t *dst,const ops_keydata_t *src); +void ops_keyring_free(ops_keyring_t *keyring); +void ops_dump_keyring(const ops_keyring_t *keyring); +const ops_public_key_t * +ops_get_public_key_from_data(const ops_keydata_t *data); +ops_boolean_t ops_is_key_secret(const ops_keydata_t *data); +const ops_secret_key_t * +ops_get_secret_key_from_data(const ops_keydata_t *data); +ops_secret_key_t * +ops_get_writable_secret_key_from_data(ops_keydata_t *data); +ops_secret_key_t *ops_decrypt_secret_key_from_data(const ops_keydata_t *key, + const char *pphrase); + +ops_boolean_t ops_keyring_read_from_file(ops_keyring_t *keyring, const ops_boolean_t armour, const char *filename); +ops_boolean_t ops_keyring_read_from_mem(ops_keyring_t *keyring, const ops_boolean_t armour, ops_memory_t *mem); +ops_boolean_t ops_write_keyring_to_file(const ops_keyring_t *keyring,ops_boolean_t armoured,const char *filename,ops_boolean_t write_all_packets); + +char *ops_malloc_passphrase(char *passphrase); +char *ops_get_passphrase(void); + +void ops_keyring_list(const ops_keyring_t* keyring); + +void ops_set_secret_key(ops_parser_content_union_t* content,const ops_keydata_t *key); + +const unsigned char* ops_get_key_id(const ops_keydata_t *key); +unsigned ops_get_user_id_count(const ops_keydata_t *key); +const unsigned char* ops_get_user_id(const ops_keydata_t *key, unsigned index); +ops_boolean_t ops_is_key_supported(const ops_keydata_t *key); +const ops_keydata_t* ops_keyring_get_key_by_index(const ops_keyring_t *keyring, int index); + +ops_user_id_t* ops_add_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid); +ops_packet_t* ops_add_packet_to_keydata(ops_keydata_t* keydata, const ops_packet_t* packet); +void ops_add_signed_userid_to_keydata(ops_keydata_t* keydata, const ops_user_id_t* userid, const ops_packet_t* packet); + +ops_boolean_t ops_add_selfsigned_userid_to_keydata(ops_keydata_t* keydata, ops_user_id_t* userid); +ops_boolean_t ops_sign_key(ops_keydata_t* keydata_to_sign, const unsigned char *signers_key_id,ops_secret_key_t *signers_decrypted_private_key); + +ops_keydata_t *ops_keydata_new(void); +void ops_keydata_init(ops_keydata_t* keydata, const ops_content_tag_t type); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/keyring_local.h b/openpgpsdk/src/openpgpsdk/keyring_local.h new file mode 100644 index 000000000..5c3860f35 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/keyring_local.h @@ -0,0 +1,63 @@ +/* + * 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 + +#define DECLARE_ARRAY(type,arr) unsigned n##arr; unsigned n##arr##_allocated; type *arr +#define EXPAND_ARRAY(str,arr) do if(str->n##arr == str->n##arr##_allocated) \ + { \ + str->n##arr##_allocated=str->n##arr##_allocated*2+10; \ + str->arr=realloc(str->arr,str->n##arr##_allocated*sizeof *str->arr); \ + } while(0) + +/** ops_keydata_key_t + */ +typedef union + { + ops_public_key_t pkey; + ops_secret_key_t skey; + } ops_keydata_key_t; + + +/** sigpacket_t */ +typedef struct + { + ops_user_id_t* userid; + ops_packet_t* packet; + } sigpacket_t; + +// XXX: gonna have to expand this to hold onto subkeys, too... +/** \struct ops_keydata + * \todo expand to hold onto subkeys + */ +struct ops_keydata + { + DECLARE_ARRAY(ops_user_id_t,uids); + DECLARE_ARRAY(ops_packet_t,packets); + DECLARE_ARRAY(sigpacket_t, sigs); + unsigned char key_id[8]; + ops_fingerprint_t fingerprint; + ops_content_tag_t type; + ops_keydata_key_t key; + }; diff --git a/openpgpsdk/src/openpgpsdk/lists.c b/openpgpsdk/src/openpgpsdk/lists.c new file mode 100644 index 000000000..5be1e2e2c --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/lists.c @@ -0,0 +1,107 @@ +/* + * 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 + * + * Set of functions to manage a dynamic list + */ + +#include + +#include + +#include + +/** + * \ingroup Core_Lists + * \brief Initialises ulong list + * \param *list Pointer to existing list structure + */ +void ops_ulong_list_init(ops_ulong_list_t *list) + { + list->size=0; + list->used=0; + list->ulongs=NULL; + } + +/** + * \ingroup Core_Lists + * \brief Frees allocated memory in ulong list. Does not free *list itself. + * \param *list + */ +void ops_ulong_list_free(ops_ulong_list_t *list) + { + if (list->ulongs) + free(list->ulongs); + ops_ulong_list_init(list); + } + +/** + * \ingroup Core_Lists + * \brief Resizes ulong list. + * + * We only resize in one direction - upwards. + * Algorithm used : double the current size then add 1 + * + * \param *list Pointer to list + * \return 1 if success, else 0 + */ + +static unsigned int ops_ulong_list_resize(ops_ulong_list_t *list) + { + + int newsize=0; + + newsize=list->size*2 + 1; + list->ulongs=realloc(list->ulongs,newsize*sizeof *list->ulongs); + if (list->ulongs) + { + list->size=newsize; + return 1; + } + else + { + /* xxx - realloc failed. error message? - rachel */ + return 0; + } + } + +/** + * \ingroup Core_Lists + * Adds entry to ulong list + * + * \param *list + * \param *ulong + * + * \return 1 if success, else 0 + */ +unsigned int ops_ulong_list_add(ops_ulong_list_t *list, unsigned long *ulong) + { + if (list->size==list->used) + if (!ops_ulong_list_resize(list)) + return 0; + + list->ulongs[list->used]=*ulong; + list->used++; + return 1; + } + diff --git a/openpgpsdk/src/openpgpsdk/lists.h b/openpgpsdk/src/openpgpsdk/lists.h new file mode 100644 index 000000000..520b1d432 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/lists.h @@ -0,0 +1,40 @@ +/* + * 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 OPS_LISTS_H +#define OPS_LISTS_H + +/** ops_ulong_list_t */ +typedef struct + { + unsigned int size;/* num of array slots allocated */ + unsigned int used; /* num of array slots currently used */ + unsigned long *ulongs; + } ops_ulong_list_t; + +void ops_ulong_list_init(ops_ulong_list_t *list); +void ops_ulong_list_free(ops_ulong_list_t *list); +unsigned int ops_ulong_list_add(ops_ulong_list_t *list, unsigned long *ulong); + +#endif /* OPS_LISTS_H */ diff --git a/openpgpsdk/src/openpgpsdk/literal.h b/openpgpsdk/src/openpgpsdk/literal.h new file mode 100644 index 000000000..8b1c30e6b --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/literal.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005-2009 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 __OPS_LITERAL_H__ +#define __OPS_LITERAL_H__ + + +ops_boolean_t write_literal_header(ops_create_info_t *info, + void *header_data); + +void ops_writer_push_literal(ops_create_info_t *info); +void ops_writer_push_literal_with_opts(ops_create_info_t *info, + unsigned int buf_size); + +#endif /* __OPS_LITERAL_H__ */ + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/memory.c b/openpgpsdk/src/openpgpsdk/memory.c new file mode 100644 index 000000000..4093e5657 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/memory.c @@ -0,0 +1,204 @@ +/* + * 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 +#include +#include +#include + +#include + +struct ops_memory + { + unsigned char *buf; + size_t length; + size_t allocated; + }; + +/** +\ingroup HighLevel_Memory +\brief Memory to initialise +\param mem memory to initialise +\param initial_size Size to initialise to +*/ +void ops_memory_init(ops_memory_t *mem,size_t initial_size) + { + mem->length=0; + if(mem->buf) + { + if(mem->allocated < initial_size) + { + mem->buf=realloc(mem->buf,initial_size); + mem->allocated=initial_size; + } + return; + } + mem->buf=malloc(initial_size); + mem->allocated=initial_size; + } + +/** +\ingroup HighLevel_Memory +\brief Pad memory to required length +\param mem Memory to use +\param length New size +*/ +void ops_memory_pad(ops_memory_t *mem,size_t length) + { + assert(mem->allocated >= mem->length); + if(mem->allocated < mem->length+length) + { + mem->allocated=mem->allocated*2+length; + mem->buf=realloc(mem->buf,mem->allocated); + } + assert(mem->allocated >= mem->length+length); + } + +/** +\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 ops_memory_add(ops_memory_t *mem,const unsigned char *src,size_t length) + { + ops_memory_pad(mem,length); + 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 ops_memory_place_int(ops_memory_t *mem,unsigned offset,unsigned n, + size_t length) + { + assert(mem->allocated >= offset+length); + + while(length--) + 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 ops_memory_release() + * \sa ops_memory_free() + */ +void ops_memory_clear(ops_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 ops_memory_clear() +\sa ops_memory_free() +*/ +void ops_memory_release(ops_memory_t *mem) + { + free(mem->buf); + mem->buf=NULL; + mem->length=0; + } + +void ops_memory_make_packet(ops_memory_t *out,ops_content_tag_t tag) + { + size_t extra; + + if(out->length < 192) + extra=1; + else if(out->length < 8384) + extra=2; + else + extra=5; + + ops_memory_pad(out,extra+1); + memmove(out->buf+extra+1,out->buf,out->length); + + out->buf[0]=OPS_PTAG_ALWAYS_SET|OPS_PTAG_NEW_FORMAT|tag; + + if(out->length < 192) + out->buf[1]=out->length; + else if(out->length < 8384) + { + out->buf[1]=((out->length-192) >> 8)+192; + out->buf[2]=out->length-192; + } + else + { + out->buf[1]=0xff; + out->buf[2]=out->length >> 24; + out->buf[3]=out->length >> 16; + out->buf[4]=out->length >> 8; + out->buf[5]=out->length; + } + + out->length+=extra+1; + } + +/** + \ingroup HighLevel_Memory + \brief Create a new zeroed ops_memory_t + \return Pointer to new ops_memory_t + \note Free using ops_memory_free() after use. + \sa ops_memory_free() +*/ + +ops_memory_t *ops_memory_new() + { return ops_mallocz(sizeof(ops_memory_t)); } + +/** + \ingroup HighLevel_Memory + \brief Free memory ptr and associated memory + \param mem Memory to be freed + \sa ops_memory_release() + \sa ops_memory_clear() +*/ + +void ops_memory_free(ops_memory_t *mem) + { + ops_memory_release(mem); + free(mem); + } + +/** + \ingroup HighLevel_Memory + \brief Get length of data stored in ops_memory_t struct + \return Number of bytes in data +*/ +size_t ops_memory_get_length(const ops_memory_t *mem) + { return mem->length; } + +/** + \ingroup HighLevel_Memory + \brief Get data stored in ops_memory_t struct + \return Pointer to data +*/ +void *ops_memory_get_data(ops_memory_t *mem) + { return mem->buf; } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/memory.h b/openpgpsdk/src/openpgpsdk/memory.h new file mode 100644 index 000000000..3fa098026 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/memory.h @@ -0,0 +1,52 @@ +/* + * 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 +#include +#include "packet.h" + +#ifndef OPS_MEMORY_H +#define OPS_MEMORY_H + +/** ops_memory_t + */ +typedef struct ops_memory ops_memory_t; + +ops_memory_t *ops_memory_new(void); +void ops_memory_free(ops_memory_t *mem); +void ops_memory_init(ops_memory_t *mem,size_t initial_size); +void ops_memory_pad(ops_memory_t *mem,size_t length); +void ops_memory_add(ops_memory_t *mem,const unsigned char *src,size_t length); +void ops_memory_place_int(ops_memory_t *mem,unsigned offset,unsigned n, + size_t length); +void ops_memory_make_packet(ops_memory_t *out,ops_content_tag_t tag); +void ops_memory_clear(ops_memory_t *mem); +void ops_memory_release(ops_memory_t *mem); + +void ops_writer_set_memory(ops_create_info_t *info,ops_memory_t *mem); + +size_t ops_memory_get_length(const ops_memory_t *mem); +void *ops_memory_get_data(ops_memory_t *mem); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/openssl_crypto.c b/openpgpsdk/src/openpgpsdk/openssl_crypto.c new file mode 100644 index 000000000..1d70ccac9 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/openssl_crypto.c @@ -0,0 +1,812 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "keyring_local.h" +#include + +#include + +static int debug=0; + +void test_secret_key(const ops_secret_key_t *skey) + { + RSA* test=RSA_new(); + + test->n=BN_dup(skey->public_key.key.rsa.n); + test->e=BN_dup(skey->public_key.key.rsa.e); + + test->d=BN_dup(skey->key.rsa.d); + test->p=BN_dup(skey->key.rsa.p); + test->q=BN_dup(skey->key.rsa.q); + + assert(RSA_check_key(test)==1); + RSA_free(test); + } + +static void md5_init(ops_hash_t *hash) + { + assert(!hash->data); + hash->data=malloc(sizeof(MD5_CTX)); + MD5_Init(hash->data); + } + +static void md5_add(ops_hash_t *hash,const unsigned char *data,unsigned length) + { + MD5_Update(hash->data,data,length); + } + +static unsigned md5_finish(ops_hash_t *hash,unsigned char *out) + { + MD5_Final(out,hash->data); + free(hash->data); + hash->data=NULL; + return 16; + } + +static ops_hash_t md5={OPS_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 ops_hash_md5(ops_hash_t *hash) + { + *hash=md5; + } + +static void sha1_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha1_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA_CTX)); + SHA1_Init(hash->data); + } + +static void sha1_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha1_finish(ops_hash_t *hash,unsigned char *out) + { + SHA1_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA_DIGEST_LENGTH; + } + +static ops_hash_t sha1={OPS_HASH_SHA1,SHA_DIGEST_LENGTH,"SHA1",sha1_init, + sha1_add,sha1_finish,NULL}; + +/** + \ingroup Core_Crypto + \brief Initialise to SHA1 + \param hash Hash to initialise +*/ +void ops_hash_sha1(ops_hash_t *hash) + { + *hash=sha1; + } + +static void sha256_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha256_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA256_CTX)); + SHA256_Init(hash->data); + } + +static void sha256_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha256_finish(ops_hash_t *hash,unsigned char *out) + { + SHA256_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA256_DIGEST_LENGTH; + } + +static ops_hash_t sha256={OPS_HASH_SHA256,SHA256_DIGEST_LENGTH,"SHA256",sha256_init, + sha256_add,sha256_finish,NULL}; + +void ops_hash_sha256(ops_hash_t *hash) + { + *hash=sha256; + } + +/* + * SHA384 + */ + +static void sha384_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha384_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA512_CTX)); + SHA384_Init(hash->data); + } + +static void sha384_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha384_finish(ops_hash_t *hash,unsigned char *out) + { + SHA384_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA384_DIGEST_LENGTH; + } + +static ops_hash_t sha384={OPS_HASH_SHA384,SHA384_DIGEST_LENGTH,"SHA384",sha384_init, + sha384_add,sha384_finish,NULL}; + +void ops_hash_sha384(ops_hash_t *hash) + { + *hash=sha384; + } + +/* + * SHA512 + */ + +static void sha512_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha512_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA512_CTX)); + SHA512_Init(hash->data); + } + +static void sha512_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha512_finish(ops_hash_t *hash,unsigned char *out) + { + SHA512_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA512_DIGEST_LENGTH; + } + +static ops_hash_t sha512={OPS_HASH_SHA512,SHA512_DIGEST_LENGTH,"SHA512",sha512_init, + sha512_add,sha512_finish,NULL}; + +void ops_hash_sha512(ops_hash_t *hash) + { + *hash=sha512; + } + +/* + * SHA224 + */ + +static void sha224_init(ops_hash_t *hash) + { + if (debug) + { + fprintf(stderr,"***\n***\nsha1_init\n***\n"); + } + assert(!hash->data); + hash->data=malloc(sizeof(SHA256_CTX)); + SHA224_Init(hash->data); + } + +static void sha224_add(ops_hash_t *hash,const unsigned char *data, + unsigned length) + { + if (debug) + { + unsigned int i=0; + fprintf(stderr,"adding %d to hash:\n ", length); + for (i=0; idata,data,length); + } + +static unsigned sha224_finish(ops_hash_t *hash,unsigned char *out) + { + SHA224_Final(out,hash->data); + if (debug) + { + unsigned i=0; + fprintf(stderr,"***\n***\nsha1_finish\n***\n"); + for (i=0; idata); + hash->data=NULL; + return SHA224_DIGEST_LENGTH; + } + +static ops_hash_t sha224={OPS_HASH_SHA224,SHA224_DIGEST_LENGTH,"SHA224",sha224_init, + sha224_add,sha224_finish,NULL}; + +void ops_hash_sha224(ops_hash_t *hash) + { + *hash=sha224; + } + +ops_boolean_t ops_dsa_verify(const unsigned char *hash,size_t hash_length, + const ops_dsa_signature_t *sig, + const ops_dsa_public_key_t *dsa) + { + DSA_SIG *osig; + DSA *odsa; + int ret; + + osig=DSA_SIG_new(); + osig->r=sig->r; + osig->s=sig->s; + + if(BN_num_bits(dsa->q) != 160) + { + fprintf(stderr,"(WW) ops_dsa_verify: openssl does only supports 'q' of 160 bits. Current is %d bits.\n",BN_num_bits(dsa->q)) ; + return ops_false ; + } + + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + + if (debug) + { + fprintf(stderr,"hash passed in:\n"); + unsigned i; + for (i=0; iq)); + unsigned int qlen=BN_num_bytes(odsa->q); + if (qlen < hash_length) + hash_length=qlen; + // ret=DSA_do_verify(hash,hash_length,osig,odsa); + ret=DSA_do_verify(hash,hash_length,osig,odsa); + if (debug) + { + fprintf(stderr,"ret=%d\n",ret); + } + + if(ret < 0) + { + ERR_load_crypto_strings() ; + unsigned long err = 0 ; + while(err = ERR_get_error()) + fprintf(stderr,"DSA_do_verify(): ERR = %ld. lib error:\"%s\", func_error:\"%s\", reason:\"%s\"\n",err,ERR_lib_error_string(err),ERR_func_error_string(err),ERR_reason_error_string(err)) ; + //assert(ret >= 0); + return ops_false ; + } + + odsa->p=odsa->q=odsa->g=odsa->pub_key=NULL; + DSA_free(odsa); + + osig->r=osig->s=NULL; + DSA_SIG_free(osig); + + return ret != 0; + } + +/** + \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 rsa RSA public key + \return size of recovered message digest +*/ +int ops_rsa_public_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + orsa=RSA_new(); + orsa->n=rsa->n; + orsa->e=rsa->e; + + n=RSA_public_decrypt(length,in,out,orsa,RSA_NO_PADDING); + + orsa->n=orsa->e=NULL; + 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 srsa RSA secret key + \param rsa RSA public key + \return number of bytes decrypted +*/ +int ops_rsa_private_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + orsa=RSA_new(); + orsa->n=rsa->n; // XXX: do we need n? + orsa->d=srsa->d; + orsa->p=srsa->q; + orsa->q=srsa->p; + + /* debug */ + orsa->e=rsa->e; + // If this isn't set, it's very likely that the programmer hasn't + // decrypted the secret key. RSA_check_key segfaults in that case. + // Use ops_decrypt_secret_key_from_data() to do that. + assert(orsa->d); + assert(RSA_check_key(orsa) == 1); + orsa->e=NULL; + /* end debug */ + + n=RSA_private_encrypt(length,in,out,orsa,RSA_NO_PADDING); + + orsa->n=orsa->d=orsa->p=orsa->q=NULL; + 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 srsa RSA secret key +\param rsa RSA public key +\return size of recovered plaintext +*/ +int ops_rsa_private_decrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_secret_key_t *srsa, + const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + char errbuf[1024]; + + orsa=RSA_new(); + orsa->n=rsa->n; // XXX: do we need n? + orsa->d=srsa->d; + orsa->p=srsa->q; + orsa->q=srsa->p; + + /* debug */ + orsa->e=rsa->e; + assert(RSA_check_key(orsa) == 1); + orsa->e=NULL; + /* end debug */ + + n=RSA_private_decrypt(length,in,out,orsa,RSA_NO_PADDING); + + // printf("ops_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]); + fprintf(stderr,"openssl error : %s\n",errbuf); + } + orsa->n=orsa->d=orsa->p=orsa->q=NULL; + RSA_free(orsa); + + 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 rsa RSA Public Key +*/ +int ops_rsa_public_encrypt(unsigned char *out,const unsigned char *in, + size_t length,const ops_rsa_public_key_t *rsa) + { + RSA *orsa; + int n; + + // printf("ops_rsa_public_encrypt: length=%ld\n", length); + + orsa=RSA_new(); + orsa->n=rsa->n; + orsa->e=rsa->e; + + // printf("len: %ld\n", length); + // ops_print_bn("n: ", orsa->n); + // ops_print_bn("e: ", orsa->e); + n=RSA_public_encrypt(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); + } + + orsa->n=orsa->e=NULL; + RSA_free(orsa); + + return n; + } + +/** + \ingroup Core_Crypto + \brief initialises openssl + \note Would usually call ops_init() instead + \sa ops_init() +*/ +void ops_crypto_init() + { +#ifdef DMALLOC + CRYPTO_malloc_debug_init(); + CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + } + +/** + \ingroup Core_Crypto + \brief Finalise openssl + \note Would usually call ops_finish() instead + \sa ops_finish() +*/ +void ops_crypto_finish() + { + CRYPTO_cleanup_all_ex_data(); + // FIXME: what should we do instead (function is deprecated)? + // ERR_remove_state(0); +#ifdef DMALLOC + CRYPTO_mem_leaks_fp(stderr); +#endif + } + +/** + \ingroup Core_Hashes + \brief Get Hash name + \param hash Hash struct + \return Hash name +*/ +const char *ops_text_from_hash(ops_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 ops_true if key generated successfully; otherwise ops_false + \note It is the caller's responsibility to call ops_keydata_free(keydata) +*/ +ops_boolean_t ops_rsa_generate_keypair(const int numbits, const unsigned long e, + ops_keydata_t* keydata) + { + ops_secret_key_t *skey=NULL; + RSA *rsa=RSA_new(); + BN_CTX *ctx=BN_CTX_new(); + BIGNUM *ebn=BN_new(); + + ops_keydata_init(keydata,OPS_PTAG_CT_SECRET_KEY); + skey=ops_get_writable_secret_key_from_data(keydata); + + // generate the key pair + + BN_set_word(ebn,e); + RSA_generate_key_ex(rsa,numbits,ebn,NULL); + + // populate ops key from ssl key + + skey->public_key.version=4; + skey->public_key.creation_time=time(NULL); + skey->public_key.days_valid=0; + skey->public_key.algorithm= OPS_PKA_RSA; + + skey->public_key.key.rsa.n=BN_dup(rsa->n); + skey->public_key.key.rsa.e=BN_dup(rsa->e); + + skey->s2k_usage=OPS_S2KU_ENCRYPTED_AND_HASHED; + skey->s2k_specifier=OPS_S2KS_SALTED; + //skey->s2k_specifier=OPS_S2KS_SIMPLE; + skey->algorithm=OPS_SA_CAST5; // \todo make param + skey->hash_algorithm=OPS_HASH_SHA1; // \todo make param + skey->octet_count=0; + skey->checksum=0; + + skey->key.rsa.d=BN_dup(rsa->d); + skey->key.rsa.p=BN_dup(rsa->p); + skey->key.rsa.q=BN_dup(rsa->q); + skey->key.rsa.u=BN_mod_inverse(NULL,rsa->p, rsa->q, ctx); + assert(skey->key.rsa.u); + BN_CTX_free(ctx); + + RSA_free(rsa); + + ops_keyid(keydata->key_id, &keydata->key.skey.public_key); + ops_fingerprint(&keydata->fingerprint, &keydata->key.skey.public_key); + + // Generate checksum + + ops_create_info_t *cinfo=NULL; + ops_memory_t *mem=NULL; + + ops_setup_memory_write(&cinfo, &mem, 128); + + ops_push_skey_checksum_writer(cinfo, skey); + + switch(skey->public_key.algorithm) + { + // case OPS_PKA_DSA: + // return ops_write_mpi(key->key.dsa.x,info); + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!ops_write_mpi(skey->key.rsa.d,cinfo) + || !ops_write_mpi(skey->key.rsa.p,cinfo) + || !ops_write_mpi(skey->key.rsa.q,cinfo) + || !ops_write_mpi(skey->key.rsa.u,cinfo)) + return ops_false; + break; + + // case OPS_PKA_ELGAMAL: + // return ops_write_mpi(key->key.elgamal.x,info); + + default: + assert(0); + break; + } + + // close rather than pop, since its the only one on the stack + ops_writer_close(cinfo); + ops_teardown_memory_write(cinfo, mem); + + // should now have checksum in skey struct + + // test + if (debug) + test_secret_key(skey); + + return ops_true; + } + +/** + \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 ops_keydata_free(keydata) + \sa ops_rsa_generate_keypair() + \sa ops_keydata_free() +*/ +ops_keydata_t* ops_rsa_create_selfsigned_keypair(const int numbits, const unsigned long e, ops_user_id_t * userid) + { + ops_keydata_t *keydata=NULL; + + keydata=ops_keydata_new(); + + if (ops_rsa_generate_keypair(numbits, e, keydata) != ops_true + || ops_add_selfsigned_userid_to_keydata(keydata, userid) != ops_true) + { + ops_keydata_free(keydata); + free(keydata) ; + return NULL; + } + + return keydata; + } + +/* +int ops_dsa_size(const ops_dsa_public_key_t *dsa) + { + int size; + DSA *odsa; + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + + DSAparams_print_fp(stderr, odsa); + size=DSA_size(odsa); + + odsa->p=odsa->q=odsa->g=odsa->pub_key=odsa->priv_key=NULL; + DSA_free(odsa); + + return size; + } +*/ + +DSA_SIG* ops_dsa_sign(unsigned char* hashbuf, unsigned hashsize, const ops_dsa_secret_key_t *sdsa, const ops_dsa_public_key_t *dsa) + { + DSA *odsa; + DSA_SIG *dsasig; + + odsa=DSA_new(); + odsa->p=dsa->p; + odsa->q=dsa->q; + odsa->g=dsa->g; + odsa->pub_key=dsa->y; + odsa->priv_key=sdsa->x; + + dsasig=DSA_do_sign(hashbuf,hashsize,odsa); + + odsa->p=odsa->q=odsa->g=odsa->pub_key=odsa->priv_key=NULL; + DSA_free(odsa); + + return dsasig; + } + +// eof diff --git a/openpgpsdk/src/openpgpsdk/packet-parse.c b/openpgpsdk/src/openpgpsdk/packet-parse.c new file mode 100644 index 000000000..84d4f193a --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-parse.c @@ -0,0 +1,3331 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse_local.h" + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +#include + +static int debug=0; + +/** + * limited_read_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 pinfo How to parse + * + * \return 1 on success, 0 on failure + */ +static int limited_read_data(ops_data_t *data,unsigned int len, + ops_region_t *subregion,ops_parse_info_t *pinfo) + { + data->len = len; + + assert(subregion->length-subregion->length_read >= len); + + data->contents=malloc(data->len); + if (!data->contents) + return 0; + + if (!ops_limited_read(data->contents, data->len,subregion,&pinfo->errors, + &pinfo->rinfo,&pinfo->cbinfo)) + return 0; + + return 1; + } + +/** + * read_data reads the remainder of the subregion's data + * into a data_t structure + * + * \param data + * \param subregion + * \param pinfo + * + * \return 1 on success, 0 on failure + */ +static int read_data(ops_data_t *data,ops_region_t *subregion, + ops_parse_info_t *pinfo) + { + int len; + + len=subregion->length-subregion->length_read; + + if ( len >= 0 ) { + return(limited_read_data(data,len,subregion,pinfo)); + } + return 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_unsigned_string(unsigned char **str,ops_region_t *subregion, + ops_parse_info_t *pinfo) + { + int len=0; + + len=subregion->length-subregion->length_read; + + *str=malloc(len+1); + if(!(*str)) + return 0; + + if(len && !ops_limited_read(*str,len,subregion,&pinfo->errors, + &pinfo->rinfo,&pinfo->cbinfo)) + return 0; + + /*! ensure the string is NULL-terminated */ + + (*str)[len]='\0'; + + return 1; + } + +static int read_string(char **str, ops_region_t *subregion, ops_parse_info_t *pinfo) + { + return (read_unsigned_string((unsigned char **)str, subregion, pinfo)); + } + +void ops_init_subregion(ops_region_t *subregion,ops_region_t *region) + { + memset(subregion,'\0',sizeof *subregion); + subregion->parent=region; + } + +/*! macro to save typing */ +#define C content.content + +/* XXX: replace ops_ptag_t with something more appropriate for limiting + reads */ + +/** + * 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 *pinfo, 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 *pinfo + * + * \return OPS_R_OK + * \return OPS_R_PARTIAL_READ + * \return OPS_R_EOF + * \return OPS_R_EARLY_EOF + * + * \sa #ops_reader_ret_t for details of return codes + */ + +static int sub_base_read(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_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=rinfo->reader((char*)dest+n,length-n,errors,rinfo,cbinfo); + + assert(r <= (int)(length-n)); + + // XXX: should we save the error and return what was read so far? + if(r < 0) + return r; + + if(r == 0) + break; + + n+=r; + } + + if(n == 0) + return 0; + + if(rinfo->accumulate) + { + assert(rinfo->asize >= rinfo->alength); + if(rinfo->alength+n > rinfo->asize) + { + rinfo->asize=rinfo->asize*2+n; + rinfo->accumulated=realloc(rinfo->accumulated,rinfo->asize); + } + assert(rinfo->asize >= rinfo->alength+n); + memcpy(rinfo->accumulated+rinfo->alength,dest,n); + } + // we track length anyway, because it is used for packet offsets + rinfo->alength+=n; + // and also the position + rinfo->position+=n; + + return n; + } + +int ops_stacked_read(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { return sub_base_read(dest,length,errors,rinfo->next,cbinfo); } + +/* This will do a full read so long as length < MAX_INT */ +static int base_read(unsigned char *dest,size_t length, + ops_parse_info_t *pinfo) + { + return sub_base_read(dest,length,&pinfo->errors,&pinfo->rinfo, + &pinfo->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(unsigned char *dest,size_t length,int *last_read, + ops_error_t **errors,ops_reader_info_t *rinfo, + ops_parse_cb_info_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(dest+t,length-t,errors,rinfo,cbinfo); + + if(r <= 0) + { + *last_read=r; + return t; + } + + 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 ops_true on success, ops_false on failure + */ +static ops_boolean_t _read_scalar(unsigned *result,unsigned length, + ops_parse_info_t *pinfo) + { + unsigned t=0; + + assert (length <= sizeof(*result)); + + while(length--) + { + unsigned char c[1]; + int r; + + r=base_read(c,1,pinfo); + if(r != 1) + return ops_false; + t=(t << 8)+c[0]; + } + + *result=t; + return ops_true; + } + +/** + * \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 ops_ptag_t::length_read. + * + * 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 rinfo Reader info + * \param cbinfo Callback info + * \return ops_true on success, ops_false on error + */ +ops_boolean_t ops_limited_read(unsigned char *dest,size_t length, + ops_region_t *region,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + size_t r; + int lr; + + if(!region->indeterminate && region->length_read+length > region->length) + { + OPS_ERROR(errors,OPS_E_P_NOT_ENOUGH_DATA,"Not enough data"); + return ops_false; + } + + r=full_read(dest,length,&lr,errors,rinfo,cbinfo); + + if(lr < 0) + { + OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed"); + return ops_false; + } + + if(!region->indeterminate && r != length) + { + OPS_ERROR(errors,OPS_E_R_READ_FAILED,"Read failed"); + return ops_false; + } + + region->last_read=r; + do + { + region->length_read+=r; + assert(!region->parent || region->length <= region->parent->length); + } + while((region=region->parent)); + + return ops_true; + } + +/** + \ingroup Core_ReadPackets + \brief Call ops_limited_read on next in stack +*/ +ops_boolean_t ops_stacked_limited_read(void *dest, unsigned length, + ops_region_t *region, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + return ops_limited_read(dest, length, region, errors, rinfo->next, cbinfo); + } + +static ops_boolean_t limited_read(unsigned char *dest,unsigned length, + ops_region_t *region,ops_parse_info_t *info) + { + return ops_limited_read(dest,length,region,&info->errors, + &info->rinfo,&info->cbinfo); + } + +static ops_boolean_t exact_limited_read(unsigned char *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_boolean_t ret; + + pinfo->exact_read=ops_true; + ret=limited_read(dest,length,region,pinfo); + pinfo->exact_read=ops_false; + + return ret; + } + +/** Skip over length bytes of this packet. + * + * Calls limited_read() 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 *pinfo How to parse + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + */ +static int limited_skip(unsigned length,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char buf[8192]; + + while(length) + { + int n=length%8192; + if(!limited_read(buf,n,region,pinfo)) + return 0; + length-=n; + } + return 1; + } + +/** Read a scalar. + * + * Read a big-endian scalar of length bytes, respecting packet + * boundaries (by calling limited_read() 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 *pinfo How to parse + * \param *cb The callback + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + * + * \see RFC4880 3.1 + */ +static int limited_read_scalar(unsigned *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[4]=""; + unsigned t; + unsigned n; + + assert(length <= 4); + assert(sizeof(*dest) >= 4); + if(!limited_read(c,length,region,pinfo)) + return 0; + + for(t=0,n=0 ; n < length ; ++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 limited_read() 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 *pinfo How to parse + * \param *cb The callback + * \return 1 on success, 0 on error (calls the cb with OPS_PARSER_ERROR in limited_read()). + * + * \see RFC4880 3.1 + */ +static int limited_read_size_t_scalar(size_t *dest,unsigned length, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned tmp; + + assert(sizeof(*dest) >= 4); + + /* Note that because the scalar is at most 4 bytes, we don't care + if size_t is bigger than usigned */ + if(!limited_read_scalar(&tmp,length,region,pinfo)) + 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 limited_read_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 limited_read_scalar() + * + * \see RFC4880 3.5 + */ +static int limited_read_time(time_t *dest,ops_region_t *region, + ops_parse_info_t *pinfo) + { + /* + * Cannot assume that time_t is 4 octets long - + * there is at least one architecture (SunOS 5.10) where it is 8. + */ + if (sizeof(*dest)==4) + { + return limited_read_scalar((unsigned *)dest,4,region,pinfo); + } + else + { + time_t mytime=0; + int i=0; + unsigned char c[1]; + for (i=0; i<4; i++) + { + if (!limited_read(c,1,region,pinfo)) + return 0; + mytime=(mytime << 8) + c[0]; + } + *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 limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX + * see comment below - the callback is called with a OPS_PARSER_ERROR in case of an error) + * + * \see RFC4880 3.2 + */ +static int limited_read_mpi(BIGNUM **pbn,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned length; + unsigned nonzero; + unsigned char buf[8192]=""; /* an MPI has a 2 byte length part. Length + is given in bits, so the largest we should + ever need for the buffer is 8192 bytes. */ + ops_boolean_t ret; + + pinfo->reading_mpi_length=ops_true; + ret=limited_read_scalar(&length,2,region,pinfo); + + pinfo->reading_mpi_length=ops_false; + 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; + + assert(length <= 8192); + if(!limited_read(buf,length,region,pinfo)) + return 0; + + if((buf[0] >> nonzero) != 0 || !(buf[0]&(1 << (nonzero-1)))) + { + OPS_ERROR(&pinfo->errors,OPS_E_P_MPI_FORMAT_ERROR,"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,length,NULL); + 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 *pinfo How to parse + * \return ops_true if OK, else ops_false + * + */ + +static ops_boolean_t read_new_length(unsigned *length,ops_parse_info_t *pinfo) + { + unsigned char c[1]; + + if(base_read(c,1,pinfo) != 1) + return ops_false; + if(c[0] < 192) + { + // 1. One-octet packet + *length=c[0]; + return ops_true; + } + + else if (c[0]>=192 && c[0]<=223) + { + // 2. Two-octet packet + unsigned t=(c[0]-192) << 8; + + if(base_read(c,1,pinfo) != 1) + return ops_false; + *length=t+c[0]+192; + return ops_true; + } + + else if (c[0]==255) + { + // 3. Five-Octet packet + return _read_scalar(length,4,pinfo); + } + + else if (c[0]>=224 && c[0]<255) + { + // 4. Partial Body Length + OPS_ERROR(&pinfo->errors,OPS_E_UNIMPLEMENTED, + "New format Partial Body Length fields not yet implemented"); + return ops_false; + } + return ops_false; + } + +/** 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 limited_read_scalar() or limited_read() or if the MPI is not properly formed (XXX + * see comment below) + * + * \see RFC4880 4.2.2 + * \see ops_ptag_t + */ +static int limited_read_new_length(unsigned *length,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + + if(!limited_read(c,1,region,pinfo)) + return 0; + if(c[0] < 192) + { + *length=c[0]; + return 1; + } + if(c[0] < 255) + { + unsigned t=(c[0]-192) << 8; + + if(!limited_read(c,1,region,pinfo)) + return 0; + *length=t+c[0]+192; + return 1; + } + return limited_read_scalar(length,4,region,pinfo); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +static void data_free(ops_data_t *data) + { + free(data->contents); + data->contents=NULL; + data->len=0; + } + +/** +\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 ops_packet_free(ops_packet_t *packet) + { + free(packet->raw); + packet->raw=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_headers_free(ops_headers_t *headers) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + { + free(headers->headers[n].key); + free(headers->headers[n].value); + } + free(headers->headers); + headers->headers=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_signed_cleartext_trailer_free(ops_signed_cleartext_trailer_t *trailer) + { + free(trailer->hash); + trailer->hash=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_cmd_get_passphrase_free(ops_secret_key_passphrase_t *skp) + { + if (skp->passphrase && *skp->passphrase) + { + free(*skp->passphrase); + *skp->passphrase=NULL; + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free any memory allocated when parsing the packet content */ +void ops_parser_content_free(ops_parser_content_t *c) + { + switch(c->tag) + { + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_COMPRESSED: + case OPS_PTAG_SS_CREATION_TIME: + case OPS_PTAG_SS_EXPIRATION_TIME: + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + case OPS_PTAG_SS_TRUST: + case OPS_PTAG_SS_ISSUER_KEY_ID: + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + case OPS_PTAG_SS_PRIMARY_USER_ID: + case OPS_PTAG_SS_REVOCABLE: + case OPS_PTAG_SS_REVOCATION_KEY: + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + case OPS_PTAG_CT_LITERAL_DATA_BODY: + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + case OPS_PTAG_CT_UNARMOURED_TEXT: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_SE_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + case OPS_PTAG_CT_SE_IP_DATA_BODY: + case OPS_PTAG_CT_MDC: + case OPS_PARSER_CMD_GET_SECRET_KEY: + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + ops_headers_free(&c->content.signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + ops_headers_free(&c->content.armour_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + ops_signed_cleartext_trailer_free(&c->content.signed_cleartext_trailer); + break; + + case OPS_PTAG_CT_TRUST: + ops_trust_free(&c->content.trust); + break; + + case OPS_PTAG_CT_SIGNATURE: + case OPS_PTAG_CT_SIGNATURE_FOOTER: + ops_signature_free(&c->content.signature); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + ops_public_key_free(&c->content.public_key); + break; + + case OPS_PTAG_CT_USER_ID: + ops_user_id_free(&c->content.user_id); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + ops_user_id_free(&c->content.ss_signers_user_id); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + ops_user_attribute_free(&c->content.user_attribute); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + ops_ss_preferred_ska_free(&c->content.ss_preferred_ska); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + ops_ss_preferred_hash_free(&c->content.ss_preferred_hash); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + ops_ss_preferred_compression_free(&c->content.ss_preferred_compression); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + ops_ss_key_flags_free(&c->content.ss_key_flags); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + ops_ss_key_server_prefs_free(&c->content.ss_key_server_prefs); + break; + + case OPS_PTAG_SS_FEATURES: + ops_ss_features_free(&c->content.ss_features); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + ops_ss_notation_data_free(&c->content.ss_notation_data); + break; + + case OPS_PTAG_SS_REGEXP: + ops_ss_regexp_free(&c->content.ss_regexp); + break; + + case OPS_PTAG_SS_POLICY_URI: + ops_ss_policy_url_free(&c->content.ss_policy_url); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + ops_ss_preferred_key_server_free(&c->content.ss_preferred_key_server); + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + ops_ss_userdefined_free(&c->content.ss_userdefined); + break; + + case OPS_PTAG_SS_RESERVED: + ops_ss_reserved_free(&c->content.ss_unknown); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + ops_ss_revocation_reason_free(&c->content.ss_revocation_reason); + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + ops_ss_embedded_signature_free(&c->content.ss_embedded_signature); + break; + + case OPS_PARSER_PACKET_END: + ops_packet_free(&c->content.packet); + break; + + case OPS_PARSER_ERROR: + case OPS_PARSER_ERRCODE: + break; + + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + ops_secret_key_free(&c->content.secret_key); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_pk_session_key_free(&c->content.pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + case OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD: + ops_cmd_get_passphrase_free(&c->content.secret_key_passphrase); + break; + + default: + fprintf(stderr,"Can't free %d (0x%x)\n",c->tag,c->tag); + assert(0); + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +static void free_BN(BIGNUM **pp) + { + BN_free(*pp); + *pp=NULL; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +void ops_pk_session_key_free(ops_pk_session_key_t *sk) + { + switch(sk->algorithm) + { + case OPS_PKA_RSA: + free_BN(&sk->parameters.rsa.encrypted_m); + break; + + case OPS_PKA_ELGAMAL: + free_BN(&sk->parameters.elgamal.g_to_k); + free_BN(&sk->parameters.elgamal.encrypted_m); + break; + + default: + assert(0); + } + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing a public key */ +void ops_public_key_free(ops_public_key_t *p) + { + switch(p->algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + free_BN(&p->key.rsa.n); + free_BN(&p->key.rsa.e); + break; + + case OPS_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 OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + free_BN(&p->key.elgamal.p); + free_BN(&p->key.elgamal.g); + free_BN(&p->key.elgamal.y); + break; + + default: + assert(0); + } + } + +void ops_public_key_copy(ops_public_key_t *dst,const ops_public_key_t *src) +{ + *dst = *src ; + + switch(src->algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + dst->key.rsa.n = BN_dup(src->key.rsa.n); + dst->key.rsa.e = BN_dup(src->key.rsa.e); + break; + + case OPS_PKA_DSA: + dst->key.dsa.p = BN_dup(src->key.dsa.p); + dst->key.dsa.q = BN_dup(src->key.dsa.q); + dst->key.dsa.g = BN_dup(src->key.dsa.g); + dst->key.dsa.y = BN_dup(src->key.dsa.y); + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + dst->key.elgamal.p = BN_dup(src->key.elgamal.p); + dst->key.elgamal.g = BN_dup(src->key.elgamal.g); + dst->key.elgamal.y = BN_dup(src->key.elgamal.y); + break; + + //case 0: + // nothing to free + // break; + + default: + assert(0); + } +} +/** + \ingroup Core_ReadPackets +*/ +static int parse_public_key_data(ops_public_key_t *key,ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + + assert (region->length_read == 0); /* We should not have read anything so far */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + key->version=c[0]; + if(key->version < 2 || key->version > 4) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_PUBLIC_KEY_VRSN, + "Bad public key version (0x%02x)",key->version); + return 0; + } + + if(!limited_read_time(&key->creation_time,region,pinfo)) + return 0; + + key->days_valid=0; + if((key->version == 2 || key->version == 3) + && !limited_read_scalar(&key->days_valid,2,region,pinfo)) + return 0; + + if(!limited_read(c,1,region,pinfo)) + return 0; + + key->algorithm=c[0]; + + switch(key->algorithm) + { + case OPS_PKA_DSA: + if(!limited_read_mpi(&key->key.dsa.p,region,pinfo) + || !limited_read_mpi(&key->key.dsa.q,region,pinfo) + || !limited_read_mpi(&key->key.dsa.g,region,pinfo) + || !limited_read_mpi(&key->key.dsa.y,region,pinfo)) + return 0; + break; + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&key->key.rsa.n,region,pinfo) + || !limited_read_mpi(&key->key.rsa.e,region,pinfo)) + return 0; + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&key->key.elgamal.p,region,pinfo) + || !limited_read_mpi(&key->key.elgamal.g,region,pinfo) + || !limited_read_mpi(&key->key.elgamal.y,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm (%s)",ops_show_pka(key->algorithm)); + 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_public_key(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if(!parse_public_key_data(&C.public_key,region,pinfo)) + return 0; + + // XXX: this test should be done for all packets, surely? + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", region->length-region->length_read); + return 0; + } + + CBP(pinfo,tag,&content); + + return 1; + } + + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_regexp_free(ops_ss_regexp_t *regexp) + { + string_free(®exp->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_policy_url_free(ops_ss_policy_url_t *policy_url) + { + string_free(&policy_url->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this signature sub-packet type */ +void ops_ss_preferred_key_server_free(ops_ss_preferred_key_server_t *preferred_key_server) + { + string_free(&preferred_key_server->text); + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this packet type */ +void ops_user_attribute_free(ops_user_attribute_t *user_att) + { + data_free(&user_att->data); + } + +/** + * \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_user_attribute(ops_region_t *region, ops_parse_info_t *pinfo) + { + + ops_parser_content_t content; + + /* xxx- treat as raw data for now. Could break down further + into attribute sub-packets later - rachel */ + + assert(region->length_read == 0); /* We should not have read anything so far */ + + if(!read_data(&C.user_attribute.data,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_USER_ATTRIBUTE,&content); + + return 1; + } + +/** +\ingroup Core_Create +\brief Free allocated memory +*/ +/*! Free the memory used when parsing this packet type */ +void ops_user_id_free(ops_user_id_t *id) + { + free(id->user_id); + id->user_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_user_id(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + assert(region->length_read == 0); /* We should not have read anything so far */ + + C.user_id.user_id=malloc(region->length+1); /* XXX should we not like check malloc's return value? */ + + if(region->length && !limited_read(C.user_id.user_id,region->length,region, + pinfo)) + return 0; + + C.user_id.user_id[region->length]='\0'; /* terminate the string */ + + CBP(pinfo,OPS_PTAG_CT_USER_ID,&content); + + return 1; + } + +/** + * \ingroup Core_Create + * \brief Free the memory used when parsing a private/experimental PKA signature + * \param unknown_sig + */ +void free_unknown_sig_pka(ops_unknown_signature_t *unknown_sig) + { + data_free(&unknown_sig->data); + } + +/** + * \ingroup Core_Create + * \brief Free the memory used when parsing a signature + * \param sig + */ +void ops_signature_free(ops_signature_t *sig) + { + switch(sig->info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + free_BN(&sig->info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + free_BN(&sig->info.signature.dsa.r); + free_BN(&sig->info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + free_BN(&sig->info.signature.elgamal.r); + free_BN(&sig->info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + free_unknown_sig_pka(&sig->info.signature.unknown); + break; + + default: + assert(0); + } + free(sig->info.v4_hashed_data); + } + +/** + * \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_signature(ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + // clear signature + memset(&C.signature,'\0',sizeof C.signature); + + C.signature.info.version=OPS_V3; + + /* hash info length */ + if(!limited_read(c,1,region,pinfo)) + return 0; + if(c[0] != 5) + ERRP(pinfo,"bad hash info length"); + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.type=c[0]; + /* XXX: check signature type */ + + if(!limited_read_time(&C.signature.info.creation_time,region,pinfo)) + return 0; + C.signature.info.creation_time_set=ops_true; + + if(!limited_read(C.signature.info.signer_id,OPS_KEY_ID_SIZE,region,pinfo)) + return 0; + C.signature.info.signer_id_set=ops_true; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.key_algorithm=c[0]; + /* XXX: check algorithm */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.hash_algorithm=c[0]; + /* XXX: check algorithm */ + + if(!limited_read(C.signature.hash2,2,region,pinfo)) + return 0; + + switch(C.signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo)) + return 0; + break; + + case OPS_PKA_DSA: + if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo)) + return 0; + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG, + "Unsupported signature key algorithm (%s)", + ops_show_pka(C.signature.info.key_algorithm)); + return 0; + } + + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA,"Unconsumed data (%d)",region->length-region->length_read); + return 0; + } + + if(C.signature.info.signer_id_set) + C.signature.hash=ops_parse_hash_find(pinfo,C.signature.info.signer_id); + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE,&content); + + 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_signature_subpacket(ops_signature_t *sig, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_region_t subregion; + unsigned char c[1]=""; + ops_parser_content_t content; + unsigned t8,t7; + ops_boolean_t read=ops_true; + unsigned char bool[1]=""; + + ops_init_subregion(&subregion,region); + if(!limited_read_new_length(&subregion.length,region,pinfo)) + return 0; + + if(subregion.length > region->length) + ERRP(pinfo,"Subpacket too long"); + + if(!limited_read(c,1,&subregion,pinfo)) + return 0; + + t8=(c[0]&0x7f)/8; + t7=1 << (c[0]&7); + + content.critical=c[0] >> 7; + content.tag=OPS_PTAG_SIGNATURE_SUBPACKET_BASE+(c[0]&0x7f); + + /* Application wants it delivered raw */ + if(pinfo->ss_raw[t8]&t7) + { + C.ss_raw.tag=content.tag; + C.ss_raw.length=subregion.length-1; + C.ss_raw.raw=malloc(C.ss_raw.length); + if(!limited_read(C.ss_raw.raw,C.ss_raw.length,&subregion,pinfo)) + return 0; + CBP(pinfo,OPS_PTAG_RAW_SS,&content); + return 1; + } + + switch(content.tag) + { + case OPS_PTAG_SS_CREATION_TIME: + case OPS_PTAG_SS_EXPIRATION_TIME: + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + if(!limited_read_time(&C.ss_time.time,&subregion,pinfo)) + return 0; + if(content.tag == OPS_PTAG_SS_CREATION_TIME) + { + sig->info.creation_time=C.ss_time.time; + sig->info.creation_time_set=ops_true; + } + break; + + case OPS_PTAG_SS_TRUST: + if(!limited_read(&C.ss_trust.level,1,&subregion,pinfo) + || !limited_read(&C.ss_trust.amount,1,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCABLE: + if(!limited_read(bool,1,&subregion,pinfo)) + return 0; + C.ss_revocable.revocable=!!bool[0]; + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + if(!limited_read(C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE, + &subregion,pinfo)) + return 0; + memcpy(sig->info.signer_id,C.ss_issuer_key_id.key_id,OPS_KEY_ID_SIZE); + sig->info.signer_id_set=ops_true; + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + if(!read_data(&C.ss_preferred_ska.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + if(!read_data(&C.ss_preferred_hash.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + if(!read_data(&C.ss_preferred_compression.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + if(!limited_read (bool,1,&subregion,pinfo)) + return 0; + C.ss_primary_user_id.primary_user_id = !!bool[0]; + break; + + case OPS_PTAG_SS_KEY_FLAGS: + if(!read_data(&C.ss_key_flags.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + if(!read_data(&C.ss_key_server_prefs.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_FEATURES: + if(!read_data(&C.ss_features.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + if(!read_unsigned_string(&C.ss_signers_user_id.user_id,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + // \todo should do something with this sig? + if (!read_data(&C.ss_embedded_signature.sig,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_NOTATION_DATA: + if(!limited_read_data(&C.ss_notation_data.flags,4,&subregion,pinfo)) + return 0; + if(!limited_read_size_t_scalar(&C.ss_notation_data.name.len,2, + &subregion,pinfo)) + return 0; + if(!limited_read_size_t_scalar(&C.ss_notation_data.value.len,2, + &subregion,pinfo)) + return 0; + if(!limited_read_data(&C.ss_notation_data.name, + C.ss_notation_data.name.len,&subregion,pinfo)) + return 0; + if(!limited_read_data(&C.ss_notation_data.value, + C.ss_notation_data.value.len,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_POLICY_URI: + if(!read_string(&C.ss_policy_url.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REGEXP: + if(!read_string(&C.ss_regexp.text,&subregion, pinfo)) + return 0; + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + if(!read_string(&C.ss_preferred_key_server.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + if(!read_data(&C.ss_userdefined.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_RESERVED: + if(!read_data(&C.ss_unknown.data,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + /* first byte is the machine-readable code */ + if(!limited_read(&C.ss_revocation_reason.code,1,&subregion,pinfo)) + return 0; + + /* the rest is a human-readable UTF-8 string */ + if(!read_string(&C.ss_revocation_reason.text,&subregion,pinfo)) + return 0; + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + /* octet 0 = clss. Bit 0x80 must be set */ + if(!limited_read (&C.ss_revocation_key.clss,1,&subregion,pinfo)) + return 0; + if(!(C.ss_revocation_key.clss&0x80)) + { + printf("Warning: OPS_PTAG_SS_REVOCATION_KEY class: " + "Bit 0x80 should be set\n"); + return 0; + } + + /* octet 1 = algid */ + if(!limited_read(&C.ss_revocation_key.algid,1,&subregion,pinfo)) + return 0; + + /* octets 2-21 = fingerprint */ + if(!limited_read(&C.ss_revocation_key.fingerprint[0],20,&subregion, + pinfo)) + return 0; + break; + + default: + if(pinfo->ss_parsed[t8]&t7) + OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_UNKNOWN_SS, + "Unknown signature subpacket type (%d)", c[0]&0x7f); + read=ops_false; + break; + } + + /* Application doesn't want it delivered parsed */ + if(!(pinfo->ss_parsed[t8]&t7)) + { + if(content.critical) + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_CRITICAL_SS_IGNORED, + "Critical signature subpacket ignored (%d)", + c[0]&0x7f); + if(!read && !limited_skip(subregion.length-1,&subregion,pinfo)) + return 0; + // printf("skipped %d length %d\n",c[0]&0x7f,subregion.length); + if(read) + ops_parser_content_free(&content); + return 1; + } + + if(read && subregion.length_read != subregion.length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", + subregion.length-subregion.length_read); + return 0; + } + + CBP(pinfo,content.tag,&content); + + return 1; + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type + \param ss_preferred_ska +*/ +void ops_ss_preferred_ska_free(ops_ss_preferred_ska_t *ss_preferred_ska) + { + data_free(&ss_preferred_ska->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type + \param ss_preferred_hash +*/ +void ops_ss_preferred_hash_free(ops_ss_preferred_hash_t *ss_preferred_hash) + { + data_free(&ss_preferred_hash->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_preferred_compression_free(ops_ss_preferred_compression_t *ss_preferred_compression) + { + data_free(&ss_preferred_compression->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_key_flags_free(ops_ss_key_flags_t *ss_key_flags) + { + data_free(&ss_key_flags->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_features_free(ops_ss_features_t *ss_features) + { + data_free(&ss_features->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_key_server_prefs_free(ops_ss_key_server_prefs_t *ss_key_server_prefs) + { + data_free(&ss_key_server_prefs->data); + } + +/** + * \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_signature_subpacket() for each subpacket until the + * entire set is consumed. + * + * This function does not call the callback directly, parse_one_signature_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_signature_subpackets(ops_signature_t *sig, + ops_region_t *region, + ops_parse_info_t *pinfo) + { + ops_region_t subregion; + ops_parser_content_t content; + + ops_init_subregion(&subregion,region); + if(!limited_read_scalar(&subregion.length,2,region,pinfo)) + return 0; + + if(subregion.length > region->length) + ERRP(pinfo,"Subpacket set too long"); + + while(subregion.length_read < subregion.length) + if(!parse_one_signature_subpacket(sig,&subregion,pinfo)) + return 0; + + if(subregion.length_read != subregion.length) + { + if(!limited_skip(subregion.length-subregion.length_read,&subregion, + pinfo)) + ERRP(pinfo,"Read failed while recovering from subpacket length mismatch"); + ERRP(pinfo,"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_signature(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + //debug=1; + if (debug) + { fprintf(stderr, "\nparse_v4_signature\n"); } + + // clear signature + memset(&C.signature,'\0',sizeof C.signature); + + /* We need to hash the packet data from version through the hashed subpacket data */ + + C.signature.v4_hashed_data_start=pinfo->rinfo.alength-1; + + /* Set version,type,algorithms */ + + C.signature.info.version=OPS_V4; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.type=c[0]; + if (debug) + { fprintf(stderr, "signature type=%d\n", C.signature.info.type); } + + /* XXX: check signature type */ + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.key_algorithm=c[0]; + /* XXX: check algorithm */ + if (debug) + { fprintf(stderr, "key_algorithm=%d\n", C.signature.info.key_algorithm); } + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.signature.info.hash_algorithm=c[0]; + /* XXX: check algorithm */ + if (debug) + { fprintf(stderr, "hash_algorithm=%d %s\n", C.signature.info.hash_algorithm, ops_show_hash_algorithm(C.signature.info.hash_algorithm)); } + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE_HEADER,&content); + + if(!parse_signature_subpackets(&C.signature,region,pinfo)) + return 0; + + C.signature.info.v4_hashed_data_length=pinfo->rinfo.alength + -C.signature.v4_hashed_data_start; + + // copy hashed subpackets + if (C.signature.info.v4_hashed_data) + free(C.signature.info.v4_hashed_data); + C.signature.info.v4_hashed_data=ops_mallocz(C.signature.info.v4_hashed_data_length); + + if (!pinfo->rinfo.accumulate) + { + /* We must accumulate, else we can't check the signature */ + fprintf(stderr,"*** ERROR: must set accumulate to true\n"); + assert(0); + } + + memcpy(C.signature.info.v4_hashed_data, + pinfo->rinfo.accumulated+C.signature.v4_hashed_data_start, + C.signature.info.v4_hashed_data_length); + + if(!parse_signature_subpackets(&C.signature,region,pinfo)) + return 0; + + if(!limited_read(C.signature.hash2,2,region,pinfo)) + return 0; + + switch(C.signature.info.key_algorithm) + { + case OPS_PKA_RSA: + if(!limited_read_mpi(&C.signature.info.signature.rsa.sig,region,pinfo)) + return 0; + break; + + case OPS_PKA_DSA: + if(!limited_read_mpi(&C.signature.info.signature.dsa.r,region,pinfo)) + ERRP(pinfo,"Error reading DSA r field in signature"); + if (!limited_read_mpi(&C.signature.info.signature.dsa.s,region,pinfo)) + ERRP(pinfo,"Error reading DSA s field in signature"); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + if(!limited_read_mpi(&C.signature.info.signature.elgamal.r,region,pinfo) + || !limited_read_mpi(&C.signature.info.signature.elgamal.s,region,pinfo)) + return 0; + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + if (!read_data(&C.signature.info.signature.unknown.data,region,pinfo)) + return 0; + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SIGNATURE_ALG, + "Bad v4 signature key algorithm (%s)", + ops_show_pka(C.signature.info.key_algorithm)); + return 0; + } + + if(region->length_read != region->length) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_R_UNCONSUMED_DATA, + "Unconsumed data (%d)", + region->length-region->length_read); + return 0; + } + + CBP(pinfo,OPS_PTAG_CT_SIGNATURE_FOOTER,&content); + + return 1; + } + +/** + * \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_signature(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + assert(region->length_read == 0); /* We should not have read anything so far */ + + memset(&content,'\0',sizeof content); + + if(!limited_read(c,1,region,pinfo)) + return 0; + + if(c[0] == 2 || c[0] == 3) + return parse_v3_signature(region,pinfo); + else if(c[0] == 4) + return parse_v4_signature(region,pinfo); + + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_SIGNATURE_VRSN, + "Bad signature version (%d)",c[0]); + return 0; + } + +/** + \ingroup Core_ReadPackets + \brief Parse Compressed packet +*/ +static int parse_compressed(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(c,1,region,pinfo)) + return 0; + + C.compressed.type=c[0]; + + CBP(pinfo,OPS_PTAG_CT_COMPRESSED,&content); + + /* The content of a compressed data packet is more OpenPGP packets + once decompressed, so recursively handle them */ + + return ops_decompress(region,pinfo,C.compressed.type); + } + +/** + \ingroup Core_ReadPackets + \brief Parse a One Pass Signature packet +*/ +static int parse_one_pass(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(&C.one_pass_signature.version,1,region,pinfo)) + return 0; + if(C.one_pass_signature.version != 3) + { + OPS_ERROR_1(&pinfo->errors,OPS_E_PROTO_BAD_ONE_PASS_SIG_VRSN, + "Bad one-pass signature version (%d)", + C.one_pass_signature.version); + return 0; + } + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.sig_type=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.hash_algorithm=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.key_algorithm=c[0]; + + if(!limited_read(C.one_pass_signature.keyid, + sizeof C.one_pass_signature.keyid,region,pinfo)) + return 0; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.one_pass_signature.nested=!!c[0]; + + CBP(pinfo,OPS_PTAG_CT_ONE_PASS_SIGNATURE,&content); + + // XXX: we should, perhaps, let the app choose whether to hash or not + ops_parse_hash_init(pinfo,C.one_pass_signature.hash_algorithm, + C.one_pass_signature.keyid); + + return 1; + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_userdefined_free(ops_ss_userdefined_t *ss_userdefined) + { + data_free(&ss_userdefined->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_reserved_free(ops_ss_unknown_t *ss_unknown) + { + data_free(&ss_unknown->data); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_notation_data_free(ops_ss_notation_data_t *ss_notation_data) + { + data_free(&ss_notation_data->name); + data_free(&ss_notation_data->value); + } + +void ops_ss_embedded_signature_free(ops_ss_embedded_signature_t *ss_embedded_signature) + { + data_free(&ss_embedded_signature->sig); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this signature sub-packet type +*/ +void ops_ss_revocation_reason_free(ops_ss_revocation_reason_t *ss_revocation_reason) + { + string_free(&ss_revocation_reason->text); + } + +/** + \ingroup Core_Create + \brief Free the memory used when parsing this packet type +*/ +void ops_trust_free(ops_trust_t *trust) + { + data_free(&trust->data); + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Trust packet +*/ +static int +parse_trust (ops_region_t *region, ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if(!read_data(&C.trust.data,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_TRUST, &content); + + return 1; + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Literal Data packet +*/ +static int parse_literal_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + unsigned char c[1]=""; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.literal_data_header.format=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + if(!limited_read((unsigned char *)C.literal_data_header.filename,c[0], + region,pinfo)) + return 0; + C.literal_data_header.filename[c[0]]='\0'; + + if(!limited_read_time(&C.literal_data_header.modification_time,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_HEADER,&content); + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + C.literal_data_body.data = (unsigned char *)malloc(l) ; + + if(!limited_read(C.literal_data_body.data,l,region,pinfo)) + { + free(C.literal_data_body.data); + return 0; + } + + C.literal_data_body.length=l; + + ops_parse_hash_data(pinfo,C.literal_data_body.data,l); + + CBP(pinfo,OPS_PTAG_CT_LITERAL_DATA_BODY,&content); + free(C.literal_data_body.data); + } + + return 1; + } + +/** + * \ingroup Core_Create + * + * ops_secret_key_free() frees the memory associated with "key". Note that + * the key itself is not freed. + * + * \param key + */ + +void ops_secret_key_free(ops_secret_key_t *key) + { + switch(key->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_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 OPS_PKA_DSA: + free_BN(&key->key.dsa.x); + break; + + default: + fprintf(stderr,"ops_secret_key_free: Unknown algorithm: %d (%s)\n",key->public_key.algorithm, ops_show_pka(key->public_key.algorithm)); + //assert(0); + } + + ops_public_key_free(&key->public_key); + } + +void ops_secret_key_copy(ops_secret_key_t *dst,const ops_secret_key_t *src) +{ + *dst = *src ; + ops_public_key_copy(&dst->public_key,&src->public_key); + + switch(src->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + dst->key.rsa.d = BN_dup(src->key.rsa.d) ; + dst->key.rsa.p = BN_dup(src->key.rsa.p) ; + dst->key.rsa.q = BN_dup(src->key.rsa.q) ; + dst->key.rsa.u = BN_dup(src->key.rsa.u) ; + break; + + case OPS_PKA_DSA: + dst->key.dsa.x = BN_dup(src->key.dsa.x) ; + break; + + default: + fprintf(stderr,"ops_secret_key_copy: Unknown algorithm: %d (%s)\n",src->public_key.algorithm, ops_show_pka(src->public_key.algorithm)); + //assert(0); + } +} +static int consume_packet(ops_region_t *region,ops_parse_info_t *pinfo, + ops_boolean_t warn) + { + ops_data_t remainder; + ops_parser_content_t content; + + if(region->indeterminate) + ERRP(pinfo,"Can't consume indeterminate packets"); + + if(read_data(&remainder,region,pinfo)) + { + /* now throw it away */ + data_free(&remainder); + if(warn) + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_CONSUMED,"Warning: packet consumer"); + } + else if(warn) + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Warning: Packet was not consumed"); + else + { + OPS_ERROR(&pinfo->errors,OPS_E_P_PACKET_NOT_CONSUMED,"Packet was not consumed"); + return 0; + } + + return 1; + } + +/** + * \ingroup Core_ReadPackets + * \brief Parse a secret key + */ +static int parse_secret_key(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + unsigned char c[1]=""; + ops_crypt_t decrypt; + int ret=1; + ops_region_t encregion; + ops_region_t *saved_region=NULL; + ops_hash_t checkhash; + int blocksize; + ops_boolean_t crypted; + + if (debug) + { fprintf(stderr,"\n---------\nparse_secret_key:\n"); + fprintf(stderr,"region length=%d, length_read=%d, remainder=%d\n", region->length, region->length_read, region->length-region->length_read); + } + + memset(&content,'\0',sizeof content); + if(!parse_public_key_data(&C.secret_key.public_key,region,pinfo)) + return 0; + + if (debug) + { + fprintf(stderr,"parse_secret_key: public key parsed\n"); + ops_print_public_key(&C.secret_key.public_key); + } + + pinfo->reading_v3_secret=C.secret_key.public_key.version != OPS_V4; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.s2k_usage=c[0]; + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED + || C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.algorithm=c[0]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.s2k_specifier=c[0]; + + assert(C.secret_key.s2k_specifier == OPS_S2KS_SIMPLE + || C.secret_key.s2k_specifier == OPS_S2KS_SALTED + || C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED); + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.hash_algorithm=c[0]; + + if(C.secret_key.s2k_specifier != OPS_S2KS_SIMPLE + && !limited_read(C.secret_key.salt,8,region,pinfo)) + { + return 0; + } + + if(C.secret_key.s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED) + { + if(!limited_read(c,1,region,pinfo)) + return 0; + C.secret_key.octet_count=(16+(c[0]&15)) << ((c[0] >> 4)+6); + } + } + else if(C.secret_key.s2k_usage != OPS_S2KU_NONE) + { + // this is V3 style, looks just like a V4 simple hash + C.secret_key.algorithm=C.secret_key.s2k_usage; + C.secret_key.s2k_usage=OPS_S2KU_ENCRYPTED; + C.secret_key.s2k_specifier=OPS_S2KS_SIMPLE; + C.secret_key.hash_algorithm=OPS_HASH_MD5; + } + + crypted=C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED + || C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED; + + if(crypted) + { + int n; + ops_parser_content_t pc; + char *passphrase; + unsigned char key[OPS_MAX_KEY_SIZE+OPS_MAX_HASH_SIZE]; + ops_hash_t hashes[(OPS_MAX_KEY_SIZE+OPS_MIN_HASH_SIZE-1)/OPS_MIN_HASH_SIZE]; + int keysize; + int hashsize; + size_t l; + + blocksize=ops_block_size(C.secret_key.algorithm); + assert(blocksize > 0 && blocksize <= OPS_MAX_BLOCK_SIZE); + + if(!limited_read(C.secret_key.iv,blocksize,region,pinfo)) + return 0; + + memset(&pc,'\0',sizeof pc); + passphrase=NULL; + pc.content.secret_key_passphrase.passphrase=&passphrase; + pc.content.secret_key_passphrase.secret_key=&C.secret_key; + CBP(pinfo,OPS_PARSER_CMD_GET_SK_PASSPHRASE,&pc); + if(!passphrase) + { + if (debug) + { + // \todo make into proper error + fprintf(stderr,"parse_secret_key: can't get passphrase\n"); + } + + if(!consume_packet(region,pinfo,ops_false)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_SECRET_KEY,&content); + + return 1; + } + + keysize=ops_key_size(C.secret_key.algorithm); + assert(keysize > 0 && keysize <= OPS_MAX_KEY_SIZE); + + hashsize=ops_hash_size(C.secret_key.hash_algorithm); + assert(hashsize > 0 && hashsize <= OPS_MAX_HASH_SIZE); + + for(n=0 ; n*hashsize < keysize ; ++n) + { + int i; + + ops_hash_any(&hashes[n],C.secret_key.hash_algorithm); + hashes[n].init(&hashes[n]); + // preload hashes with zeroes... + for(i=0 ; i < n ; ++i) + hashes[n].add(&hashes[n],(unsigned char *)"",1); + } + + l=strlen(passphrase); + + for(n=0 ; n*hashsize < keysize ; ++n) + { + unsigned i; + + switch(C.secret_key.s2k_specifier) + { + case OPS_S2KS_SALTED: + hashes[n].add(&hashes[n],C.secret_key.salt,OPS_SALT_SIZE); + // flow through... + case OPS_S2KS_SIMPLE: + hashes[n].add(&hashes[n],(unsigned char*)passphrase,l); + break; + + case OPS_S2KS_ITERATED_AND_SALTED: + for(i=0 ; i < C.secret_key.octet_count ; i+=l+OPS_SALT_SIZE) + { + int j=l+OPS_SALT_SIZE; + + if(i+j > C.secret_key.octet_count && i != 0) + j=C.secret_key.octet_count-i; + + hashes[n].add(&hashes[n],C.secret_key.salt, + j > OPS_SALT_SIZE ? OPS_SALT_SIZE : j); + if(j > OPS_SALT_SIZE) + hashes[n].add(&hashes[n],(unsigned char *)passphrase,j-OPS_SALT_SIZE); + } + + } + } + + for(n=0 ; n*hashsize < keysize ; ++n) + { + int r=hashes[n].finish(&hashes[n],key+n*hashsize); + assert(r == hashsize); + } + + free(passphrase); + + ops_crypt_any(&decrypt,C.secret_key.algorithm); + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\nREADING:\niv="); + for (i=0; ilength-region->length_read; + if(C.secret_key.public_key.version != OPS_V4) + { + encregion.length-=2; + } + saved_region=region; + region=&encregion; + } + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + ops_hash_sha1(&checkhash); + ops_reader_push_hash(pinfo,&checkhash); + } + else + { + ops_reader_push_sum16(pinfo); + } + + switch(C.secret_key.public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + if(!limited_read_mpi(&C.secret_key.key.rsa.d,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.p,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.q,region,pinfo) + || !limited_read_mpi(&C.secret_key.key.rsa.u,region,pinfo)) + ret=0; + + break; + + case OPS_PKA_DSA: + + if(!limited_read_mpi(&C.secret_key.key.dsa.x,region,pinfo)) + ret=0; + break; + + default: + OPS_ERROR_2(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,"Unsupported Public Key algorithm %d (%s)",C.secret_key.public_key.algorithm,ops_show_pka(C.secret_key.public_key.algorithm)); + ret=0; + // assert(0); + } + + if (debug) + { + fprintf(stderr,"4 MPIs read\n"); + // ops_print_secret_key_verbose(OPS_PTAG_CT_SECRET_KEY, &C.secret_key); + } + + pinfo->reading_v3_secret=ops_false; + + if(C.secret_key.s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + { + unsigned char hash[20]; + + ops_reader_pop_hash(pinfo); + checkhash.finish(&checkhash,hash); + + if(crypted && C.secret_key.public_key.version != OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + region=saved_region; + } + + if(ret) + { + if(!limited_read(C.secret_key.checkhash,20,region,pinfo)) + return 0; + + if(memcmp(hash,C.secret_key.checkhash,20)) + ERRP(pinfo,"Hash mismatch in secret key"); + } + } + else + { + unsigned short sum; + + sum=ops_reader_pop_sum16(pinfo); + + if(crypted && C.secret_key.public_key.version != OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + region=saved_region; + } + + if(ret) + { + if(!limited_read_scalar(&C.secret_key.checksum,2,region, + pinfo)) + return 0; + + if(sum != C.secret_key.checksum) + ERRP(pinfo,"Checksum mismatch in secret key"); + } + } + + if(crypted && C.secret_key.public_key.version == OPS_V4) + { + ops_reader_pop_decrypt(pinfo); + } + + assert(!ret || region->length_read == region->length); + + if(!ret) + return 0; + + CBP(pinfo,OPS_PTAG_CT_SECRET_KEY,&content); + + if (debug) + { fprintf(stderr, "--- end of parse_secret_key\n\n"); } + + return 1; + } + +/** + \ingroup Core_ReadPackets + \brief Parse a Public Key Session Key packet +*/ +static int parse_pk_session_key(ops_region_t *region, + ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + ops_parser_content_t pc; + + int n; + BIGNUM *enc_m; + unsigned k; + const ops_secret_key_t *secret; + unsigned char cs[2]; + unsigned char* iv; + + // Can't rely on it being CAST5 + // \todo FIXME RW + // const size_t sz_unencoded_m_buf=CAST_KEY_LENGTH+1+2; + const size_t sz_unencoded_m_buf=1024; + unsigned char unencoded_m_buf[sz_unencoded_m_buf]; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.pk_session_key.version=c[0]; + if(C.pk_session_key.version != OPS_PKSK_V3) + { + OPS_ERROR_1(&pinfo->errors, OPS_E_PROTO_BAD_PKSK_VRSN, + "Bad public-key encrypted session key version (%d)", + C.pk_session_key.version); + return 0; + } + + if(!limited_read(C.pk_session_key.key_id, + sizeof C.pk_session_key.key_id,region,pinfo)) + return 0; + + if (debug) + { + int i; + int x=sizeof C.pk_session_key.key_id; + printf("session key: public key id: x=%d\n",x); + for (i=0; ierrors, OPS_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG, + "Unknown public key algorithm in session key (%s)", + ops_show_pka(C.pk_session_key.algorithm)); + return 0; + } + + memset(&pc,'\0',sizeof pc); + secret=NULL; + pc.content.get_secret_key.secret_key=&secret; + pc.content.get_secret_key.pk_session_key=&C.pk_session_key; + + CBP(pinfo,OPS_PARSER_CMD_GET_SECRET_KEY,&pc); + + if(!secret) + { + CBP(pinfo,OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY,&content); + + return 1; + } + + // n=ops_decrypt_mpi(buf,sizeof buf,enc_m,secret); + n=ops_decrypt_and_unencode_mpi(unencoded_m_buf,sizeof unencoded_m_buf,enc_m,secret); + + if(n < 1) + { + ERRP(pinfo,"decrypted message too short"); + return 0; + } + + // PKA + C.pk_session_key.symmetric_algorithm=unencoded_m_buf[0]; + + if (!ops_is_sa_supported(C.pk_session_key.symmetric_algorithm)) + { + // ERR1P + OPS_ERROR_1(&pinfo->errors,OPS_E_ALG_UNSUPPORTED_SYMMETRIC_ALG, + "Symmetric algorithm %s not supported", + ops_show_symmetric_algorithm(C.pk_session_key.symmetric_algorithm)); + return 0; + } + + k=ops_key_size(C.pk_session_key.symmetric_algorithm); + + if((unsigned)n != k+3) + { + OPS_ERROR_2(&pinfo->errors,OPS_E_PROTO_DECRYPTED_MSG_WRONG_LEN, + "decrypted message wrong length (got %d expected %d)", + n,k+3); + return 0; + } + + assert(k <= sizeof C.pk_session_key.key); + + memcpy(C.pk_session_key.key,unencoded_m_buf+1,k); + + if (debug) + { + printf("session key recovered (len=%d):\n",k); + unsigned int j; + for(j=0; jerrors, OPS_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; + } + + // all is well + CBP(pinfo,OPS_PTAG_CT_PK_SESSION_KEY,&content); + + ops_crypt_any(&pinfo->decrypt,C.pk_session_key.symmetric_algorithm); + iv=ops_mallocz(pinfo->decrypt.blocksize); + pinfo->decrypt.set_iv(&pinfo->decrypt, iv); + pinfo->decrypt.set_key(&pinfo->decrypt,C.pk_session_key.key); + ops_encrypt_init(&pinfo->decrypt); + return 1; + } + +// XXX: make this static? +int ops_decrypt_se_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + int r=1; + ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo); + + if(decrypt) + { + unsigned char buf[OPS_MAX_BLOCK_SIZE+2]=""; + size_t b=decrypt->blocksize; + // ops_parser_content_t content; + ops_region_t encregion; + + + ops_reader_push_decrypt(pinfo,decrypt,region); + + ops_init_subregion(&encregion,NULL); + encregion.length=b+2; + + if(!exact_limited_read(buf,b+2,&encregion,pinfo)) + return 0; + + if(buf[b-2] != buf[b] || buf[b-1] != buf[b+1]) + { + ops_reader_pop_decrypt(pinfo); + OPS_ERROR_4(&pinfo->errors, OPS_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 == OPS_PTAG_CT_SE_DATA_BODY) + { + decrypt->decrypt_resync(decrypt); + decrypt->block_encrypt(decrypt,decrypt->civ,decrypt->civ); + } + + + r=ops_parse(pinfo); + + ops_reader_pop_decrypt(pinfo); + } + else + { + ops_parser_content_t content; + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + if(l > sizeof C.se_data_body.data) + l=sizeof C.se_data_body.data; + + if(!limited_read(C.se_data_body.data,l,region,pinfo)) + return 0; + + C.se_data_body.length=l; + + CBP(pinfo,tag,&content); + } + } + + return r; + } + +int ops_decrypt_se_ip_data(ops_content_tag_t tag,ops_region_t *region, + ops_parse_info_t *pinfo) + { + int r=1; + ops_crypt_t *decrypt=ops_parse_get_decrypt(pinfo); + + if(decrypt) + { + ops_reader_push_decrypt(pinfo,decrypt,region); + ops_reader_push_se_ip_data(pinfo,decrypt,region); + + r=ops_parse(pinfo); + + // assert(0); + ops_reader_pop_se_ip_data(pinfo); + ops_reader_pop_decrypt(pinfo); + } + else + { + ops_parser_content_t content; + + while(region->length_read < region->length) + { + unsigned l=region->length-region->length_read; + + if(l > sizeof C.se_data_body.data) + l=sizeof C.se_data_body.data; + + if(!limited_read(C.se_data_body.data,l,region,pinfo)) + return 0; + + C.se_data_body.length=l; + + CBP(pinfo,tag,&content); + } + } + + return r; + } + +/** + \ingroup Core_ReadPackets + \brief Read a Symmetrically Encrypted packet +*/ +static int parse_se_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + /* there's no info to go with this, so just announce it */ + CBP(pinfo,OPS_PTAG_CT_SE_DATA_HEADER,&content); + + /* The content of an encrypted data packet is more OpenPGP packets + once decrypted, so recursively handle them */ + return ops_decrypt_se_data(OPS_PTAG_CT_SE_DATA_BODY,region,pinfo); + } + +/** + \ingroup Core_ReadPackets + \brief Read a Symmetrically Encrypted Integrity Protected packet +*/ +static int parse_se_ip_data(ops_region_t *region,ops_parse_info_t *pinfo) + { + unsigned char c[1]=""; + ops_parser_content_t content; + + if(!limited_read(c,1,region,pinfo)) + return 0; + C.se_ip_data_header.version=c[0]; + assert(C.se_ip_data_header.version == OPS_SE_IP_V1); + + /* The content of an encrypted data packet is more OpenPGP packets + once decrypted, so recursively handle them */ + return ops_decrypt_se_ip_data(OPS_PTAG_CT_SE_IP_DATA_BODY,region,pinfo); + } + +/** + \ingroup Core_ReadPackets + \brief Read a MDC packet +*/ +static int parse_mdc(ops_region_t *region, ops_parse_info_t *pinfo) + { + ops_parser_content_t content; + + if (!limited_read((unsigned char *)&C.mdc,OPS_SHA1_HASH_SIZE,region,pinfo)) + return 0; + + CBP(pinfo,OPS_PTAG_CT_MDC,&content); + + 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 *pinfo 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 ops_parse_one_packet(ops_parse_info_t *pinfo, + unsigned long *pktlen) + { + unsigned char ptag[1]; + ops_parser_content_t content; + int r; + ops_region_t region; + ops_boolean_t indeterminate=ops_false; + + C.ptag.position=pinfo->rinfo.position; + + r=base_read(ptag,1,pinfo); + + // errors in the base read are effectively EOF. + if(r <= 0) + return -1; + + *pktlen=0; + + if(!(*ptag&OPS_PTAG_ALWAYS_SET)) + { + C.error.error="Format error (ptag bit not set)"; + CBP(pinfo,OPS_PARSER_ERROR,&content); + OPS_ERROR(&pinfo->errors, OPS_E_P_UNKNOWN_TAG, C.error.error); + return 0; + } + C.ptag.new_format=!!(*ptag&OPS_PTAG_NEW_FORMAT); + if(C.ptag.new_format) + { + C.ptag.content_tag=*ptag&OPS_PTAG_NF_CONTENT_TAG_MASK; + C.ptag.length_type=0; + if(!read_new_length(&C.ptag.length,pinfo)) + return 0; + + } + else + { + ops_boolean_t rb = ops_false; + + C.ptag.content_tag=(*ptag&OPS_PTAG_OF_CONTENT_TAG_MASK) + >> OPS_PTAG_OF_CONTENT_TAG_SHIFT; + C.ptag.length_type=*ptag&OPS_PTAG_OF_LENGTH_TYPE_MASK; + switch(C.ptag.length_type) + { + case OPS_PTAG_OF_LT_ONE_BYTE: + rb=_read_scalar(&C.ptag.length,1,pinfo); + break; + + case OPS_PTAG_OF_LT_TWO_BYTE: + rb=_read_scalar(&C.ptag.length,2,pinfo); + break; + + case OPS_PTAG_OF_LT_FOUR_BYTE: + rb=_read_scalar(&C.ptag.length,4,pinfo); + break; + + case OPS_PTAG_OF_LT_INDETERMINATE: + C.ptag.length=0; + indeterminate=ops_true; + rb=ops_true; + break; + } + if(!rb) + { + OPS_ERROR(&pinfo->errors, OPS_E_P, "Cannot read tag length"); + return 0; + } + } + + CBP(pinfo,OPS_PARSER_PTAG,&content); + + ops_init_subregion(®ion,NULL); + region.length=C.ptag.length; + region.indeterminate=indeterminate; + switch(C.ptag.content_tag) + { + case OPS_PTAG_CT_SIGNATURE: + r=parse_signature(®ion,pinfo); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + r=parse_public_key(C.ptag.content_tag,®ion,pinfo); + break; + + case OPS_PTAG_CT_TRUST: + r=parse_trust(®ion, pinfo); + break; + + case OPS_PTAG_CT_USER_ID: + r=parse_user_id(®ion,pinfo); + break; + + case OPS_PTAG_CT_COMPRESSED: + r=parse_compressed(®ion,pinfo); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + r=parse_one_pass(®ion,pinfo); + break; + + case OPS_PTAG_CT_LITERAL_DATA: + r=parse_literal_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + r=parse_user_attribute(®ion,pinfo); + break; + + case OPS_PTAG_CT_SECRET_KEY: + r=parse_secret_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_SECRET_SUBKEY: + r=parse_secret_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + r=parse_pk_session_key(®ion,pinfo); + break; + + case OPS_PTAG_CT_SE_DATA: + r=parse_se_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_SE_IP_DATA: + r=parse_se_ip_data(®ion,pinfo); + break; + + case OPS_PTAG_CT_MDC: + r=parse_mdc(®ion, pinfo); + break; + + default: + OPS_ERROR_1(&pinfo->errors,OPS_E_P_UNKNOWN_TAG, + "Unknown content tag 0x%x", C.ptag.content_tag); + r=0; + } + + /* Ensure that the entire packet has been consumed */ + + if(region.length != region.length_read && !region.indeterminate) + if(!consume_packet(®ion,pinfo,ops_false)) + r=-1; + + // also consume it if there's been an error? + // \todo decide what to do about an error on an + // indeterminate packet + if (r==0) + { + if (!consume_packet(®ion,pinfo,ops_false)) + r=-1; + } + + /* set pktlen */ + + *pktlen=pinfo->rinfo.alength; + + /* do callback on entire packet, if desired and there was no error */ + + if(r > 0 && pinfo->rinfo.accumulate) + { + C.packet.length=pinfo->rinfo.alength; + C.packet.raw=pinfo->rinfo.accumulated; + + CBP(pinfo,OPS_PARSER_PACKET_END,&content); + //free(pinfo->rinfo.accumulated); + pinfo->rinfo.accumulated=NULL; + pinfo->rinfo.asize=0; + } + else + C.packet.raw = NULL ; + + pinfo->rinfo.alength=0; + + if(r < 0) + return -1; + + return r ? 1 : 0; + } + +/** + * \ingroup Core_ReadPackets + * + * \brief Parse packets from an input stream until EOF or error. + * + * \details Setup the necessary parsing configuration in "pinfo" before calling ops_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, pinfo->errors holds any errors encountered while parsing. + * + * \param pinfo Parsing configuration + * \return 1 on success in all packets, 0 on error in any packet + * + * \sa CoreAPI Overview + * + * \sa ops_print_errors(), ops_parse_and_print_errors() + * + * Example code + * \code +ops_parse_cb_t* example_callback(); +void example() + { + int fd=0; + ops_parse_info_t *pinfo=NULL; + char *filename="pubring.gpg"; + + // setup pinfo to read from file with example callback + fd=ops_setup_file_read(&pinfo, filename, NULL, example_callback, ops_false); + + // specify how we handle signature subpackets + ops_parse_options(pinfo, OPS_PTAG_SS_ALL, OPS_PARSE_PARSED); + + if (!ops_parse(pinfo)) + ops_print_errors(pinfo->errors); + + ops_teardown_file_read(pinfo,fd); + } + * \endcode + */ + +int ops_parse(ops_parse_info_t *pinfo) + { + int r; + unsigned long pktlen; + + do + // Parse until we get a return code of 0 (error) or -1 (EOF) + { + r=ops_parse_one_packet(pinfo,&pktlen); + } while (r > 0); + + return pinfo->errors ? 0 : 1; + return r == -1 ? 0 : 1; + } + +/** +\ingroup Core_ReadPackets +\brief Parse packets and print any errors + * \param pinfo Parsing configuration + * \return 1 on success in all packets, 0 on error in any packet + * \sa CoreAPI Overview + * \sa ops_parse() +*/ + +int ops_parse_and_print_errors(ops_parse_info_t *pinfo) + { + ops_parse(pinfo); + ops_print_errors(pinfo->errors); + return pinfo->errors ? 0 : 1; + } + +/** + * \ingroup Core_ReadPackets + * + * \brief Specifies whether one or more signature + * subpacket types should be returned parsed; or raw; or ignored. + * + * \param pinfo Pointer to previously allocated structure + * \param tag Packet tag. OPS_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 ops_parse_options(ops_parse_info_t *pinfo, + ops_content_tag_t tag, + ops_parse_type_t type) + { + int t8,t7; + + if(tag == OPS_PTAG_SS_ALL) + { + int n; + + for(n=0 ; n < 256 ; ++n) + ops_parse_options(pinfo,OPS_PTAG_SIGNATURE_SUBPACKET_BASE+n, + type); + return; + } + + assert(tag >= OPS_PTAG_SIGNATURE_SUBPACKET_BASE + && tag <= OPS_PTAG_SIGNATURE_SUBPACKET_BASE+NTAGS-1); + t8=(tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)/8; + t7=1 << ((tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE)&7); + switch(type) + { + case OPS_PARSE_RAW: + pinfo->ss_raw[t8] |= t7; + pinfo->ss_parsed[t8] &= ~t7; + break; + + case OPS_PARSE_PARSED: + pinfo->ss_raw[t8] &= ~t7; + pinfo->ss_parsed[t8] |= t7; + break; + + case OPS_PARSE_IGNORE: + pinfo->ss_raw[t8] &= ~t7; + pinfo->ss_parsed[t8] &= ~t7; + break; + } + } + +/** +\ingroup Core_ReadPackets +\brief Creates a new zero-ed ops_parse_info_t struct +\sa ops_parse_info_delete() +*/ +ops_parse_info_t *ops_parse_info_new(void) + { return ops_mallocz(sizeof(ops_parse_info_t)); } + +/** +\ingroup Core_ReadPackets +\brief Free ops_parse_info_t struct and its contents +\sa ops_parse_info_new() +*/ +void ops_parse_info_delete(ops_parse_info_t *pinfo) + { + ops_parse_cb_info_t *cbinfo,*next; + + for(cbinfo=pinfo->cbinfo.next ; cbinfo ; cbinfo=next) + { + next=cbinfo->next; + free(cbinfo); + } + if(pinfo->rinfo.destroyer) + pinfo->rinfo.destroyer(&pinfo->rinfo); + ops_free_errors(pinfo->errors); + if(pinfo->rinfo.accumulated) + free(pinfo->rinfo.accumulated); + free(pinfo); + } + +/** +\ingroup Core_ReadPackets +\brief Returns the parse_info's reader_info +\return Pointer to the reader_info inside the parse_info +*/ +ops_reader_info_t *ops_parse_get_rinfo(ops_parse_info_t *pinfo) + { return &pinfo->rinfo; } + +/** +\ingroup Core_ReadPackets +\brief Sets the parse_info's callback +This is used when adding the first callback in a stack of callbacks. +\sa ops_parse_cb_push() +*/ + +void ops_parse_cb_set(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg) + { + pinfo->cbinfo.cb=cb; + pinfo->cbinfo.arg=arg; + pinfo->cbinfo.errors=&pinfo->errors; + } + +/** +\ingroup Core_ReadPackets +\brief Adds a further callback to a stack of callbacks +\sa ops_parse_cb_set() +*/ +void ops_parse_cb_push(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg) + { + ops_parse_cb_info_t *cbinfo=malloc(sizeof *cbinfo); + + *cbinfo=pinfo->cbinfo; + pinfo->cbinfo.next=cbinfo; + ops_parse_cb_set(pinfo,cb,arg); + } + +/** +\ingroup Core_ReadPackets +\brief Returns callback's arg +*/ +void *ops_parse_cb_get_arg(ops_parse_cb_info_t *cbinfo) + { return cbinfo->arg; } + +/** +\ingroup Core_ReadPackets +\brief Returns callback's errors +*/ +void *ops_parse_cb_get_errors(ops_parse_cb_info_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 OPS_FINISHED +*/ +ops_parse_cb_return_t ops_parse_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo) + { + if(cbinfo->cb) + return cbinfo->cb(content,cbinfo); + else + return OPS_FINISHED; + } + +/** +\ingroup Core_ReadPackets +\brief Calls the next callback in the stack +\return Return value from callback +*/ +ops_parse_cb_return_t ops_parse_stacked_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo) + { return ops_parse_cb(content,cbinfo->next); } + +/** +\ingroup Core_ReadPackets +\brief Returns the parse_info's errors +\return parse_info's errors +*/ +ops_error_t *ops_parse_info_get_errors(ops_parse_info_t *pinfo) + { return pinfo->errors; } + +ops_crypt_t *ops_parse_get_decrypt(ops_parse_info_t *pinfo) + { + if(pinfo->decrypt.algorithm) + return &pinfo->decrypt; + return NULL; + } + +// XXX: this could be improved by sharing all hashes that are the +// same, then duping them just before checking the signature. +void ops_parse_hash_init(ops_parse_info_t *pinfo,ops_hash_algorithm_t type, + const unsigned char *keyid) + { + ops_parse_hash_info_t *hash; + + pinfo->hashes=realloc(pinfo->hashes, + (pinfo->nhashes+1)*sizeof *pinfo->hashes); + hash=&pinfo->hashes[pinfo->nhashes++]; + + ops_hash_any(&hash->hash,type); + hash->hash.init(&hash->hash); + memcpy(hash->keyid,keyid,sizeof hash->keyid); + } + +void ops_parse_hash_data(ops_parse_info_t *pinfo,const void *data, + size_t length) + { + size_t n; + + for(n=0 ; n < pinfo->nhashes ; ++n) + pinfo->hashes[n].hash.add(&pinfo->hashes[n].hash,data,length); + } + +ops_hash_t *ops_parse_hash_find(ops_parse_info_t *pinfo, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + size_t n; + + for(n=0 ; n < pinfo->nhashes ; ++n) + if(!memcmp(pinfo->hashes[n].keyid,keyid,OPS_KEY_ID_SIZE)) + return &pinfo->hashes[n].hash; + return NULL; + } + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ diff --git a/openpgpsdk/src/openpgpsdk/packet-parse.h b/openpgpsdk/src/openpgpsdk/packet-parse.h new file mode 100644 index 000000000..f0bcfb8da --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-parse.h @@ -0,0 +1,161 @@ +/* + * 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 OPS_PACKET_PARSE_H +#define OPS_PACKET_PARSE_H + +#include "types.h" +#include "packet.h" +#include "lists.h" + +/** ops_region_t */ +typedef struct ops_region + { + struct ops_region *parent; + unsigned length; + unsigned length_read; + unsigned last_read; /*!< length of last read, only valid in deepest child */ + ops_boolean_t indeterminate:1; + } ops_region_t; + +void ops_init_subregion(ops_region_t *subregion,ops_region_t *region); + +#if 0 +/** Return values for reader functions e.g. ops_packet_reader_t() */ +enum ops_reader_ret_t + { + OPS_R_OK =0, /*!< success */ + OPS_R_EOF =1, /*!< reached end of file, no data has been returned */ + OPS_R_EARLY_EOF =2, /*!< could not read the requested + number of bytes and either + OPS_RETURN_LENGTH was not set and at + least 1 byte was read, or there was + an abnormal end to the file (or + armoured block) */ + OPS_R_PARTIAL_READ =3, /*!< if OPS_RETURN_LENGTH is set and + the buffer was not filled */ + OPS_R_ERROR =4, /*!< if there was an error reading */ + }; +#endif + +/** ops_parse_callback_return_t */ +typedef enum + { + OPS_RELEASE_MEMORY, + OPS_KEEP_MEMORY, + OPS_FINISHED + } ops_parse_cb_return_t; + +typedef struct ops_parse_cb_info ops_parse_cb_info_t; + +typedef ops_parse_cb_return_t +ops_parse_cb_t(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); + +typedef struct ops_parse_info ops_parse_info_t; +typedef struct ops_reader_info ops_reader_info_t; +typedef struct ops_crypt_info ops_crypt_info_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 ops_reader_t(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo); + +typedef void ops_reader_destroyer_t(ops_reader_info_t *rinfo); + +ops_parse_info_t *ops_parse_info_new(void); +void ops_parse_info_delete(ops_parse_info_t *pinfo); +ops_error_t *ops_parse_info_get_errors(ops_parse_info_t *pinfo); +ops_crypt_t *ops_parse_get_decrypt(ops_parse_info_t *pinfo); + +void ops_parse_cb_set(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg); +void ops_parse_cb_push(ops_parse_info_t *pinfo,ops_parse_cb_t *cb,void *arg); +void *ops_parse_cb_get_arg(ops_parse_cb_info_t *cbinfo); +void *ops_parse_cb_get_errors(ops_parse_cb_info_t *cbinfo); +void ops_reader_set(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg); +void ops_reader_push(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg); +void ops_reader_pop(ops_parse_info_t *pinfo); +void *ops_reader_get_arg_from_pinfo(ops_parse_info_t *pinfo); + +void *ops_reader_get_arg(ops_reader_info_t *rinfo); + +ops_parse_cb_return_t ops_parse_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t ops_parse_stacked_cb(const ops_parser_content_t *content, + ops_parse_cb_info_t *cbinfo); +ops_reader_info_t *ops_parse_get_rinfo(ops_parse_info_t *pinfo); + +int ops_parse(ops_parse_info_t *parse_info); +int ops_parse_and_print_errors(ops_parse_info_t *parse_info); +int ops_parse_and_save_errs(ops_parse_info_t *parse_info,ops_ulong_list_t *errs); +int ops_parse_errs(ops_parse_info_t *parse_info,ops_ulong_list_t *errs); + +void ops_parse_and_validate(ops_parse_info_t *parse_info); + +void ops_parse_options(ops_parse_info_t *pinfo,ops_content_tag_t tag, + ops_parse_type_t type); + +ops_boolean_t ops_limited_read(unsigned char *dest,size_t length, + ops_region_t *region,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo); +ops_boolean_t ops_stacked_limited_read(void *dest,unsigned length, + ops_region_t *region, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo); +void ops_parse_hash_init(ops_parse_info_t *pinfo,ops_hash_algorithm_t type, + const unsigned char *keyid); +void ops_parse_hash_data(ops_parse_info_t *pinfo,const void *data, + size_t length); +void ops_parse_hash_finish(ops_parse_info_t *pinfo); +ops_hash_t *ops_parse_hash_find(ops_parse_info_t *pinfo, + const unsigned char keyid[OPS_KEY_ID_SIZE]); + +ops_reader_t ops_stacked_read; + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + + +#endif diff --git a/openpgpsdk/src/openpgpsdk/packet-print.c b/openpgpsdk/src/openpgpsdk/packet-print.c new file mode 100644 index 000000000..7d6fee2d3 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-print.c @@ -0,0 +1,1836 @@ +/* + * 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 Standard API print functions +*/ + +#include +#include +#include "openpgpsdk/crypto.h" +#include "openpgpsdk/keyring.h" +#include "keyring_local.h" +#include "parse_local.h" +#include "openpgpsdk/packet-show.h" +#include "openpgpsdk/util.h" +#include "openpgpsdk/std_print.h" +#include "openpgpsdk/readerwriter.h" +#include "openpgpsdk/armour.h" + +static int indent=0; + +void print_bn( const char *name, + const BIGNUM *bn); +static void print_hex(const unsigned char *src, + size_t length); +static void print_hexdump(const char *name, + const unsigned char *data, + unsigned int len); +static void print_hexdump_data(const char *name, + const unsigned char *data, + unsigned int len); +static void print_indent(); +static void print_name(const char *name); +static void print_string_and_value(char *name, + const char *str, + unsigned char value); +static void print_tagname(const char *str); +static void print_time( char *name, + time_t time); +static void print_time_short(time_t time); +static void print_unsigned_int(char *name, + unsigned int val); +static void showtime(const char *name,time_t t); +static void showtime_short(time_t t); + +/** + \ingroup Core_Print + + Prints a public key in succinct detail + + \param key Ptr to public key +*/ + +void +ops_print_public_keydata(const ops_keydata_t *key) + { + printf("pub "); + + ops_show_pka(key->key.pkey.algorithm); + printf(" "); + + hexdump(key->key_id, OPS_KEY_ID_SIZE); + printf(" "); + + print_time_short(key->key.pkey.creation_time); + printf(" "); + + if (key->nuids==1) + { + // print on same line as other info + printf ("%s\n", key->uids[0].user_id); + } + else + { + // print all uids on separate line + unsigned int i; + printf("\n"); + for (i=0; inuids; i++) + { + printf("uid %s\n",key->uids[i].user_id); + } + } + } + +/** +\ingroup Core_Print +\param pkey +*/ +void +ops_print_public_key(const ops_public_key_t *pkey) + { + printf("------- PUBLIC KEY ------\n"); + print_unsigned_int("Version",pkey->version); + print_time("Creation Time", pkey->creation_time); + if(pkey->version == OPS_V3) + print_unsigned_int("Days Valid",pkey->days_valid); + + print_string_and_value("Algorithm",ops_show_pka(pkey->algorithm), + pkey->algorithm); + + switch(pkey->algorithm) + { + case OPS_PKA_DSA: + print_bn("p",pkey->key.dsa.p); + print_bn("q",pkey->key.dsa.q); + print_bn("g",pkey->key.dsa.g); + print_bn("y",pkey->key.dsa.y); + break; + + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("n",pkey->key.rsa.n); + print_bn("e",pkey->key.rsa.e); + break; + + case OPS_PKA_ELGAMAL: + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("p",pkey->key.elgamal.p); + print_bn("g",pkey->key.elgamal.g); + print_bn("y",pkey->key.elgamal.y); + break; + + default: + assert(0); + } + + printf("------- end of PUBLIC KEY ------\n"); + } + +/** + \ingroup Core_Print + + Prints a public key in full detail + + \param key Ptr to public key +*/ + +void +ops_print_public_keydata_verbose(const ops_keydata_t *key) + { + const ops_public_key_t* pkey=&key->key.pkey; + + ops_print_public_key(pkey); + } + +/** + \ingroup Core_Print + + Prints a secret key + + \param key Ptr to public key +*/ + +void +ops_print_secret_keydata(const ops_keydata_t *key) + { + printf("sec "); + ops_show_pka(key->key.pkey.algorithm); + printf(" "); + + hexdump(key->key_id, OPS_KEY_ID_SIZE); + printf(" "); + + print_time_short(key->key.pkey.creation_time); + printf(" "); + + if (key->nuids==1) + { + // print on same line as other info + printf ("%s\n", key->uids[0].user_id); + } + else + { + // print all uids on separate line + unsigned int i; + printf("\n"); + for (i=0; inuids; i++) + { + printf("uid %s\n",key->uids[i].user_id); + } + } + } + +/* +void +ops_print_secret_key_verbose(const ops_secret_key_t* skey) + { + if(key->type == OPS_PTAG_CT_SECRET_KEY) + print_tagname("SECRET_KEY"); + else + print_tagname("ENCRYPTED_SECRET_KEY"); + ops_print_secret_key(key->type,skey); + } +*/ + +/** +\ingroup Core_Print +\param type +\param skey +*/ +void +ops_print_secret_key_verbose(const ops_content_tag_t type, const ops_secret_key_t* skey) + { + printf("------- SECRET KEY or ENCRYPTED SECRET KEY ------\n"); + if(type == OPS_PTAG_CT_SECRET_KEY) + print_tagname("SECRET_KEY"); + else + print_tagname("ENCRYPTED_SECRET_KEY"); + // ops_print_public_key(key); + printf("S2K Usage: %d\n",skey->s2k_usage); + if(skey->s2k_usage != OPS_S2KU_NONE) + { + printf("S2K Specifier: %d\n",skey->s2k_specifier); + printf("Symmetric algorithm: %d (%s)\n",skey->algorithm, + ops_show_symmetric_algorithm(skey->algorithm)); + printf("Hash algorithm: %d (%s)\n",skey->hash_algorithm, + ops_show_hash_algorithm(skey->hash_algorithm)); + if(skey->s2k_specifier != OPS_S2KS_SIMPLE) + print_hexdump("Salt",skey->salt,sizeof skey->salt); + if(skey->s2k_specifier == OPS_S2KS_ITERATED_AND_SALTED) + printf("Octet count: %d\n",skey->octet_count); + print_hexdump("IV",skey->iv,ops_block_size(skey->algorithm)); + } + + /* no more set if encrypted */ + if(type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + return; + + switch(skey->public_key.algorithm) + { + case OPS_PKA_RSA: + print_bn("d",skey->key.rsa.d); + print_bn("p",skey->key.rsa.p); + print_bn("q",skey->key.rsa.q); + print_bn("u",skey->key.rsa.u); + break; + + case OPS_PKA_DSA: + print_bn("x",skey->key.dsa.x); + break; + + default: + assert(0); + } + + if(skey->s2k_usage == OPS_S2KU_ENCRYPTED_AND_HASHED) + print_hexdump("Checkhash",skey->checkhash,OPS_CHECKHASH_SIZE); + else + printf("Checksum: %04x\n",skey->checksum); + + printf("------- end of SECRET KEY or ENCRYPTED SECRET KEY ------\n"); + } + +/** +\ingroup Core_Print +\param key +*/ +void +ops_print_secret_keydata_verbose(const ops_keydata_t *key) + { + const ops_secret_key_t* skey=&key->key.skey; + ops_print_public_keydata(key); + ops_print_secret_key_verbose(key->type,skey); + } + +// static functions + +static void print_unsigned_int(char *name, unsigned int val) + { + print_name(name); + printf("%d\n", val); + } + +static void print_time( char *name, time_t time) + { + print_indent(); + printf("%s: ",name); + showtime("time",time); + printf("\n"); + } + +static void print_time_short(time_t time) + { + showtime_short(time); + } + +static void print_string_and_value(char *name,const char *str, + unsigned char value) + { + print_name(name); + + printf("%s", str); + printf(" (0x%x)", value); + printf("\n"); + } + +void print_bn( const char *name, const BIGNUM *bn) + { + print_indent(); + printf("%s=",name); + if(bn) + { + BN_print_fp(stdout,bn); + putchar('\n'); + } + else + puts("(unset)"); + } + +static void print_tagname(const char *str) + { + print_indent(); + printf("%s packet\n", str); + } + +static void print_hexdump(const char *name, + const unsigned char *data, + unsigned int len) + { + print_name(name); + + printf("len=%d, data=0x", len); + print_hex(data,len); + printf("\n"); + } + +static void print_hexdump_data(const char *name, + const unsigned char *data, + unsigned int len) + { + print_name(name); + + printf("0x"); + print_hex(data,len); + printf("\n"); + } + +static void print_data(const char *name,const ops_data_t *data) + { + print_hexdump(name,data->contents,data->len); + } + + +static void print_name(const char *name) + { + print_indent(); + if(name) + printf("%s: ",name); + } + +static void print_indent() + { + int i=0; + + for(i=0 ; i < indent ; i++) + printf(" "); + } + +/* printhex is now print_hex for consistency */ +static void print_hex(const unsigned char *src,size_t length) + { + while(length--) + printf("%02X",*src++); + } + +static void showtime(const char *name,time_t t) + { + printf("%s=" TIME_T_FMT " (%.24s)",name,t,ctime(&t)); + } +static void showtime_short(time_t t) + { + struct tm* tm; + /* + const int maxbuf=512; + char buf[maxbuf+1]; + buf[maxbuf]='\0'; + // this needs to be tm struct + strftime(buf,maxbuf,"%F",&t); + printf(buf); + */ + tm=gmtime(&t); + printf ("%04d-%02d-%02d", tm->tm_year+1900, tm->tm_mon, tm->tm_mday); + } + + +static void print_packet_hex(const ops_packet_t *packet) + { + unsigned char *cur; + int i; + int rem; + int blksz=4; + + printf("\nhexdump of packet contents follows:\n"); + + + for (i=1,cur=packet->raw; cur<(packet->raw+packet->length); cur+=blksz,i++) + { + rem = packet->raw+packet->length-cur; + hexdump(cur,rem<=blksz ? rem : blksz); + printf(" "); + if (!(i%8)) + printf("\n"); + + } + + printf("\n"); + } + +static void print_escaped(const unsigned char *data,size_t length) + { + while(length-- > 0) + { + if((*data >= 0x20 && *data < 0x7f && *data != '%') || *data == '\n') + putchar(*data); + else + printf("%%%02x",*data); + ++data; + } + } + +static void print_string(const char *name,const char *str) + { + print_name(name); + print_escaped((unsigned char *)str,strlen(str)); + putchar('\n'); + } + +static void print_utf8_string(const char *name,const unsigned char *str) + { + // \todo Do this better for non-English character sets + print_string(name,(const char *)str); + } + +static void print_duration(char *name, time_t time) + { + int mins, hours, days, years; + + print_indent(); + printf("%s: ",name); + printf("duration " TIME_T_FMT " seconds",time); + + mins=time/60; + hours=mins/60; + days=hours/24; + years=days/365; + + printf(" (approx. "); + if (years) + printf("%d %s",years,years==1?"year":"years"); + else if (days) + printf("%d %s",days,days==1?"day":"days"); + else if (hours) + printf("%d %s", hours, hours==1?"hour":"hours"); + + printf(")"); + printf("\n"); + } + +static void print_boolean(const char *name, unsigned char bool) + { + print_name(name); + + if(bool) + printf("Yes"); + else + printf("No"); + printf("\n"); + } + +static void print_text_breakdown( ops_text_t *text) + { + unsigned i; + char *prefix=".. "; + + /* these were recognised */ + + for(i=0 ; iknown.used ; i++) + { + print_indent(); + fputs(prefix,stdout); + printf("%s\n",text->known.strings[i]); + } + + /* these were not recognised. the strings will contain the hex value + of the unrecognised value in string format - see process_octet_str() + */ + + if(text->unknown.used) + { + printf("\n"); + print_indent(); + printf("Not Recognised: "); + } + for( i=0; i < text->unknown.used; i++) + { + print_indent(); + fputs(prefix,stdout); + printf("%s\n",text->unknown.strings[i]); + } + + } + +static void print_headers(const ops_headers_t *headers) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + printf("%s=%s\n",headers->headers[n].key,headers->headers[n].value); + } + +static void print_block(const char *name,const unsigned char *str, + size_t length) + { + int o=length; + + print_indent(); + printf(">>>>> %s >>>>>\n",name); + + print_indent(); + for( ; length > 0 ; --length) + { + if(*str >= 0x20 && *str < 0x7f && *str != '%') + putchar(*str); + else if(*str == '\n') + { + putchar(*str); + print_indent(); + } + else + printf("%%%02x",*str); + ++str; + } + if(o && str[-1] != '\n') + { + putchar('\n'); + print_indent(); + fputs("[no newline]",stdout); + } + else + print_indent(); + printf("<<<<< %s <<<<<\n",name); + } + +/** +\ingroup Core_Print +\param tag +\param key +*/ +void ops_print_pk_session_key(ops_content_tag_t tag, + const ops_pk_session_key_t *key) + { + if(tag == OPS_PTAG_CT_PK_SESSION_KEY) + print_tagname("PUBLIC KEY SESSION KEY"); + else + print_tagname("ENCRYPTED PUBLIC KEY SESSION KEY"); + + printf("Version: %d\n",key->version); + print_hexdump("Key ID",key->key_id,sizeof key->key_id); + printf("Algorithm: %d (%s)\n",key->algorithm, + ops_show_pka(key->algorithm)); + switch(key->algorithm) + { + case OPS_PKA_RSA: + print_bn("encrypted_m",key->parameters.rsa.encrypted_m); + break; + + case OPS_PKA_ELGAMAL: + print_bn("g_to_k",key->parameters.elgamal.g_to_k); + print_bn("encrypted_m",key->parameters.elgamal.encrypted_m); + break; + + default: + assert(0); + } + + if(tag != OPS_PTAG_CT_PK_SESSION_KEY) + return; + + printf("Symmetric algorithm: %d (%s)\n",key->symmetric_algorithm, + ops_show_symmetric_algorithm(key->symmetric_algorithm)); + print_hexdump("Key",key->key,ops_key_size(key->symmetric_algorithm)); + printf("Checksum: %04x\n",key->checksum); + } + +static void start_subpacket(unsigned type) + { + indent++; + print_indent(); + printf("-- %s (type 0x%02x)\n", + ops_show_ss_type(type), + type-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + } + +static void end_subpacket() + { + indent--; + } + +/** +\ingroup Core_Print +\param content_ +*/ +int ops_print_packet(const ops_parser_content_t *content_) + { + const ops_parser_content_union_t *content=&content_->content; + ops_text_t *text; + const char *str; + static ops_boolean_t unarmoured; + + if(unarmoured && content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT) + { + unarmoured=ops_false; + puts("UNARMOURED TEXT ends"); + } + + if (content_->tag==OPS_PARSER_PTAG) + { + printf("=> OPS_PARSER_PTAG: %s\n", ops_show_packet_tag(content->ptag.content_tag)); + } + else + { + printf("=> %s\n", ops_show_packet_tag(content_->tag)); + } + + switch(content_->tag) + { + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + break; + + case OPS_PARSER_ERRCODE: + printf("parse error: %s\n", + ops_errcode(content->errcode.errcode)); + break; + + case OPS_PARSER_PACKET_END: + print_packet_hex(&content->packet); + break; + + case OPS_PARSER_PTAG: + if(content->ptag.content_tag == OPS_PTAG_CT_PUBLIC_KEY) + { + indent=0; + printf("\n*** NEXT KEY ***\n"); + } + + printf("\n"); + print_indent(); + printf("==== ptag new_format=%d content_tag=%d length_type=%d" + " length=0x%x (%d) position=0x%x (%d)\n",content->ptag.new_format, + content->ptag.content_tag,content->ptag.length_type, + content->ptag.length,content->ptag.length, + content->ptag.position,content->ptag.position); + print_tagname(ops_show_packet_tag(content->ptag.content_tag)); + break; + + case OPS_PTAG_CT_SE_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED DATA"); + break; + + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA HEADER"); + printf("Version: %d\n",content->se_ip_data_header.version); + break; + + case OPS_PTAG_CT_SE_IP_DATA_BODY: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA BODY"); + printf(" data body length=%d\n", + content->se_data_body.length); + printf(" data="); + hexdump(content->se_data_body.data, + content->se_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if (content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + print_tagname("PUBLIC KEY"); + else + print_tagname("PUBLIC SUBKEY"); + ops_print_public_key(&content->public_key); + break; + + case OPS_PTAG_CT_TRUST: + print_tagname("TRUST"); + print_data("Trust",&content->trust.data); + break; + + case OPS_PTAG_CT_USER_ID: + /* XXX: how do we print UTF-8? */ + print_tagname("USER ID"); + print_utf8_string("user_id",content->user_id.user_id); + break; + + case OPS_PTAG_CT_SIGNATURE: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if (content->signature.info.creation_time_set) + print_time("Signature Creation Time", + content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + print_unsigned_int("Hashed data len", content->signature.info.v4_hashed_data_length); + + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + default: + assert(0); + } + + if(content->signature.hash) + printf("data hash is set\n"); + + break; + + case OPS_PTAG_CT_COMPRESSED: + print_tagname("COMPRESSED"); + print_unsigned_int("Compressed Data Type", content->compressed.type); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + print_tagname("ONE PASS SIGNATURE"); + + print_unsigned_int("Version",content->one_pass_signature.version); + print_string_and_value("Signature Type", + ops_show_sig_type(content->one_pass_signature.sig_type), + content->one_pass_signature.sig_type); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->one_pass_signature.hash_algorithm), + content->one_pass_signature.hash_algorithm); + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->one_pass_signature.key_algorithm), + content->one_pass_signature.key_algorithm); + print_hexdump_data("Signer ID", + content->one_pass_signature.keyid, + sizeof content->one_pass_signature.keyid); + + print_unsigned_int("Nested", + content->one_pass_signature.nested); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + print_tagname("USER ATTRIBUTE"); + print_hexdump("User Attribute", + content->user_attribute.data.contents, + content->user_attribute.data.len); + break; + + case OPS_PTAG_RAW_SS: + assert(!content_->critical); + start_subpacket(content_->tag); + print_unsigned_int("Raw Signature Subpacket: tag", + content->ss_raw.tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + print_hexdump("Raw Data", + content->ss_raw.raw, + content->ss_raw.length); + break; + + case OPS_PTAG_SS_CREATION_TIME: + start_subpacket(content_->tag); + print_time("Signature Creation Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Signature Expiration Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Key Expiration Time", content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_TRUST: + start_subpacket(content_->tag); + print_string("Trust Signature",""); + print_unsigned_int("Level", + content->ss_trust.level); + print_unsigned_int("Amount", + content->ss_trust.amount); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCABLE: + start_subpacket(content_->tag); + print_boolean("Revocable",content->ss_revocable.revocable); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + start_subpacket(content_->tag); + /* not yet tested */ + printf (" revocation key: class=0x%x", + content->ss_revocation_key.clss); + if (content->ss_revocation_key.clss&0x40) + printf (" (sensitive)"); + printf (", algid=0x%x", content->ss_revocation_key.algid); + printf(", fingerprint="); + hexdump(content->ss_revocation_key.fingerprint,20); + printf("\n"); + end_subpacket(); + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + start_subpacket(content_->tag); + print_hexdump("Issuer Key Id", &content->ss_issuer_key_id.key_id[0], + sizeof content->ss_issuer_key_id.key_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + start_subpacket(content_->tag); + print_data( "Preferred Symmetric Algorithms", + &content->ss_preferred_ska.data); + + text = ops_showall_ss_preferred_ska(content->ss_preferred_ska); + print_text_breakdown(text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + start_subpacket(content_->tag); + print_boolean("Primary User ID", + content->ss_primary_user_id.primary_user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + start_subpacket(content_->tag); + print_data("Preferred Hash Algorithms", + &content->ss_preferred_hash.data); + + text = ops_showall_ss_preferred_hash(content->ss_preferred_hash); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + start_subpacket(content_->tag); + print_data( "Preferred Compression Algorithms", + &content->ss_preferred_compression.data); + + text = ops_showall_ss_preferred_compression(content->ss_preferred_compression); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + start_subpacket(content_->tag); + print_data( "Key Flags", &content->ss_key_flags.data); + + text = ops_showall_ss_key_flags(content->ss_key_flags); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + start_subpacket(content_->tag); + print_data( "Key Server Preferences", + &content->ss_key_server_prefs.data); + + text = ops_showall_ss_key_server_prefs(content->ss_key_server_prefs); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_FEATURES: + start_subpacket(content_->tag); + print_data( "Features", + &content->ss_features.data); + + text = ops_showall_ss_features(content->ss_features); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + start_subpacket(content_->tag); + print_indent(); + printf("Notation Data:\n"); + + indent++; + print_data( "Flags", + &content->ss_notation_data.flags); + text = ops_showall_ss_notation_data_flags(content->ss_notation_data); + print_text_breakdown( text); + ops_text_free(text); + + /* xxx - TODO: print out UTF - rachel */ + + print_data( "Name", + &content->ss_notation_data.name); + + print_data( "Value", + &content->ss_notation_data.value); + + indent--; + end_subpacket(); + break; + + case OPS_PTAG_SS_REGEXP: + start_subpacket(content_->tag); + print_hexdump("Regular Expression", + (unsigned char *)content->ss_regexp.text, + strlen(content->ss_regexp.text)); + print_string(NULL, + content->ss_regexp.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_POLICY_URI: + start_subpacket(content_->tag); + print_string("Policy URL", + content->ss_policy_url.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + start_subpacket(content_->tag); + print_utf8_string("Signer's User ID",content->ss_signers_user_id.user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + start_subpacket(content_->tag); + print_string("Preferred Key Server", + content->ss_preferred_key_server.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_EMBEDDED_SIGNATURE: + start_subpacket(content_->tag); + end_subpacket(content_->tag); // \todo print out contents? + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + start_subpacket(content_->tag); + print_hexdump("Internal or user-defined", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_RESERVED: + start_subpacket(content_->tag); + print_hexdump("Reserved", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + start_subpacket(content_->tag); + print_hexdump("Revocation Reason", + &content->ss_revocation_reason.code, + 1); + str=ops_show_ss_rr_code(content->ss_revocation_reason.code); + print_string(NULL,str); + /* xxx - todo : output text as UTF-8 string */ + end_subpacket(); + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + print_tagname("LITERAL DATA HEADER"); + printf(" literal data header format=%c filename='%s'\n", + content->literal_data_header.format, + content->literal_data_header.filename); + showtime(" modification time", + content->literal_data_header.modification_time); + printf("\n"); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + print_tagname("LITERAL DATA BODY"); + printf(" literal data body length=%d\n", + content->literal_data_body.length); + printf(" data="); + print_escaped(content->literal_data_body.data, + content->literal_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_SIGNATURE_HEADER: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if(content->signature.info.creation_time_set) + print_time("Signature Creation Time", content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + break; + + case OPS_PTAG_CT_SIGNATURE_FOOTER: + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + print_data("Private/Experimental", + &content->signature.info.signature.unknown.data); + break; + + default: + assert(0); + } + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + print_tagname("OPS_PARSER_CMD_GET_SK_PASSPHRASE"); + /* + if(passphrase_prompt) + { + print_secret_key(OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, + content->secret_key_passphrase.secret_key); + *content->secret_key_passphrase.passphrase=ops_get_passphrase(); + if(!**content->secret_key_passphrase.passphrase) + break; + return OPS_KEEP_MEMORY; + } + else + printf(">>> ASKED FOR PASSPHRASE <<<\n"); + */ + + break; + + case OPS_PTAG_CT_SECRET_KEY: + print_tagname("OPS_PTAG_CT_SECRET_KEY"); + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + // print_secret_key(content_->tag,&content->secret_key); + print_tagname("OPS_PTAG_CT_ENCRYPTED_SECRET_KEY"); + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + print_tagname("ARMOUR HEADER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + print_tagname("SIGNED CLEARTEXT HEADER"); + print_headers(&content->signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + print_tagname("SIGNED CLEARTEXT BODY"); + print_block("signed cleartext",content->signed_cleartext_body.data, + content->signed_cleartext_body.length); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + print_tagname("SIGNED CLEARTEXT TRAILER"); + printf("hash algorithm: %d\n", + content->signed_cleartext_trailer.hash->algorithm); + printf("\n"); + break; + + case OPS_PTAG_CT_UNARMOURED_TEXT: + if(!unarmoured) + { + print_tagname("UNARMOURED TEXT"); + unarmoured=ops_true; + } + putchar('['); + print_escaped(content->unarmoured_text.data, + content->unarmoured_text.length); + putchar(']'); + break; + + case OPS_PTAG_CT_ARMOUR_TRAILER: + print_tagname("ARMOUR TRAILER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_print_pk_session_key(content_->tag,&content->pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + ops_print_pk_session_key(OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, + content->get_secret_key.pk_session_key); + break; + + default: + print_tagname("UNKNOWN PACKET TYPE"); + fprintf(stderr,"ops_print_packet: unknown tag=%d (0x%x)\n",content_->tag, + content_->tag); + exit(1); + } + return 1; + } + +static ops_parse_cb_return_t cb_list_packets(const ops_parser_content_t * content_, ops_parse_cb_info_t *cbinfo) + { + OPS_USED(cbinfo); + + ops_print_packet(content_); +#ifdef XXX + if(unarmoured && content_->tag != OPS_PTAG_CT_UNARMOURED_TEXT) + { + unarmoured=ops_false; + puts("UNARMOURED TEXT ends"); + } + + switch(content_->tag) + { + case OPS_PARSER_ERROR: + printf("parse error: %s\n",content->error.error); + break; + + case OPS_PARSER_ERRCODE: + printf("parse error: %s\n", + ops_errcode(content->errcode.errcode)); + break; + + case OPS_PARSER_PACKET_END: + print_packet_hex(&content->packet); + break; + + case OPS_PARSER_PTAG: + if(content->ptag.content_tag == OPS_PTAG_CT_PUBLIC_KEY) + { + indent=0; + printf("\n*** NEXT KEY ***\n"); + } + + printf("\n"); + print_indent(); + printf("==== ptag new_format=%d content_tag=%d length_type=%d" + " length=0x%x (%d) position=0x%x (%d)\n",content->ptag.new_format, + content->ptag.content_tag,content->ptag.length_type, + content->ptag.length,content->ptag.length, + content->ptag.position,content->ptag.position); + print_tagname(ops_show_packet_tag(content->ptag.content_tag)); + break; + + case OPS_PTAG_CT_SE_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED DATA"); + break; + + case OPS_PTAG_CT_SE_IP_DATA_HEADER: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA HEADER"); + printf("Version: %d\n",content->se_ip_data_header.version); + break; + + case OPS_PTAG_CT_SE_IP_DATA_BODY: + print_tagname("SYMMETRIC ENCRYPTED INTEGRITY PROTECTED DATA BODY"); + printf(" data body length=%d\n", + content->se_data_body.length); + printf(" data="); + hexdump(content->se_data_body.data, + content->se_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_PUBLIC_KEY: + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if (content_->tag == OPS_PTAG_CT_PUBLIC_KEY) + print_tagname("PUBLIC KEY"); + else + print_tagname("PUBLIC SUBKEY"); + + ops_print_public_key(&content->public_key); + break; + + case OPS_PTAG_CT_TRUST: + print_tagname("TRUST"); + print_data("Trust",&content->trust.data); + break; + + case OPS_PTAG_CT_USER_ID: + /* XXX: how do we print UTF-8? */ + print_tagname("USER ID"); + print_utf8_string("user_id",content->user_id.user_id); + break; + + case OPS_PTAG_CT_SIGNATURE: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if (content->signature.info.creation_time_set) + print_time("Signature Creation Time", + content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + print_unsigned_int("Hashed data len", content->signature.info.v4_hashed_data_length); + + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_SIGN_ONLY: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + default: + assert(0); + } + + if(content->signature.hash) + printf("data hash is set\n"); + + break; + + case OPS_PTAG_CT_COMPRESSED: + print_tagname("COMPRESSED"); + print_unsigned_int("Compressed Data Type", content->compressed.type); + break; + + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + print_tagname("ONE PASS SIGNATURE"); + + print_unsigned_int("Version",content->one_pass_signature.version); + print_string_and_value("Signature Type", + ops_show_sig_type(content->one_pass_signature.sig_type), + content->one_pass_signature.sig_type); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->one_pass_signature.hash_algorithm), + content->one_pass_signature.hash_algorithm); + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->one_pass_signature.key_algorithm), + content->one_pass_signature.key_algorithm); + print_hexdump_data("Signer ID", + content->one_pass_signature.keyid, + sizeof content->one_pass_signature.keyid); + + print_unsigned_int("Nested", + content->one_pass_signature.nested); + break; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + print_tagname("USER ATTRIBUTE"); + print_hexdump("User Attribute", + content->user_attribute.data.contents, + content->user_attribute.data.len); + break; + + case OPS_PTAG_RAW_SS: + assert(!content_->critical); + start_subpacket(content_->tag); + print_unsigned_int("Raw Signature Subpacket: tag", + content->ss_raw.tag-OPS_PTAG_SIGNATURE_SUBPACKET_BASE); + print_hexdump("Raw Data", + content->ss_raw.raw, + content->ss_raw.length); + break; + + case OPS_PTAG_SS_CREATION_TIME: + start_subpacket(content_->tag); + print_time("Signature Creation Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Signature Expiration Time",content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_EXPIRATION_TIME: + start_subpacket(content_->tag); + print_duration("Key Expiration Time", content->ss_time.time); + end_subpacket(); + break; + + case OPS_PTAG_SS_TRUST: + start_subpacket(content_->tag); + print_string("Trust Signature",""); + print_unsigned_int("Level", + content->ss_trust.level); + print_unsigned_int("Amount", + content->ss_trust.amount); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCABLE: + start_subpacket(content_->tag); + print_boolean("Revocable",content->ss_revocable.revocable); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_KEY: + start_subpacket(content_->tag); + /* not yet tested */ + printf (" revocation key: class=0x%x", + content->ss_revocation_key.clss); + if (content->ss_revocation_key.clss&0x40) + printf (" (sensitive)"); + printf (", algid=0x%x", content->ss_revocation_key.algid); + printf(", fingerprint="); + hexdump(content->ss_revocation_key.fingerprint,20); + printf("\n"); + end_subpacket(); + break; + + case OPS_PTAG_SS_ISSUER_KEY_ID: + start_subpacket(content_->tag); + print_hexdump("Issuer Key Id", + &content->ss_issuer_key_id.key_id[0], + sizeof content->ss_issuer_key_id.key_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_SKA: + start_subpacket(content_->tag); + print_data( "Preferred Symmetric Algorithms", + &content->ss_preferred_ska.data); + + text = ops_showall_ss_preferred_ska(content->ss_preferred_ska); + print_text_breakdown(text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_PRIMARY_USER_ID: + start_subpacket(content_->tag); + print_boolean("Primary User ID", + content->ss_primary_user_id.primary_user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_HASH: + start_subpacket(content_->tag); + print_data("Preferred Hash Algorithms", + &content->ss_preferred_hash.data); + + text = ops_showall_ss_preferred_hash(content->ss_preferred_hash); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_COMPRESSION: + start_subpacket(content_->tag); + print_data( "Preferred Compression Algorithms", + &content->ss_preferred_compression.data); + + text = ops_showall_ss_preferred_compression(content->ss_preferred_compression); + print_text_breakdown(text); + ops_text_free(text); + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_FLAGS: + start_subpacket(content_->tag); + print_data( "Key Flags", &content->ss_key_flags.data); + + text = ops_showall_ss_key_flags(content->ss_key_flags); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_KEY_SERVER_PREFS: + start_subpacket(content_->tag); + print_data( "Key Server Preferences", + &content->ss_key_server_prefs.data); + + text = ops_showall_ss_key_server_prefs(content->ss_key_server_prefs); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_FEATURES: + start_subpacket(content_->tag); + print_data( "Features", + &content->ss_features.data); + + text = ops_showall_ss_features(content->ss_features); + print_text_breakdown( text); + ops_text_free(text); + + end_subpacket(); + break; + + case OPS_PTAG_SS_NOTATION_DATA: + start_subpacket(content_->tag); + print_indent(); + printf("Notation Data:\n"); + + indent++; + print_data( "Flags", + &content->ss_notation_data.flags); + text = ops_showall_ss_notation_data_flags(content->ss_notation_data); + print_text_breakdown( text); + ops_text_free(text); + + /* xxx - TODO: print out UTF - rachel */ + + print_data( "Name", + &content->ss_notation_data.name); + + print_data( "Value", + &content->ss_notation_data.value); + + indent--; + end_subpacket(); + break; + + case OPS_PTAG_SS_REGEXP: + start_subpacket(content_->tag); + print_hexdump("Regular Expression", + (unsigned char *)content->ss_regexp.text, + strlen(content->ss_regexp.text)); + print_string(NULL, + content->ss_regexp.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_POLICY_URL: + start_subpacket(content_->tag); + print_string("Policy URL", + content->ss_policy_url.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_SIGNERS_USER_ID: + start_subpacket(content_->tag); + print_utf8_string("Signer's User ID",content->ss_signers_user_id.user_id); + end_subpacket(); + break; + + case OPS_PTAG_SS_PREFERRED_KEY_SERVER: + start_subpacket(content_->tag); + print_string("Preferred Key Server", + content->ss_preferred_key_server.text); + end_subpacket(); + break; + + case OPS_PTAG_SS_USERDEFINED00: + case OPS_PTAG_SS_USERDEFINED01: + case OPS_PTAG_SS_USERDEFINED02: + case OPS_PTAG_SS_USERDEFINED03: + case OPS_PTAG_SS_USERDEFINED04: + case OPS_PTAG_SS_USERDEFINED05: + case OPS_PTAG_SS_USERDEFINED06: + case OPS_PTAG_SS_USERDEFINED07: + case OPS_PTAG_SS_USERDEFINED08: + case OPS_PTAG_SS_USERDEFINED09: + case OPS_PTAG_SS_USERDEFINED10: + start_subpacket(content_->tag); + print_hexdump("Internal or user-defined", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_RESERVED: + start_subpacket(content_->tag); + print_hexdump("Reserved", + content->ss_userdefined.data.contents, + content->ss_userdefined.data.len); + end_subpacket(); + break; + + case OPS_PTAG_SS_REVOCATION_REASON: + start_subpacket(content_->tag); + print_hexdump("Revocation Reason", + &content->ss_revocation_reason.code, + 1); + str=ops_show_ss_rr_code(content->ss_revocation_reason.code); + print_string(NULL,str); + /* xxx - todo : output text as UTF-8 string */ + end_subpacket(); + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + print_tagname("LITERAL DATA HEADER"); + printf(" literal data header format=%c filename='%s'\n", + content->literal_data_header.format, + content->literal_data_header.filename); + print_time(" modification time", + content->literal_data_header.modification_time); + printf("\n"); + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + print_tagname("LITERAL DATA BODY"); + printf(" literal data body length=%d\n", + content->literal_data_body.length); + printf(" data="); + print_escaped(content->literal_data_body.data, + content->literal_data_body.length); + printf("\n"); + break; + + case OPS_PTAG_CT_SIGNATURE_HEADER: + print_tagname("SIGNATURE"); + print_indent(); + print_unsigned_int("Signature Version", + content->signature.info.version); + if(content->signature.info.creation_time_set) + print_time("Signature Creation Time", content->signature.info.creation_time); + + print_string_and_value("Signature Type", + ops_show_sig_type(content->signature.info.type), + content->signature.info.type); + + if(content->signature.info.signer_id_set) + print_hexdump_data("Signer ID", + content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + + print_string_and_value("Public Key Algorithm", + ops_show_pka(content->signature.info.key_algorithm), + content->signature.info.key_algorithm); + print_string_and_value("Hash Algorithm", + ops_show_hash_algorithm(content->signature.info.hash_algorithm), + content->signature.info.hash_algorithm); + + break; + + case OPS_PTAG_CT_SIGNATURE_FOOTER: + print_indent(); + print_hexdump_data("hash2",&content->signature.hash2[0],2); + + switch(content->signature.info.key_algorithm) + { + case OPS_PKA_RSA: + print_bn("sig",content->signature.info.signature.rsa.sig); + break; + + case OPS_PKA_DSA: + print_bn("r",content->signature.info.signature.dsa.r); + print_bn("s",content->signature.info.signature.dsa.s); + break; + + case OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + print_bn("r",content->signature.info.signature.elgamal.r); + print_bn("s",content->signature.info.signature.elgamal.s); + break; + + case OPS_PKA_PRIVATE00: + case OPS_PKA_PRIVATE01: + case OPS_PKA_PRIVATE02: + case OPS_PKA_PRIVATE03: + case OPS_PKA_PRIVATE04: + case OPS_PKA_PRIVATE05: + case OPS_PKA_PRIVATE06: + case OPS_PKA_PRIVATE07: + case OPS_PKA_PRIVATE08: + case OPS_PKA_PRIVATE09: + case OPS_PKA_PRIVATE10: + print_data("Private/Experimental", + &content->signature.info.signature.unknown.data); + break; + + default: + assert(0); + } + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: +#ifdef XXX + if(passphrase_prompt) + { + ops_print_secret_key(OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, + content->secret_key_passphrase.secret_key); + *content->secret_key_passphrase.passphrase=ops_get_passphrase(); + if(!**content->secret_key_passphrase.passphrase) + break; + return OPS_KEEP_MEMORY; + } + else + printf(">>> ASKED FOR PASSPHRASE <<<\n"); +#else + if (cbinfo->cryptinfo.cb_get_passphrase) + return cbinfo->cryptinfo.cb_get_passphrase(content_,cbinfo); +#endif /*XXX*/ + break; + + case OPS_PTAG_CT_SECRET_KEY: + case OPS_PTAG_CT_ENCRYPTED_SECRET_KEY: + ops_print_secret_key_verbose(content_->tag,&content->secret_key); + break; + + case OPS_PTAG_CT_ARMOUR_HEADER: + print_tagname("ARMOUR HEADER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + print_tagname("SIGNED CLEARTEXT HEADER"); + print_headers(&content->signed_cleartext_header.headers); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + print_tagname("SIGNED CLEARTEXT BODY"); + print_block("signed cleartext",content->signed_cleartext_body.data, + content->signed_cleartext_body.length); + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + print_tagname("SIGNED CLEARTEXT TRAILER"); + printf("hash algorithm: %d\n", + content->signed_cleartext_trailer.hash->algorithm); + printf("\n"); + break; + + case OPS_PTAG_CT_UNARMOURED_TEXT: + if(!unarmoured) + { + print_tagname("UNARMOURED TEXT"); + unarmoured=ops_true; + } + putchar('['); + print_escaped(content->unarmoured_text.data, + content->unarmoured_text.length); + putchar(']'); + break; + + case OPS_PTAG_CT_ARMOUR_TRAILER: + print_tagname("ARMOUR TRAILER"); + print_string("type",content->armour_header.type); + break; + + case OPS_PTAG_CT_PK_SESSION_KEY: + case OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY: + ops_print_pk_session_key(content_->tag,&content->pk_session_key); + break; + + case OPS_PARSER_CMD_GET_SECRET_KEY: + ops_print_pk_session_key(OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, + content->get_secret_key.pk_session_key); + +#ifdef XXX + decrypter=ops_keyring_find_key_by_id(&keyring, + content->get_secret_key.pk_session_key->key_id); + if(!decrypter || !ops_key_is_secret(decrypter)) + break; + + puts("[Decryption key found in keyring]"); + + secret=ops_get_secret_key_from_data(decrypter); + while(!secret) + { + /* then it must be encrypted */ + char *phrase=ops_get_passphrase(); + secret=ops_decrypt_secret_key_from_data(decrypter,phrase); + free(phrase); + } + + *content->get_secret_key.secret_key=secret; +#else + return callback_cmd_get_secret_key(content_,cbinfo); +#endif /*XXX*/ + break; + + default: + print_tagname("UNKNOWN PACKET TYPE"); + fprintf(stderr,"packet-dump: unknown tag=%d (0x%x)\n",content_->tag, + content_->tag); + exit(1); + } +#endif /*XXX*/ + return OPS_RELEASE_MEMORY; + } + +/** +\ingroup Core_Print +\param filename +\param armour +\param keyring +\param cb_get_passphrase +*/ +void ops_list_packets(char* filename, ops_boolean_t armour, ops_keyring_t* keyring, ops_parse_cb_t* cb_get_passphrase) + { + int fd=0; + ops_parse_info_t *pinfo=NULL; + const ops_boolean_t accumulate=ops_true; + + fd=ops_setup_file_read(&pinfo, filename, NULL, cb_list_packets, accumulate); + ops_parse_options(pinfo,OPS_PTAG_SS_ALL,OPS_PARSE_PARSED); + pinfo->cryptinfo.keyring=keyring; + pinfo->cryptinfo.cb_get_passphrase=cb_get_passphrase; + + if(armour) + ops_reader_push_dearmour(pinfo); + + ops_parse_and_print_errors(pinfo); + + ops_teardown_file_read(pinfo,fd); + } diff --git a/openpgpsdk/src/openpgpsdk/packet-show-cast.h b/openpgpsdk/src/openpgpsdk/packet-show-cast.h new file mode 100644 index 000000000..b5c8b0ab4 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-show-cast.h @@ -0,0 +1,38 @@ +/* Generated from packet-show.cast by ../../util/caster.pl, do not edit. */ + +#include "types.h" + +/* (line 4) char *show_packet_tag(ops_packet_tag_t packet_tag, packet_tag_map_t *packet_tag_map) -> char *ops_str_from_map(int packet_tag, ops_map_t *packet_tag_map) */ +char *ops_str_from_map(int packet_tag, ops_map_t *packet_tag_map); +#define show_packet_tag(packet_tag,packet_tag_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_packet_tag_t , packet_tag),CHECKED_INSTANCE_OF( packet_tag_map_t *, packet_tag_map)) +typedef char * show_packet_tag_t(ops_packet_tag_t , packet_tag_map_t *); + +/* (line 5) char *show_sig_type(ops_sig_type_t sig_type, sig_type_map_t *sig_type_map) -> char *ops_str_from_map(int sig_type, ops_map_t *sig_type_map) */ +char *ops_str_from_map(int sig_type, ops_map_t *sig_type_map); +#define show_sig_type(sig_type,sig_type_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_sig_type_t , sig_type),CHECKED_INSTANCE_OF( sig_type_map_t *, sig_type_map)) +typedef char * show_sig_type_t(ops_sig_type_t , sig_type_map_t *); + +/* (line 6) char *show_pka(ops_public_key_algorithm_t pka, public_key_algorithm_map_t *pka_map) -> char *ops_str_from_map(int pka, ops_map_t *pka_map) */ +char *ops_str_from_map(int pka, ops_map_t *pka_map); +#define show_pka(pka,pka_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_public_key_algorithm_t , pka),CHECKED_INSTANCE_OF( public_key_algorithm_map_t *, pka_map)) +typedef char * show_pka_t(ops_public_key_algorithm_t , public_key_algorithm_map_t *); + +/* (line 7) char *show_ss_type(ops_ss_type_t ss_type, ss_type_map_t *ss_type_map) -> char *ops_str_from_map(int ss_type, ops_map_t *ss_type_map) */ +char *ops_str_from_map(int ss_type, ops_map_t *ss_type_map); +#define show_ss_type(ss_type,ss_type_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_ss_type_t , ss_type),CHECKED_INSTANCE_OF( ss_type_map_t *, ss_type_map)) +typedef char * show_ss_type_t(ops_ss_type_t , ss_type_map_t *); + +/* (line 8) char *show_ss_rr_code(ops_ss_rr_code_t ss_rr_code, ss_rr_code_map_t *ss_rr_code_map) -> char *ops_str_from_map(int ss_rr_code, ops_map_t *ss_rr_code_map) */ +char *ops_str_from_map(int ss_rr_code, ops_map_t *ss_rr_code_map); +#define show_ss_rr_code(ss_rr_code,ss_rr_code_map) ops_str_from_map(CHECKED_INSTANCE_OF(ops_ss_rr_code_t , ss_rr_code),CHECKED_INSTANCE_OF( ss_rr_code_map_t *, ss_rr_code_map)) +typedef char * show_ss_rr_code_t(ops_ss_rr_code_t , ss_rr_code_map_t *); + +/* (line 9) char *show_hash_algorithm(unsigned char hash,+ops_map_t *hash_algorithm_map) -> char *ops_str_from_map(int hash,ops_map_t *hash_algorithm_map) */ +char *ops_str_from_map(int hash,ops_map_t *hash_algorithm_map); +#define show_hash_algorithm(hash) ops_str_from_map(CHECKED_INSTANCE_OF(unsigned char , hash),CHECKED_INSTANCE_OF(ops_map_t *, hash_algorithm_map)) +typedef char * show_hash_algorithm_t(unsigned char ); + +/* (line 10) char *show_symmetric_algorithm(unsigned char hash,+ops_map_t *symmetric_algorithm_map) -> char *ops_str_from_map(int hash,ops_map_t *symmetric_algorithm_map) */ +char *ops_str_from_map(int hash,ops_map_t *symmetric_algorithm_map); +#define show_symmetric_algorithm(hash) ops_str_from_map(CHECKED_INSTANCE_OF(unsigned char , hash),CHECKED_INSTANCE_OF(ops_map_t *, symmetric_algorithm_map)) +typedef char * show_symmetric_algorithm_t(unsigned char ); diff --git a/openpgpsdk/src/openpgpsdk/packet-show.c b/openpgpsdk/src/openpgpsdk/packet-show.c new file mode 100644 index 000000000..8be6c7563 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-show.c @@ -0,0 +1,857 @@ +/* + * 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 + +#include +#include + +#include +#include + +#include + +/* + * Arrays of value->text maps + */ + +static ops_map_t packet_tag_map[] = + { + { OPS_PTAG_CT_RESERVED, "Reserved" }, + { OPS_PTAG_CT_PK_SESSION_KEY, "Public-Key Encrypted Session Key" }, + { OPS_PTAG_CT_SIGNATURE, "Signature" }, + { OPS_PTAG_CT_SK_SESSION_KEY, "Symmetric-Key Encrypted Session Key" }, + { OPS_PTAG_CT_ONE_PASS_SIGNATURE, "One-Pass Signature" }, + { OPS_PTAG_CT_SECRET_KEY, "Secret Key" }, + { OPS_PTAG_CT_PUBLIC_KEY, "Public Key" }, + { OPS_PTAG_CT_SECRET_SUBKEY, "Secret Subkey" }, + { OPS_PTAG_CT_COMPRESSED, "Compressed Data" }, + { OPS_PTAG_CT_SE_DATA, "Symmetrically Encrypted Data" }, + { OPS_PTAG_CT_MARKER, "Marker" }, + { OPS_PTAG_CT_LITERAL_DATA, "Literal Data" }, + { OPS_PTAG_CT_TRUST, "Trust" }, + { OPS_PTAG_CT_USER_ID, "User ID" }, + { OPS_PTAG_CT_PUBLIC_SUBKEY, "Public Subkey" }, + { OPS_PTAG_CT_RESERVED2, "reserved" }, + { OPS_PTAG_CT_RESERVED3, "reserved" }, + { OPS_PTAG_CT_USER_ATTRIBUTE, "User Attribute" }, + { OPS_PTAG_CT_SE_IP_DATA, "Sym. Encrypted and Integrity Protected Data" }, + { OPS_PTAG_CT_MDC, "Modification Detection Code" }, + { OPS_PARSER_PTAG, "OPS_PARSER_PTAG" }, + { OPS_PTAG_RAW_SS, "OPS_PTAG_RAW_SS" }, + { OPS_PTAG_SS_ALL, "OPS_PTAG_SS_ALL" }, + { OPS_PARSER_PACKET_END, "OPS_PARSER_PACKET_END" }, + { OPS_PTAG_SIGNATURE_SUBPACKET_BASE, "OPS_PTAG_SIGNATURE_SUBPACKET_BASE" }, + + { OPS_PTAG_SS_CREATION_TIME, "SS: Signature Creation Time" }, + { OPS_PTAG_SS_EXPIRATION_TIME, "SS: Signature Expiration Time" }, + { OPS_PTAG_SS_EXPORTABLE_CERTIFICATION, "SS: Exportable Certification" }, + { OPS_PTAG_SS_TRUST, "SS: Trust Signature" }, + { OPS_PTAG_SS_REGEXP, "SS: Regular Expression" }, + { OPS_PTAG_SS_REVOCABLE, "SS: Revocable" }, + { OPS_PTAG_SS_KEY_EXPIRATION_TIME, "SS: Key Expiration Time" }, + { OPS_PTAG_SS_RESERVED, "SS: Reserved" }, + { OPS_PTAG_SS_PREFERRED_SKA, "SS: Preferred Secret Key Algorithm" }, + { OPS_PTAG_SS_REVOCATION_KEY, "SS: Revocation Key" }, + { OPS_PTAG_SS_ISSUER_KEY_ID, "SS: Issuer Key Id" }, + { OPS_PTAG_SS_NOTATION_DATA, "SS: Notation Data" }, + { OPS_PTAG_SS_PREFERRED_HASH, "SS: Preferred Hash Algorithm" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"SS: Preferred Compression Algorithm" }, + { OPS_PTAG_SS_KEY_SERVER_PREFS, "SS: Key Server Preferences" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"SS: Preferred Key Server" }, + { OPS_PTAG_SS_PRIMARY_USER_ID, "SS: Primary User ID" }, + { OPS_PTAG_SS_POLICY_URI, "SS: Policy URI" }, + { OPS_PTAG_SS_KEY_FLAGS, "SS: Key Flags" }, + { OPS_PTAG_SS_SIGNERS_USER_ID, "SS: Signer's User ID" }, + { OPS_PTAG_SS_REVOCATION_REASON, "SS: Reason for Revocation" }, + { OPS_PTAG_SS_FEATURES, "SS: Features" }, + { OPS_PTAG_SS_SIGNATURE_TARGET, "SS: Signature Target" }, + { OPS_PTAG_SS_EMBEDDED_SIGNATURE, "SS: Embedded Signature" }, + + { OPS_PTAG_CT_LITERAL_DATA_HEADER, "CT: Literal Data Header" }, + { OPS_PTAG_CT_LITERAL_DATA_BODY, "CT: Literal Data Body" }, + { OPS_PTAG_CT_SIGNATURE_HEADER, "CT: Signature Header" }, + { OPS_PTAG_CT_SIGNATURE_FOOTER, "CT: Signature Footer" }, + { OPS_PTAG_CT_ARMOUR_HEADER, "CT: Armour Header" }, + { OPS_PTAG_CT_ARMOUR_TRAILER, "CT: Armour Trailer" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER, "CT: Signed Cleartext Header" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY, "CT: Signed Cleartext Body" }, + { OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, "CT: Signed Cleartext Trailer" }, + { OPS_PTAG_CT_UNARMOURED_TEXT, "CT: Unarmoured Text" }, + { OPS_PTAG_CT_ENCRYPTED_SECRET_KEY, "CT: Encrypted Secret Key" }, + { OPS_PTAG_CT_SE_DATA_HEADER, "CT: Sym Encrypted Data Header" }, + { OPS_PTAG_CT_SE_DATA_BODY, "CT: Sym Encrypted Data Body" }, + { OPS_PTAG_CT_SE_IP_DATA_HEADER, "CT: Sym Encrypted IP Data Header" }, + { OPS_PTAG_CT_SE_IP_DATA_BODY, "CT: Sym Encrypted IP Data Body" }, + { OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, "CT: Encrypted PK Session Key" }, + { OPS_PARSER_CMD_GET_SK_PASSPHRASE, "CMD: Get Secret Key Passphrase" }, + { OPS_PARSER_CMD_GET_SECRET_KEY, "CMD: Get Secret Key" }, + { OPS_PARSER_ERROR, "OPS_PARSER_ERROR" }, + { OPS_PARSER_ERRCODE, "OPS_PARSER_ERRCODE" }, + + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t packet_tag_map_t; + +static ops_map_t ss_type_map[] = + { + { OPS_PTAG_SS_CREATION_TIME, "Signature Creation Time" }, + { OPS_PTAG_SS_EXPIRATION_TIME, "Signature Expiration Time" }, + { OPS_PTAG_SS_TRUST, "Trust Signature" }, + { OPS_PTAG_SS_REGEXP, "Regular Expression" }, + { OPS_PTAG_SS_REVOCABLE, "Revocable" }, + { OPS_PTAG_SS_KEY_EXPIRATION_TIME, "Key Expiration Time" }, + { OPS_PTAG_SS_PREFERRED_SKA, "Preferred Symmetric Algorithms" }, + { OPS_PTAG_SS_REVOCATION_KEY, "Revocation Key" }, + { OPS_PTAG_SS_ISSUER_KEY_ID, "Issuer key ID" }, + { OPS_PTAG_SS_NOTATION_DATA, "Notation Data" }, + { OPS_PTAG_SS_PREFERRED_HASH, "Preferred Hash Algorithms" }, + { OPS_PTAG_SS_PREFERRED_COMPRESSION,"Preferred Compression Algorithms" }, + { OPS_PTAG_SS_KEY_SERVER_PREFS, "Key Server Preferences" }, + { OPS_PTAG_SS_PREFERRED_KEY_SERVER, "Preferred Key Server" }, + { OPS_PTAG_SS_PRIMARY_USER_ID, "Primary User ID" }, + { OPS_PTAG_SS_POLICY_URI, "Policy URI" }, + { OPS_PTAG_SS_KEY_FLAGS, "Key Flags" }, + { OPS_PTAG_SS_REVOCATION_REASON, "Reason for Revocation" }, + { OPS_PTAG_SS_FEATURES, "Features" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t ss_type_map_t; + + +static ops_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 */ + }; +typedef ops_map_t ss_rr_code_map_t; + +static ops_map_t sig_type_map[] = + { + { OPS_SIG_BINARY, "Signature of a binary document" }, + { OPS_SIG_TEXT, "Signature of a canonical text document" }, + { OPS_SIG_STANDALONE, "Standalone signature" }, + { OPS_CERT_GENERIC, "Generic certification of a User ID and Public Key packet" }, + { OPS_CERT_PERSONA, "Persona certification of a User ID and Public Key packet" }, + { OPS_CERT_CASUAL, "Casual certification of a User ID and Public Key packet" }, + { OPS_CERT_POSITIVE, "Positive certification of a User ID and Public Key packet" }, + { OPS_SIG_SUBKEY, "Subkey Binding Signature" }, + { OPS_SIG_PRIMARY, "Primary Key Binding Signature" }, + { OPS_SIG_DIRECT, "Signature directly on a key" }, + { OPS_SIG_REV_KEY, "Key revocation signature" }, + { OPS_SIG_REV_SUBKEY, "Subkey revocation signature" }, + { OPS_SIG_REV_CERT, "Certification revocation signature" }, + { OPS_SIG_TIMESTAMP, "Timestamp signature" }, + { OPS_SIG_3RD_PARTY, "Third-Party Confirmation signature" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t sig_type_map_t; + +static ops_map_t public_key_algorithm_map[] = + { + { OPS_PKA_RSA, "RSA (Encrypt or Sign)" }, + { OPS_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only" }, + { OPS_PKA_RSA_SIGN_ONLY, "RSA Sign-Only" }, + { OPS_PKA_ELGAMAL, "Elgamal (Encrypt-Only)" }, + { OPS_PKA_DSA, "DSA" }, + { OPS_PKA_RESERVED_ELLIPTIC_CURVE, "Reserved for Elliptic Curve" }, + { OPS_PKA_RESERVED_ECDSA, "Reserved for ECDSA" }, + { OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Reserved (formerly Elgamal Encrypt or Sign" }, + { OPS_PKA_RESERVED_DH, "Reserved for Diffie-Hellman (X9.42)" }, + { OPS_PKA_PRIVATE00, "Private/Experimental" }, + { OPS_PKA_PRIVATE01, "Private/Experimental" }, + { OPS_PKA_PRIVATE02, "Private/Experimental" }, + { OPS_PKA_PRIVATE03, "Private/Experimental" }, + { OPS_PKA_PRIVATE04, "Private/Experimental" }, + { OPS_PKA_PRIVATE05, "Private/Experimental" }, + { OPS_PKA_PRIVATE06, "Private/Experimental" }, + { OPS_PKA_PRIVATE07, "Private/Experimental" }, + { OPS_PKA_PRIVATE08, "Private/Experimental" }, + { OPS_PKA_PRIVATE09, "Private/Experimental" }, + { OPS_PKA_PRIVATE10, "Private/Experimental" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; +typedef ops_map_t public_key_algorithm_map_t; + +static ops_map_t symmetric_algorithm_map[] = + { + { OPS_SA_PLAINTEXT, "Plaintext or unencrypted data" }, + { OPS_SA_IDEA, "IDEA" }, + { OPS_SA_TRIPLEDES, "TripleDES" }, + { OPS_SA_CAST5, "CAST5" }, + { OPS_SA_BLOWFISH, "Blowfish" }, + { OPS_SA_AES_128, "AES (128-bit key)" }, + { OPS_SA_AES_192, "AES (192-bit key)" }, + { OPS_SA_AES_256, "AES (256-bit key)" }, + { OPS_SA_TWOFISH, "Twofish(256-bit key)" }, + { OPS_SA_CAMELLIA_128, "Camellia (128-bit key)" }, + { OPS_SA_CAMELLIA_192, "Camellia (192-bit key)" }, + { OPS_SA_CAMELLIA_256, "Camellia (256-bit key)" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_map_t hash_algorithm_map[] = + { + { OPS_HASH_MD5, "MD5" }, + { OPS_HASH_SHA1, "SHA1" }, + { OPS_HASH_RIPEMD, "RIPEMD160" }, + { OPS_HASH_SHA256, "SHA256" }, + { OPS_HASH_SHA384, "SHA384" }, + { OPS_HASH_SHA512, "SHA512" }, + { OPS_HASH_SHA224, "SHA224" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_map_t compression_algorithm_map[] = + { + { OPS_C_NONE, "Uncompressed" }, + { OPS_C_ZIP, "ZIP(RFC1951)" }, + { OPS_C_ZLIB, "ZLIB(RFC1950)" }, + { OPS_C_BZIP2, "Bzip2(BZ2)" }, + { 0x00, NULL }, /* this is the end-of-array marker */ + }; + +static ops_bit_map_t ss_notation_data_map_byte0[] = + { + { 0x80, "Human-readable" }, + { 0x00, NULL }, + }; + +static ops_bit_map_t *ss_notation_data_map[] = + { + ss_notation_data_map_byte0, + }; + +static ops_bit_map_t ss_feature_map_byte0[] = + { + { 0x01, "Modification Detection" }, + { 0x00, NULL }, + }; + +static ops_bit_map_t *ss_feature_map[] = + { + ss_feature_map_byte0, + }; + +static ops_bit_map_t ss_key_flags_map[] = + { + { 0x01, "May be used to certify other keys" }, + { 0x02, "May be used to sign data" }, + { 0x04, "May be used to encrypt communications" }, + { 0x08, "May be used to encrypt storage" }, + { 0x10, "Private component may have been split by a secret-sharing mechanism"}, + { 0x80, "Private component may be in possession of more than one person"}, + { 0x00, NULL }, + }; + +static ops_bit_map_t ss_key_server_prefs_map[] = + { + { 0x80, "Key holder requests that this key only be modified or updated by the key holder or an administrator of the key server" }, + { 0x00, NULL }, + }; + +#include + +/* + * Private functions + */ + +static void list_init(ops_list_t *list) + { + list->size=0; + list->used=0; + list->strings=NULL; + } + +static void list_free_strings(ops_list_t *list) + { + unsigned i; + + for(i=0; i < list->used ; i++) + { + free(list->strings[i]); + list->strings[i]=NULL; + } + } + +static void list_free(ops_list_t *list) + { + if (list->strings) + free(list->strings); + list_init(list); + } + +static unsigned int list_resize(ops_list_t *list) + { + /* We only resize in one direction - upwards. + Algorithm used : double the current size then add 1 + */ + + int newsize=0; + + newsize=list->size*2 + 1; + list->strings=realloc(list->strings,newsize*sizeof(char *)); + if (list->strings) + { + list->size=newsize; + return 1; + } + else + { + /* xxx - realloc failed. error message? - rachel */ + return 0; + } + } + +static unsigned int add_str(ops_list_t *list,char *str) + { + if (list->size==list->used) + if (!list_resize(list)) + return 0; + + list->strings[list->used]=str; + list->used++; + return 1; + } + +static char *str_from_bitfield_or_null(unsigned char octet, ops_bit_map_t *map) + { + ops_bit_map_t *row; + + for ( row=map; row->string != NULL; row++ ) + if (row->mask == octet) + return row->string; + + return NULL; + } + +static char *str_from_bitfield(unsigned char octet, ops_bit_map_t *map) + { + char *str; + str=str_from_bitfield_or_null(octet,map); + if (str) + return str; + else + return "Unknown"; + } + +/*! generic function to initialise ops_text_t structure */ +void ops_text_init(ops_text_t *text) + { + list_init(&text->known); + list_init(&text->unknown); + } + +/** + * \ingroup Core_Print + * + * ops_text_free() frees the memory used by an ops_text_t structure + * + * \param text Pointer to a previously allocated structure. This structure and its contents will be freed. + */ +void ops_text_free(ops_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); + + /* finally, free the text structure itself */ + free(text); + } + +// XXX: should this (and many others) be ops_boolean_t? +/*! generic function which adds text derived from single octet map to text */ +static unsigned int add_str_from_octet_map(ops_text_t *text,char *str, + unsigned char octet) + { + if (str && !add_str(&text->known,str)) + { + /* value recognised, but there was a problem adding it to the list */ + /* XXX - should print out error msg here, Ben? - rachel */ + return 0; + } + else if (!str) + { + /* value not recognised and there was a problem adding it to the unknown list */ + unsigned len=2+2+1; /* 2 for "0x", 2 for single octet in hex format, 1 for NULL */ + str=malloc(len); + snprintf(str,len,"0x%x",octet); + if (!add_str(&text->unknown,str)) + return 0; + } + return 1; + } + +/*! generic function which adds text derived from single bit map to text */ +static unsigned int add_str_from_bit_map(ops_text_t *text, char *str, unsigned char bit) + { + char *fmt_unknown="Unknown bit(0x%x)"; + + if (str && !add_str(&text->known,str)) + { + /* value recognised, but there was a problem adding it to the list */ + /* XXX - should print out error msg here, Ben? - rachel */ + return 0; + } + else if (!str) + { + /* value not recognised and there was a problem adding it to the unknown list */ + /* 2 chars of the string are the format definition, + this will be replaced in the output by 2 chars of hex, + so the length will be correct */ + unsigned len=strlen(fmt_unknown)+1; + str=malloc(len); + + snprintf(str,len,fmt_unknown,bit); + if (!add_str(&text->unknown,str)) + return 0; + } + return 1; + } + +/** + * Produce a structure containing human-readable textstrings + * representing the recognised and unrecognised contents + * of this byte array. text_fn() will be called on each octet in turn. + * Each octet will generate one string representing the whole byte. + * + */ + +static ops_text_t *text_from_bytemapped_octets(ops_data_t *data, + const char *(*text_fn)(unsigned char octet)) + { + + ops_text_t *text=NULL; + const char *str; + unsigned i; + + /*! allocate and initialise ops_text_t structure to store derived strings */ + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /*! for each octet in field ... */ + for(i=0 ; i < data->len ; i++) + { + /*! derive string from octet */ + str=(*text_fn)(data->contents[i]); + + /*! and add to text */ + if (!add_str_from_octet_map(text,strdup(str),data->contents[i])) + { + ops_text_free(text); + return NULL; + } + + } + /*! All values have been added to either the known or the unknown list */ + /*! Return text */ + return text; + } + +/** + * Produce a structure containing human-readable textstrings + * representing the recognised and unrecognised contents + * of this byte array, derived from each bit of each octet. + * + */ +static ops_text_t *showall_octets_bits(ops_data_t *data,ops_bit_map_t **map, + size_t nmap) + { + ops_text_t *text=NULL; + char *str; + unsigned i; + int j=0; + unsigned char mask, bit; + + /*! allocate and initialise ops_text_t structure to store derived strings */ + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /*! for each octet in field ... */ + for(i=0 ; i < data->len ; i++) + { + /*! for each bit in octet ... */ + for (j=0, mask=0x80; j<8; j++, mask = mask>>1 ) + { + bit = data->contents[i]&mask; + if (bit) + { + if(i >= nmap) + str="Unknown"; + else + str=str_from_bitfield ( bit, map[i] ); + if (!add_str_from_bit_map( text, str, bit)) + { + ops_text_free(text); + return NULL; + } + } + } + } + return text; + } + +/* + * Public Functions + */ + +/** + * \ingroup Core_Print + * returns description of the Packet Tag + * \param packet_tag + * \return string or "Unknown" +*/ +const char *ops_show_packet_tag(ops_packet_tag_t packet_tag) + { + char *rtn=NULL; + rtn=show_packet_tag(packet_tag,packet_tag_map); + + if (!rtn) + rtn="Unknown Tag"; + + return rtn; + } + +/** + * \ingroup Core_Print + * + * returns description of the Signature Sub-Packet type + * \param ss_type Signature Sub-Packet type + * \return string or "Unknown" + */ +const char *ops_show_ss_type(ops_ss_type_t ss_type) + { + return show_ss_type(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 *ops_show_ss_rr_code(ops_ss_rr_code_t ss_rr_code) + { + return show_ss_rr_code(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 *ops_show_sig_type(ops_sig_type_t sig_type) + { + return show_sig_type(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 *ops_show_pka(ops_public_key_algorithm_t pka) + { + return show_pka(pka, public_key_algorithm_map); + } + +/** + * \ingroup Core_Print + * returns description of the Preferred Compression + * \param octet Preferred Compression + * \return string or "Unknown" +*/ +const char *ops_show_ss_preferred_compression(unsigned char octet) + { + return ops_str_from_map(octet,compression_algorithm_map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Compression Algorithms + * \param ss_preferred_compression Array of Preferred Compression Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_compression(ops_ss_preferred_compression_t ss_preferred_compression) + { + return text_from_bytemapped_octets(&ss_preferred_compression.data, + &ops_show_ss_preferred_compression); + } + + +/** + * \ingroup Core_Print + * + * returns description of the Hash Algorithm type + * \param hash Hash Algorithm type + * \return string or "Unknown" + */ +const char *ops_show_hash_algorithm(unsigned char hash) + { + return show_hash_algorithm(hash); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Hash Algorithms + * \param ss_preferred_hash Array of Preferred Hash Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_hash(ops_ss_preferred_hash_t ss_preferred_hash) + { + return text_from_bytemapped_octets(&ss_preferred_hash.data, + &ops_show_hash_algorithm); + } + +const char *ops_show_symmetric_algorithm(unsigned char hash) + { + return show_symmetric_algorithm(hash); + } + +/** + * \ingroup Core_Print + * returns description of the given Preferred Symmetric Key Algorithm + * \param octet + * \return string or "Unknown" +*/ +const char *ops_show_ss_preferred_ska(unsigned char octet) + { + return ops_str_from_map(octet,symmetric_algorithm_map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Symmetric Key Algorithms + * \param ss_preferred_ska Array of Preferred Symmetric Key Algorithms + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_preferred_ska(ops_ss_preferred_ska_t ss_preferred_ska) + { + return text_from_bytemapped_octets(&ss_preferred_ska.data, + &ops_show_ss_preferred_ska); + } + +/** + * \ingroup Core_Print + * returns description of one SS Feature + * \param octet + * \return string or "Unknown" +*/ +static char *ops_show_ss_feature(unsigned char octet,unsigned offset) + { + if(offset >= OPS_ARRAY_SIZE(ss_feature_map)) + return "Unknown"; + return str_from_bitfield(octet,ss_feature_map[offset]); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given SS Features + * \param ss_features Signature Sub-Packet Features + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +/* XXX: shouldn't this use show_all_octets_bits? */ +ops_text_t *ops_showall_ss_features(ops_ss_features_t ss_features) + { + ops_text_t *text=NULL; + char *str; + unsigned i; + int j=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + for(i=0 ; i < ss_features.data.len ; i++) + { + for (j=0, mask=0x80; j<8; j++, mask = mask>>1 ) + { + bit = ss_features.data.contents[i]&mask; + if (bit) + { + str=ops_show_ss_feature ( bit, i ); + if (!add_str_from_bit_map( text, str, bit)) + { + ops_text_free(text); + return NULL; + } + } + } + } + return text; + } + +/** + * \ingroup Core_Print + * returns description of SS Key Flag + * \param octet + * \param map + * \return +*/ +const char *ops_show_ss_key_flag(unsigned char octet, ops_bit_map_t *map) + { + return str_from_bitfield(octet,map); + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given Preferred Key Flags + * \param ss_key_flags Array of Key Flags + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_key_flags(ops_ss_key_flags_t ss_key_flags) + { + ops_text_t *text=NULL; + const char *str; + int i=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /* xxx - TBD: extend to handle multiple octets of bits - rachel */ + + for (i=0,mask=0x80 ; i < 8 ; i++,mask=mask >> 1) + { + bit=ss_key_flags.data.contents[0]&mask; + if(bit) + { + str=ops_show_ss_key_flag(bit,&ss_key_flags_map[0]); + if(!add_str_from_bit_map(text,strdup(str),bit)) + { + ops_text_free(text); + return NULL; + } + } + } +/* xxx - must add error text if more than one octet. Only one currently specified -- rachel */ + return text; + } + +/** + * \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 *ops_show_ss_key_server_prefs(unsigned char prefs, + ops_bit_map_t *map) + { + return str_from_bitfield(prefs,map); + } + +/** + * \ingroup Core_Print + * returns set of descriptions of given Key Server Preferences + * \param ss_key_server_prefs + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + * +*/ +ops_text_t *ops_showall_ss_key_server_prefs(ops_ss_key_server_prefs_t ss_key_server_prefs) + { + ops_text_t *text=NULL; + const char *str; + int i=0; + unsigned char mask, bit; + + text=malloc(sizeof(ops_text_t)); + if (!text) + return NULL; + + ops_text_init(text); + + /* xxx - TBD: extend to handle multiple octets of bits - rachel */ + + for (i=0,mask=0x80 ; i < 8 ; i++,mask=mask >> 1) + { + bit=ss_key_server_prefs.data.contents[0]&mask; + if (bit) + { + str=ops_show_ss_key_server_prefs(bit, + &ss_key_server_prefs_map[0]); + if(!add_str_from_bit_map( text, strdup(str), bit)) + { + ops_text_free(text); + return NULL; + } + } + } +/* xxx - must add error text if more than one octet. Only one currently specified -- rachel */ + return text; + } + +/** + * \ingroup Core_Print + * + * returns set of descriptions of the given SS Notation Data Flags + * \param ss_notation_data Signature Sub-Packet Notation Data + * \return NULL if cannot allocate memory or other error + * \return pointer to structure, if no error + */ +ops_text_t *ops_showall_ss_notation_data_flags(ops_ss_notation_data_t ss_notation_data) + { + return showall_octets_bits(&ss_notation_data.flags,ss_notation_data_map, + OPS_ARRAY_SIZE(ss_notation_data_map)); + } diff --git a/openpgpsdk/src/openpgpsdk/packet-show.h b/openpgpsdk/src/openpgpsdk/packet-show.h new file mode 100644 index 000000000..f8dc4050f --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet-show.h @@ -0,0 +1,94 @@ +/* + * 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 OPS_PACKET_TO_TEXT_H +#define OPS_PACKET_TO_TEXT_H + +#ifndef OPS_PACKET_H +#include "packet.h" +#endif + +/** ops_list_t + */ +typedef struct + { + unsigned int size;/* num of array slots allocated */ + unsigned int used; /* num of array slots currently used */ + char **strings; + } ops_list_t; + +/** ops_text_t + */ +typedef struct + { + ops_list_t known; + ops_list_t unknown; + } ops_text_t; + +/** ops_bit_map_t + */ +typedef struct + { + unsigned char mask; + char *string; + } ops_bit_map_t; + +void ops_text_init(ops_text_t *text); +void ops_text_free(ops_text_t *text); + +const char *ops_show_packet_tag(ops_packet_tag_t packet_tag); +const char *ops_show_ss_type(ops_ss_type_t ss_type); + +const char *ops_show_sig_type(ops_sig_type_t sig_type); +const char *ops_show_pka(ops_public_key_algorithm_t pka); + +ops_text_t *ops_showall_ss_preferred_compression(ops_ss_preferred_compression_t ss_preferred_compression); +const char *ops_show_ss_preferred_compression(unsigned char octet); + +ops_text_t *ops_showall_ss_preferred_hash(ops_ss_preferred_hash_t ss_preferred_hash); +const char *ops_show_hash_algorithm(unsigned char octet); +const char *ops_show_symmetric_algorithm(unsigned char hash); + +ops_text_t *ops_showall_ss_preferred_ska(ops_ss_preferred_ska_t ss_preferred_ska); +const char *ops_show_ss_preferred_ska(unsigned char octet); + +const char *ops_show_ss_rr_code(ops_ss_rr_code_t ss_rr_code); + +ops_text_t *ops_showall_ss_features(ops_ss_features_t ss_features); + +ops_text_t *ops_showall_ss_key_flags(ops_ss_key_flags_t ss_key_flags); +const char *ops_show_ss_key_flag(unsigned char octet, ops_bit_map_t *map); + +ops_text_t *ops_showall_ss_key_server_prefs(ops_ss_key_server_prefs_t ss_key_server_prefs); +const char *ops_show_ss_key_server_prefs(unsigned char octet, + ops_bit_map_t *map); + +ops_text_t *ops_showall_ss_notation_data_flags(ops_ss_notation_data_t ss_notation_data); + +char *ops_str_from_map(int code, ops_map_t *map); + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + +#endif /* OPS_PACKET_TO_TEXT_H */ diff --git a/openpgpsdk/src/openpgpsdk/packet.h b/openpgpsdk/src/openpgpsdk/packet.h new file mode 100644 index 000000000..cbbd3499e --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/packet.h @@ -0,0 +1,948 @@ +/* + * 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 OPS_PACKET_H +#define OPS_PACKET_H + +#include "configure.h" + +#include +#include +#include +#include "types.h" +#include "errors.h" + +/** General-use structure for variable-length data + */ + +typedef struct + { + size_t len; + unsigned char *contents; + } ops_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 OPS_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 OPS_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 #OPS_PTAG_OF_CONTENT_TAG_SHIFT bits. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_OF_CONTENT_TAG_MASK 0x3c +/** Old Packet Format: Offset for the content tag. + * As described at #OPS_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 OPS_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 #ops_ptag_of_lt_t for the meaning of the values. + * + * \see RFC4880 4.2 + */ +#define OPS_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 + { + OPS_PTAG_OF_LT_ONE_BYTE =0x00, /*!< Packet has a 1 byte length - header is 2 bytes long. */ + OPS_PTAG_OF_LT_TWO_BYTE =0x01, /*!< Packet has a 2 byte length - header is 3 bytes long. */ + OPS_PTAG_OF_LT_FOUR_BYTE =0x02, /*!< Packet has a 4 byte length - header is 5 bytes long. */ + OPS_PTAG_OF_LT_INDETERMINATE =0x03 /*!< Packet has a indeterminate length. */ + } ops_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 #OPS_PTAG_NF_CONTENT_TAG_SHIFT bits. + * + * \see RFC4880 4.2 + */ +#define OPS_PTAG_NF_CONTENT_TAG_MASK 0x3f +/** New Packet Format: Offset for the content tag. + * As described at #OPS_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 OPS_PTAG_NF_CONTENT_TAG_SHIFT 0 + + +/** Structure to hold one parse error string. */ +typedef struct + { + const char *error; /*!< error message. */ + } ops_parser_error_t; + +/** Structure to hold one error code */ +typedef struct + { + ops_errcode_t errcode; + } ops_parser_errcode_t; + +/** Structure to hold one packet tag. + * \see RFC4880 4.2 + */ +typedef struct + { + unsigned new_format; /*!< Whether this packet tag is new (true) or old format (false) */ + unsigned content_tag; /*!< content_tag value - See #ops_content_tag_t for meanings */ + ops_ptag_of_lt_t length_type; /*!< Length type (#ops_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 #length_read is set. */ /* XXX: Ben, is this correct? */ + unsigned position; /*!< The position (within the current reader) of the packet */ + } ops_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 + { + OPS_PKA_RSA =1, /*!< RSA (Encrypt or Sign) */ + OPS_PKA_RSA_ENCRYPT_ONLY =2, /*!< RSA Encrypt-Only (deprecated - \see RFC4880 13.5) */ + OPS_PKA_RSA_SIGN_ONLY =3, /*!< RSA Sign-Only (deprecated - \see RFC4880 13.5) */ + OPS_PKA_ELGAMAL =16, /*!< Elgamal (Encrypt-Only) */ + OPS_PKA_DSA =17, /*!< DSA (Digital Signature Algorithm) */ + OPS_PKA_RESERVED_ELLIPTIC_CURVE =18, /*!< Reserved for Elliptic Curve */ + OPS_PKA_RESERVED_ECDSA =19, /*!< Reserved for ECDSA */ + OPS_PKA_ELGAMAL_ENCRYPT_OR_SIGN =20, /*!< Deprecated. */ + OPS_PKA_RESERVED_DH =21, /*!< Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME) */ + OPS_PKA_PRIVATE00 =100, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE01 =101, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE02 =102, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE03 =103, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE04 =104, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE05 =105, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE06 =106, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE07 =107, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE08 =108, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE09 =109, /*!< Private/Experimental Algorithm */ + OPS_PKA_PRIVATE10 =110, /*!< Private/Experimental Algorithm */ + } ops_public_key_algorithm_t; + +/** Structure to hold one DSA public key parameters. + * + * \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) */ + } ops_dsa_public_key_t; + +/** Structure to hold on RSA public key. + * + * \see RFC4880 5.5.2 + */ +typedef struct + { + BIGNUM *n; /*!< RSA public modulus n */ + BIGNUM *e; /*!< RSA public encryptiong exponent e */ + } ops_rsa_public_key_t; + +/** Structure to hold on ElGamal public key parameters. + * + * \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) */ + } ops_elgamal_public_key_t; + +/** Union to hold public key parameters of any algorithm */ +typedef union + { + ops_dsa_public_key_t dsa; /*!< A DSA public key */ + ops_rsa_public_key_t rsa; /*!< An RSA public key */ + ops_elgamal_public_key_t elgamal; /*!< An ElGamal public key */ + } ops_public_key_union_t; + +/** Version. + * OpenPGP has two different protocol versions: version 3 and version 4. + * + * \see RFC4880 5.2 + */ +typedef enum + { + OPS_V2=2, /*contents to be a null-terminated list */ + } ops_ss_preferred_ska_t; + +/** Signature Subpacket : Preferrred Hash Algorithm */ +typedef struct + { + ops_data_t data; + } ops_ss_preferred_hash_t; + +/** Signature Subpacket : Preferred Compression */ +typedef struct + { + ops_data_t data; + } ops_ss_preferred_compression_t; + +/** Signature Subpacket : Key Flags */ +typedef struct + { + ops_data_t data; + } ops_ss_key_flags_t; + +/** Signature Subpacket : Key Server Preferences */ +typedef struct + { + ops_data_t data; + } ops_ss_key_server_prefs_t; + +/** Signature Subpacket : Features */ +typedef struct + { + ops_data_t data; + } ops_ss_features_t; + +/** Signature Subpacket : Signature Target */ +typedef struct + { + ops_public_key_algorithm_t pka_alg; + ops_hash_algorithm_t hash_alg; + ops_data_t hash; + } ops_ss_signature_target_t; + +/** Signature Subpacket : Embedded Signature */ +typedef struct + { + ops_data_t sig; + } ops_ss_embedded_signature_t; + +/** ops_packet_t */ + +typedef struct + { + size_t length; + unsigned char *raw; + } ops_packet_t; + +/** Types of Compression */ +typedef enum + { + OPS_C_NONE=0, + OPS_C_ZIP=1, + OPS_C_ZLIB=2, + OPS_C_BZIP2=3, + } ops_compression_type_t; + +/* unlike most structures, this will feed its data as a stream + * to the application instead of directly including it */ +/** ops_compressed_t */ +typedef struct + { + ops_compression_type_t type; + } ops_compressed_t; + +/** ops_one_pass_signature_t */ +typedef struct + { + unsigned char version; + ops_sig_type_t sig_type; + ops_hash_algorithm_t hash_algorithm; + ops_public_key_algorithm_t key_algorithm; + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_boolean_t nested; + } ops_one_pass_signature_t; + +/** Signature Subpacket : Primary User ID */ +typedef struct + { + ops_boolean_t primary_user_id; + } ops_ss_primary_user_id_t; + +/** Signature Subpacket : Regexp */ +typedef struct + { + char *text; + } ops_ss_regexp_t; + +/** Signature Subpacket : Policy URL */ +typedef struct + { + char *text; + } ops_ss_policy_url_t; + +/** Signature Subpacket : Preferred Key Server */ +typedef struct + { + char *text; + } ops_ss_preferred_key_server_t; + +/** Signature Subpacket : Revocation Key */ +typedef struct + { + unsigned char clss; /* class - name changed for C++ */ + unsigned char algid; + unsigned char fingerprint[20]; + } ops_ss_revocation_key_t; + +/** Signature Subpacket : Revocation Reason */ +typedef struct + { + unsigned char code; + char *text; + } ops_ss_revocation_reason_t; + +/** literal_data_type_t */ +typedef enum + { + OPS_LDT_BINARY='b', + OPS_LDT_TEXT='t', + OPS_LDT_UTF8='u', + OPS_LDT_LOCAL='l', + OPS_LDT_LOCAL2='1' + } ops_literal_data_type_t; + +/** ops_literal_data_header_t */ +typedef struct + { + ops_literal_data_type_t format; + char filename[256]; + time_t modification_time; + } ops_literal_data_header_t; + +/** ops_literal_data_body_t */ +typedef struct + { + unsigned length; + unsigned char *data;//[8192]; + } ops_literal_data_body_t; + +/** ops_mdc_t */ +typedef struct + { + unsigned char data[20]; // size of SHA1 hash + } ops_mdc_t; + +/** ops_armoured_header_value_t */ +typedef struct + { + char *key; + char *value; + } ops_armoured_header_value_t; + +/** ops_headers_t */ +typedef struct + { + ops_armoured_header_value_t *headers; + unsigned nheaders; + } ops_headers_t; + +/** ops_armour_header_t */ +typedef struct + { + const char *type; + ops_headers_t headers; + } ops_armour_header_t; + +/** ops_armour_trailer_t */ +typedef struct + { + const char *type; + } ops_armour_trailer_t; + +/** ops_signed_cleartext_header_t */ +typedef struct + { + ops_headers_t headers; + } ops_signed_cleartext_header_t; + +/** ops_signed_cleartext_body_t */ +typedef struct + { + unsigned length; + unsigned char *data; // \todo fix hard-coded value? + } ops_signed_cleartext_body_t; + +/** ops_signed_cleartext_trailer_t */ +typedef struct + { + struct _ops_hash_t *hash; /*!< This will not have been finalised, but will have seen all the cleartext data in canonical form */ + } ops_signed_cleartext_trailer_t; + +/** ops_unarmoured_text_t */ +typedef struct + { + unsigned length; + unsigned char *data; + } ops_unarmoured_text_t; + +typedef enum + { + SE_IP_DATA_VERSION=1 + } ops_se_ip_data_version_t; + +typedef enum + { + OPS_PKSK_V3=3 + } ops_pk_session_key_version_t; + +/** ops_pk_session_key_parameters_rsa_t */ +typedef struct + { + BIGNUM *encrypted_m; + BIGNUM *m; + } ops_pk_session_key_parameters_rsa_t; + +/** ops_pk_session_key_parameters_elgamal_t */ +typedef struct + { + BIGNUM *g_to_k; + BIGNUM *encrypted_m; + } ops_pk_session_key_parameters_elgamal_t; + +/** ops_pk_session_key_parameters_t */ +typedef union + { + ops_pk_session_key_parameters_rsa_t rsa; + ops_pk_session_key_parameters_elgamal_t elgamal; + } ops_pk_session_key_parameters_t; + +/** ops_pk_session_key_t */ +typedef struct + { + ops_pk_session_key_version_t version; + unsigned char key_id[OPS_KEY_ID_SIZE]; + ops_public_key_algorithm_t algorithm; + ops_pk_session_key_parameters_t parameters; + ops_symmetric_algorithm_t symmetric_algorithm; + unsigned char key[OPS_MAX_KEY_SIZE]; + unsigned short checksum; + } ops_pk_session_key_t; + +/** ops_secret_key_passphrase_t */ +typedef struct + { + const ops_secret_key_t *secret_key; + char **passphrase; /* point somewhere that gets filled in to work around constness of content */ + } ops_secret_key_passphrase_t; + +typedef enum + { + OPS_SE_IP_V1=1 + } ops_se_ip_version_t; + +/** ops_se_ip_data_header_t */ +typedef struct + { + ops_se_ip_version_t version; + } ops_se_ip_data_header_t; + +/** ops_se_ip_data_body_t */ +typedef struct + { + unsigned length; + unsigned char* data; // \todo remember to free this + } ops_se_ip_data_body_t; + +/** ops_se_data_body_t */ +typedef struct + { + unsigned length; + unsigned char data[8192]; // \todo parameterise this! + } ops_se_data_body_t; + +/** ops_get_secret_key_t */ +typedef struct + { + const ops_secret_key_t **secret_key; + const ops_pk_session_key_t *pk_session_key; + } ops_get_secret_key_t; + +/** ops_parser_union_content_t */ +typedef union + { + ops_parser_error_t error; + ops_parser_errcode_t errcode; + ops_ptag_t ptag; + ops_public_key_t public_key; + ops_trust_t trust; + ops_user_id_t user_id; + ops_user_attribute_t user_attribute; + ops_signature_t signature; + ops_ss_raw_t ss_raw; + ops_ss_trust_t ss_trust; + ops_ss_revocable_t ss_revocable; + ops_ss_time_t ss_time; + ops_ss_key_id_t ss_issuer_key_id; + ops_ss_notation_data_t ss_notation_data; + ops_packet_t packet; + ops_compressed_t compressed; + ops_one_pass_signature_t one_pass_signature; + ops_ss_preferred_ska_t ss_preferred_ska; + ops_ss_preferred_hash_t ss_preferred_hash; + ops_ss_preferred_compression_t ss_preferred_compression; + ops_ss_key_flags_t ss_key_flags; + ops_ss_key_server_prefs_t ss_key_server_prefs; + ops_ss_primary_user_id_t ss_primary_user_id; + ops_ss_regexp_t ss_regexp; + ops_ss_policy_url_t ss_policy_url; + ops_ss_preferred_key_server_t ss_preferred_key_server; + ops_ss_revocation_key_t ss_revocation_key; + ops_ss_userdefined_t ss_userdefined; + ops_ss_unknown_t ss_unknown; + ops_literal_data_header_t literal_data_header; + ops_literal_data_body_t literal_data_body; + ops_mdc_t mdc; + ops_ss_features_t ss_features; + ops_ss_signature_target_t ss_signature_target; + ops_ss_embedded_signature_t ss_embedded_signature; + ops_ss_revocation_reason_t ss_revocation_reason; + ops_secret_key_t secret_key; + ops_user_id_t ss_signers_user_id; + ops_armour_header_t armour_header; + ops_armour_trailer_t armour_trailer; + ops_signed_cleartext_header_t signed_cleartext_header; + ops_signed_cleartext_body_t signed_cleartext_body; + ops_signed_cleartext_trailer_t signed_cleartext_trailer; + ops_unarmoured_text_t unarmoured_text; + ops_pk_session_key_t pk_session_key; + ops_secret_key_passphrase_t secret_key_passphrase; + ops_se_ip_data_header_t se_ip_data_header; + ops_se_ip_data_body_t se_ip_data_body; + ops_se_data_body_t se_data_body; + ops_get_secret_key_t get_secret_key; + } ops_parser_content_union_t; + +/** ops_parser_content_t */ +struct ops_parser_content_t + { + ops_content_tag_t tag; + unsigned char critical; /* for signature subpackets */ + ops_parser_content_union_t content; + }; + +/** ops_fingerprint_t */ +typedef struct + { + unsigned char fingerprint[20]; + unsigned length; + } ops_fingerprint_t; + +void ops_init(void); +void ops_finish(void); +void ops_keyid(unsigned char keyid[OPS_KEY_ID_SIZE], + const ops_public_key_t *key); +void ops_fingerprint(ops_fingerprint_t *fp,const ops_public_key_t *key); +void ops_public_key_free(ops_public_key_t *key); +void ops_public_key_copy(ops_public_key_t *dst,const ops_public_key_t *src); +void ops_user_id_free(ops_user_id_t *id); +void ops_user_attribute_free(ops_user_attribute_t *att); +void ops_signature_free(ops_signature_t *sig); +void ops_trust_free(ops_trust_t *trust); +void ops_ss_preferred_ska_free(ops_ss_preferred_ska_t *ss_preferred_ska); +void ops_ss_preferred_hash_free(ops_ss_preferred_hash_t *ss_preferred_hash); +void ops_ss_preferred_compression_free(ops_ss_preferred_compression_t *ss_preferred_compression); +void ops_ss_key_flags_free(ops_ss_key_flags_t *ss_key_flags); +void ops_ss_key_server_prefs_free(ops_ss_key_server_prefs_t *ss_key_server_prefs); +void ops_ss_features_free(ops_ss_features_t *ss_features); +void ops_ss_notation_data_free(ops_ss_notation_data_t *ss_notation_data); +void ops_ss_policy_url_free(ops_ss_policy_url_t *ss_policy_url); +void ops_ss_preferred_key_server_free(ops_ss_preferred_key_server_t *ss_preferred_key_server); +void ops_ss_regexp_free(ops_ss_regexp_t *ss_regexp); +void ops_ss_userdefined_free(ops_ss_userdefined_t *ss_userdefined); +void ops_ss_reserved_free(ops_ss_unknown_t *ss_unknown); +void ops_ss_revocation_reason_free(ops_ss_revocation_reason_t *ss_revocation_reason); +void ops_ss_signature_target_free(ops_ss_signature_target_t *ss_signature_target); +void ops_ss_embedded_signature_free(ops_ss_embedded_signature_t *ss_embedded_signature); + +void ops_packet_free(ops_packet_t *packet); +void ops_parser_content_free(ops_parser_content_t *c); +void ops_secret_key_free(ops_secret_key_t *key); +void ops_secret_key_copy(ops_secret_key_t *dst,const ops_secret_key_t *src); +void ops_pk_session_key_free(ops_pk_session_key_t *sk); + +/* vim:set textwidth=120: */ +/* vim:set ts=8: */ + +#endif diff --git a/openpgpsdk/src/openpgpsdk/parse_local.h b/openpgpsdk/src/openpgpsdk/parse_local.h new file mode 100644 index 000000000..524f251dc --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/parse_local.h @@ -0,0 +1,118 @@ +/* + * 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 +#include + +/** ops_reader_info */ +struct ops_reader_info + { + ops_reader_t *reader; /*!< the reader function to use to get the + data to be parsed */ + ops_reader_destroyer_t *destroyer; + void *arg; /*!< the args to pass to the reader function */ + + ops_boolean_t accumulate:1; /*!< set to accumulate packet data */ + unsigned char *accumulated; /*!< the accumulated data */ + unsigned asize; /*!< size of the buffer */ + unsigned alength; /*!< used buffer */ + /* XXX: what do we do about offsets into compressed packets? */ + unsigned position; /*!< the offset from the beginning (with this reader) */ + + ops_reader_info_t *next; + ops_parse_info_t *pinfo; /*!< A pointer back to the parent parse_info structure */ + }; + + +/** ops_crypt_info + Encrypt/decrypt settings +*/ +struct ops_crypt_info + { + char *passphrase; /* + +#include + +void ops_random(void *dest,size_t length) + { + RAND_bytes(dest,length); + } diff --git a/openpgpsdk/src/openpgpsdk/random.h b/openpgpsdk/src/openpgpsdk/random.h new file mode 100644 index 000000000..833094353 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/random.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifdef WIN32 +#include +#else +#include +#endif + +void ops_random(void *dest,size_t length); diff --git a/openpgpsdk/src/openpgpsdk/reader.c b/openpgpsdk/src/openpgpsdk/reader.c new file mode 100644 index 000000000..24fc8a041 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader.c @@ -0,0 +1,109 @@ +/* + * 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 +#ifndef WIN32 +#include +#else +#include +#endif +#include +#include +#include + +#include +#include + +#include "parse_local.h" + + +/** + * \ingroup Internal_Readers_Generic + * \brief Starts reader stack + * \param pinfo Parse settings + * \param reader Reader to use + * \param destroyer Destroyer to use + * \param arg Reader-specific arg + */ +void ops_reader_set(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg) + { + pinfo->rinfo.reader=reader; + pinfo->rinfo.destroyer=destroyer; + pinfo->rinfo.arg=arg; + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Adds to reader stack + * \param pinfo Parse settings + * \param reader Reader to use + * \param destroyer Reader's destroyer + * \param arg Reader-specific arg + */ +void ops_reader_push(ops_parse_info_t *pinfo,ops_reader_t *reader,ops_reader_destroyer_t *destroyer,void *arg) + { + ops_reader_info_t *rinfo=malloc(sizeof *rinfo); + + *rinfo=pinfo->rinfo; + memset(&pinfo->rinfo,'\0',sizeof pinfo->rinfo); + pinfo->rinfo.next=rinfo; + pinfo->rinfo.pinfo=pinfo; + + // should copy accumulate flags from other reader? RW + pinfo->rinfo.accumulate=rinfo->accumulate; + + ops_reader_set(pinfo,reader,destroyer,arg); + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Removes from reader stack + * \param pinfo Parse settings + */ +void ops_reader_pop(ops_parse_info_t *pinfo) + { + ops_reader_info_t *next=pinfo->rinfo.next; + // We are about to overwrite pinfo->rinfo, so free any data in the + // old rinfo structure first. + free(pinfo->rinfo.accumulated); + pinfo->rinfo=*next; + free(next); + } + +/** + * \ingroup Internal_Readers_Generic + * \brief Gets arg from reader + * \param rinfo Reader info + * \return Pointer to reader info's arg + */ +void *ops_reader_get_arg(ops_reader_info_t *rinfo) + { return rinfo->arg; } + +/** + * \ingroup Internal_Readers_Generic + * \brief Gets reader's arg from parse_info + * \param pinfo + * \return Pointer to parse_info's reader_info's arg + */ +void *ops_reader_get_arg_from_pinfo(ops_parse_info_t *pinfo) + { return pinfo->rinfo.arg; } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/reader_armoured.c b/openpgpsdk/src/openpgpsdk/reader_armoured.c new file mode 100644 index 000000000..dc8ca8f02 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_armoured.c @@ -0,0 +1,1046 @@ +/* + * 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 Code for dealing with ASCII-armoured packets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parse_local.h" + +#include +#include + +#include + +static int debug=0; + +#define CRC24_POLY 0x1864cfbL + +/** + * \struct dearmour_arg_t + */ +typedef struct + { + enum + { + OUTSIDE_BLOCK=0, + BASE64, + AT_TRAILER_NAME, + } state; + + 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 + } lastseen; + + ops_parse_info_t *parse_info; + ops_boolean_t seen_nl:1; + ops_boolean_t prev_nl:1; + ops_boolean_t allow_headers_without_gap:1; /*!< allow headers in + armoured data that + are not separated + from the data by a + blank line */ + ops_boolean_t allow_no_gap:1; /*!< allow no blank line at the + start of armoured data */ + ops_boolean_t 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 + ops_boolean_t expect_sig:1; + ops_boolean_t got_sig:1; + + // base64 stuff + unsigned buffered; + unsigned char buffer[3]; + ops_boolean_t eof64; + unsigned long checksum; + unsigned long read_checksum; + // unarmoured text blocks + unsigned char unarmoured[8192]; + size_t num_unarmoured; + // pushed back data (stored backwards) + unsigned char *pushed_back; + unsigned npushed_back; + // armoured block headers + ops_headers_t headers; + } dearmour_arg_t; + +static void push_back(dearmour_arg_t *arg,const unsigned char *buf, + unsigned length) + { + unsigned n; + + assert(!arg->pushed_back); + arg->pushed_back=malloc(length); + for(n=0 ; n < length ; ++n) + arg->pushed_back[n]=buf[length-n-1]; + arg->npushed_back=length; + } + +static int set_lastseen_headerline(dearmour_arg_t* arg, char* buf, ops_error_t **errors) + { + char* begin_msg="BEGIN PGP MESSAGE"; + char* begin_public="BEGIN PGP PUBLIC KEY BLOCK"; + char* begin_private="BEGIN PGP PRIVATE KEY BLOCK"; + char* begin_multi="BEGIN PGP MESSAGE, PART "; + char* begin_sig="BEGIN PGP SIGNATURE"; + + char* end_msg="END PGP MESSAGE"; + char* end_public="END PGP PUBLIC KEY BLOCK"; + char* end_private="END PGP PRIVATE KEY BLOCK"; + char* end_multi="END PGP MESSAGE, PART "; + char* end_sig="END PGP SIGNATURE"; + + char* begin_signed_msg="BEGIN PGP SIGNED MESSAGE"; + + int prev=arg->lastseen; + + if (!strncmp(buf,begin_msg,strlen(begin_msg))) + arg->lastseen=BEGIN_PGP_MESSAGE; + else if (!strncmp(buf,begin_public,strlen(begin_public))) + arg->lastseen=BEGIN_PGP_PUBLIC_KEY_BLOCK; + else if (!strncmp(buf,begin_private,strlen(begin_private))) + arg->lastseen=BEGIN_PGP_PRIVATE_KEY_BLOCK; + else if (!strncmp(buf,begin_multi,strlen(begin_multi))) + arg->lastseen=BEGIN_PGP_MULTI; + else if (!strncmp(buf,begin_sig,strlen(begin_sig))) + arg->lastseen=BEGIN_PGP_SIGNATURE; + + else if (!strncmp(buf,end_msg,strlen(end_msg))) + arg->lastseen=END_PGP_MESSAGE; + else if (!strncmp(buf,end_public,strlen(end_public))) + arg->lastseen=END_PGP_PUBLIC_KEY_BLOCK; + else if (!strncmp(buf,end_private,strlen(end_private))) + arg->lastseen=END_PGP_PRIVATE_KEY_BLOCK; + else if (!strncmp(buf,end_multi,strlen(end_multi))) + arg->lastseen=END_PGP_MULTI; + else if (!strncmp(buf,end_sig,strlen(end_sig))) + arg->lastseen=END_PGP_SIGNATURE; + + else if (!strncmp(buf,begin_signed_msg,strlen(begin_signed_msg))) + arg->lastseen=BEGIN_PGP_SIGNED_MESSAGE; + + else + { + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unrecognised Header Line %s", buf); + return 0; + } + + if (debug) + printf("set header: buf=%s, arg->lastseen=%d, prev=%d\n", buf, arg->lastseen, prev); + + switch (arg->lastseen) + { + case NONE: + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unrecognised last seen Header Line %s", buf); + break; + + case END_PGP_MESSAGE: + if (prev!=BEGIN_PGP_MESSAGE) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP MESSAGE, but not after BEGIN"); + break; + + case END_PGP_PUBLIC_KEY_BLOCK: + if (prev!=BEGIN_PGP_PUBLIC_KEY_BLOCK) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP PUBLIC KEY BLOCK, but not after BEGIN"); + break; + + case END_PGP_PRIVATE_KEY_BLOCK: + if (prev!=BEGIN_PGP_PRIVATE_KEY_BLOCK) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Got END PGP PRIVATE KEY BLOCK, but not after BEGIN"); + break; + + case BEGIN_PGP_MULTI: + case END_PGP_MULTI: + OPS_ERROR(errors,OPS_E_R_UNSUPPORTED,"Multi-part messages are not yet supported"); + break; + + case END_PGP_SIGNATURE: + if (prev!=BEGIN_PGP_SIGNATURE) + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"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(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + unsigned char c[1]; + + do + { + if(arg->npushed_back) + { + c[0]=arg->pushed_back[--arg->npushed_back]; + if(!arg->npushed_back) + { + free(arg->pushed_back); + arg->pushed_back=NULL; + } + } + /* XXX: should ops_stacked_read exist? Shouldn't this be a limited_read? */ + else if(ops_stacked_read(c,1,errors,rinfo,cbinfo) != 1) + return -1; + } + while(skip && c[0] == '\r'); + + arg->prev_nl=arg->seen_nl; + arg->seen_nl=c[0] == '\n'; + + return c[0]; + } + +static int eat_whitespace(int first, + dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c=first; + + while(c == ' ' || c == '\t') + c=read_char(arg,errors,rinfo,cbinfo,skip); + + return c; + } + +static int read_and_eat_whitespace(dearmour_arg_t *arg, + ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c; + + do + c=read_char(arg,errors,rinfo,cbinfo,skip); + while(c == ' ' || c == '\t'); + + return c; + } + +static void flush(dearmour_arg_t *arg,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_t content; + + if(arg->num_unarmoured == 0) + return; + + content.content.unarmoured_text.data=arg->unarmoured; + content.content.unarmoured_text.length=arg->num_unarmoured; + CB(cbinfo,OPS_PTAG_CT_UNARMOURED_TEXT,&content); + arg->num_unarmoured=0; + } + +static int unarmoured_read_char(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo, + ops_boolean_t skip) + { + int c; + + do + { + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c < 0) + return c; + arg->unarmoured[arg->num_unarmoured++]=c; + if(arg->num_unarmoured == sizeof arg->unarmoured) + flush(arg,cbinfo); + } + while(skip && c == '\r'); + + return c; + } + +/** + * \param headers + * \param key + * + * \return header value if found, otherwise NULL + */ +const char *ops_find_header(ops_headers_t *headers,const char *key) + { + unsigned n; + + for(n=0 ; n < headers->nheaders ; ++n) + if(!strcmp(headers->headers[n].key,key)) + return headers->headers[n].value; + return NULL; + } + +/** + * \param dest + * \param src + */ +void ops_dup_headers(ops_headers_t *dest,const ops_headers_t *src) + { + unsigned n; + + dest->headers=malloc(src->nheaders*sizeof *dest->headers); + dest->nheaders=src->nheaders; + + for(n=0 ; n < src->nheaders ; ++n) + { + dest->headers[n].key=strdup(src->headers[n].key); + dest->headers[n].value=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(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_t content; + ops_parser_content_t content2; + ops_signed_cleartext_body_t *body=&content.content.signed_cleartext_body; + ops_signed_cleartext_trailer_t *trailer + =&content2.content.signed_cleartext_trailer; + const char *hashstr; + ops_hash_t *hash; + int total; + + hash=malloc(sizeof *hash); + hashstr=ops_find_header(&arg->headers,"Hash"); + if(hashstr) + { + ops_hash_algorithm_t alg; + + alg=ops_hash_algorithm_from_text(hashstr); + + if(!ops_is_hash_alg_supported(&alg)) + { + free(hash); + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unsupported hash algorithm '%s'",hashstr); + return -1; + } + if(alg == OPS_HASH_UNKNOWN) + { + free(hash); + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Unknown hash algorithm '%s'",hashstr); + return -1; + } + ops_hash_any(hash,alg); + } + else + ops_hash_md5(hash); + + hash->init(hash); + + body->length=0; + total=0; + for( ; ; ) + { + int c; + unsigned count; + + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return -1; + if(arg->prev_nl && c == '-') + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != ' ') + { + /* then this had better be a trailer! */ + if(c != '-') + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad dash-escaping"); + for(count=2 ; count < 5 ; ++count) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != '-') + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad dash-escaping (2)"); + } + arg->state=AT_TRAILER_NAME; + break; + } + /* otherwise we read the next character */ + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + } + if(c == '\n' && body->length) + { + assert(memchr(body->data+1,'\n',body->length-1) == NULL); + if(body->data[0] == '\n') + hash->add(hash,(unsigned char *)"\r",1); + hash->add(hash,body->data,body->length); + if (debug) + { fprintf(stderr,"Got body:\n%s\n",body->data); } + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content); + body->length=0; + } + + body->data[body->length++]=c; + ++total; + if(body->length == sizeof body->data) + { + if (debug) + { fprintf(stderr,"Got body (2):\n%s\n",body->data); } + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content); + body->length=0; + } + } + + assert(body->data[0] == '\n'); + assert(body->length == 1); + /* don't send that one character, because its part of the trailer. */ + + trailer->hash=hash; + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER,&content2); + + return total; + } + +static int add_header(dearmour_arg_t *arg,const char *key,const char + *value) + { + /* + * Check that the header is valid + */ + if ( !strcmp(key,"Version") || !strcmp(key,"Comment") + || !strcmp(key,"MessageID") || !strcmp(key,"Hash") + || !strcmp(key,"Charset")) + { + arg->headers.headers=realloc(arg->headers.headers, + (arg->headers.nheaders+1) + *sizeof *arg->headers.headers); + arg->headers.headers[arg->headers.nheaders].key=strdup(key); + arg->headers.headers[arg->headers.nheaders].value=strdup(value); + ++arg->headers.nheaders; + return 1; + } + else + { + return 0; + } + } + +/* \todo what does a return value of 0 indicate? 1 is good, -1 is bad */ +static int parse_headers(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + int rtn=1; + char *buf; + unsigned nbuf; + unsigned size; + ops_boolean_t first=ops_true; + //ops_parser_content_t content; + + buf=NULL; + nbuf=size=0; + + for( ; ; ) + { + int c; + + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Unexpected EOF"); + rtn=-1; + break; + } + + if(c == '\n') + { + char *s; + + if(nbuf == 0) + break; + + assert(nbuf < size); + buf[nbuf]='\0'; + + s=strchr(buf,':'); + if(!s) + if(!first && !arg->allow_headers_without_gap) + { + // then we have seriously malformed armour + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No colon in armour header"); + rtn=-1; + break; + } + else + { + if(first && + !(arg->allow_headers_without_gap || arg->allow_no_gap)) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"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(arg,(unsigned char *)buf,nbuf+1); + rtn=-1; + break; + } + } + else + { + *s='\0'; + if(s[1] != ' ') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No space in armour header"); + rtn=-1; + goto end; + } + if (!add_header(arg,buf,s+2)) + { + OPS_ERROR_1(errors,OPS_E_R_BAD_FORMAT,"Invalid header %s", buf); + rtn=-1; + goto end; + } + nbuf=0; + } + first=ops_false; + } + else + { + if(size <= nbuf+1) + { + size+=size+80; + buf=realloc(buf,size); + } + buf[nbuf++]=c; + } + } + + end: + free(buf); + + return rtn; + } + +static int read4(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo, + int *pc,unsigned *pn,unsigned long *pl) + { + int n,c; + unsigned long l=0; + + for(n=0 ; n < 4 ; ++n) + { + c=read_char(arg,errors,rinfo,cbinfo,ops_true); + if(c < 0) + { + arg->eof64=ops_true; + return -1; + } + if(c == '-') + break; + if(c == '=') + break; + l <<= 6; + if(c >= 'A' && c <= 'Z') + l+=c-'A'; + else if(c >= 'a' && c <= 'z') + l+=c-'a'+26; + else if(c >= '0' && c <= '9') + l+=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 ops_crc24(unsigned checksum,unsigned char c) + { + unsigned i; + + checksum ^= c << 16; + for(i=0 ; i < 8 ; i++) + { + checksum <<= 1; + if(checksum & 0x1000000) + checksum ^= CRC24_POLY; + } + return checksum&0xffffffL; + } + +static int decode64(dearmour_arg_t *arg,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + unsigned n; + int n2; + unsigned long l; + int c; + int ret; + + assert(arg->buffered == 0); + + ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&l); + if(ret < 0) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly formed base64"); + return 0; + } + + if(n == 3) + { + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (2)"); + return 0; + } + arg->buffered=2; + arg->eof64=ops_true; + l >>= 2; + } + else if(n == 2) + { + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (3)"); + return 0; + } + arg->buffered=1; + arg->eof64=ops_true; + l >>= 4; + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64"); + return 0; + } + } + else if(n == 0) + { + if(!arg->prev_nl || c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated base64 (4)"); + return 0; + } + arg->buffered=0; + } + else + { + assert(n == 4); + arg->buffered=3; + assert(c != '-' && c != '='); + } + + if(arg->buffered < 3 && arg->buffered > 0) + { + // then we saw padding + assert(c == '='); + c=read_and_eat_whitespace(arg,errors,rinfo,cbinfo,ops_true); + if(c != '\n') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No newline at base64 end"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '=') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"No checksum at base64 end"); + return 0; + } + } + + if(c == '=') + { + // now we are at the checksum + ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&arg->read_checksum); + if(ret < 0 || n != 4) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Error in checksum"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_true); + if(arg->allow_trailing_whitespace) + c=eat_whitespace(c,arg,errors,rinfo,cbinfo,ops_true); + if(c != '\n') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Badly terminated checksum"); + return 0; + } + c=read_char(arg,errors,rinfo,cbinfo,ops_false); + if(c != '-') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad base64 trailer (2)"); + return 0; + } + } + + if(c == '-') + { + for(n=0 ; n < 4 ; ++n) + if(read_char(arg,errors,rinfo,cbinfo,ops_false) != '-') + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad base64 trailer"); + return 0; + } + arg->eof64=ops_true; + } + else + assert(arg->buffered); + + for(n=0 ; n < arg->buffered ; ++n) + { + arg->buffer[n]=l; + l >>= 8; + } + + for(n2=arg->buffered-1 ; n2 >= 0 ; --n2) + arg->checksum=ops_crc24(arg->checksum,arg->buffer[n2]); + + if(arg->eof64 && arg->read_checksum != arg->checksum) + { + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Checksum mismatch"); + return 0; + } + + return 1; + } + +static void base64(dearmour_arg_t *arg) + { + arg->state=BASE64; + arg->checksum=CRC24_INIT; + arg->eof64=ops_false; + arg->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(void *dest_,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + dearmour_arg_t *arg=ops_reader_get_arg(rinfo); + ops_parser_content_t content; + int ret; + ops_boolean_t first; + unsigned char *dest=dest_; + int saved=length; + + if(arg->eof64 && !arg->buffered) + assert(arg->state == OUTSIDE_BLOCK || arg->state == AT_TRAILER_NAME); + + while(length > 0) + { + unsigned count; + unsigned n; + char buf[1024]; + int c; + + flush(arg,cbinfo); + switch(arg->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(!arg->seen_nl) + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 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(arg,cbinfo); + /* Find and consume the 5 leading '-' */ + for(count=0 ; count < 5 ; ++count) + { + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return 0; + if(c != '-') + goto reloop; + } + + /* Now find the block type */ + for(n=0 ; n < sizeof buf-1 ; ) + { + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 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(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return 0; + if(c != '-') + /* wasn't a header after all */ + goto reloop; + } + + /* Consume final NL */ + if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return 0; + if(arg->allow_trailing_whitespace) + if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo, + ops_true)) < 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 */ + arg->num_unarmoured=0; + + /* But now we've seen a header line, then errors are + EARLY_EOF */ + if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0) + return -1; + + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + + if(!strcmp(buf,"BEGIN PGP SIGNED MESSAGE")) + { + ops_dup_headers(&content.content.signed_cleartext_header.headers,&arg->headers); + CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER,&content); + ret=process_dash_escaped(arg,errors,rinfo,cbinfo); + if(ret <= 0) + return ret; + } + else + { + content.content.armour_header.type=buf; + content.content.armour_header.headers=arg->headers; + memset(&arg->headers,'\0',sizeof arg->headers); + CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content); + base64(arg); + } + break; + + case BASE64: + first=ops_true; + while(length > 0) + { + if(!arg->buffered) + { + if(!arg->eof64) + { + ret=decode64(arg,errors,rinfo,cbinfo); + if(ret <= 0) + return ret; + } + if(!arg->buffered) + { + assert(arg->eof64); + if(first) + { + arg->state=AT_TRAILER_NAME; + goto reloop; + } + return -1; + } + } + + assert(arg->buffered); + *dest=arg->buffer[--arg->buffered]; + ++dest; + --length; + first=ops_false; + } + if(arg->eof64 && !arg->buffered) + arg->state=AT_TRAILER_NAME; + break; + + case AT_TRAILER_NAME: + for(n=0 ; n < sizeof buf-1 ; ) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c == '-') + goto got_minus2; + buf[n++]=c; + } + /* then I guess this wasn't a proper trailer */ + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer"); + break; + + got_minus2: + buf[n]='\0'; + + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + + /* Consume trailing '-' */ + for(count=1 ; count < 5 ; ++count) + { + if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0) + return -1; + if(c != '-') + /* wasn't a trailer after all */ + OPS_ERROR(errors, OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer (2)"); + } + + /* Consume final NL */ + if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0) + return -1; + if(arg->allow_trailing_whitespace) + if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo, + ops_true)) < 0) + return 0; + if(c != '\n') + /* wasn't a trailer line after all */ + OPS_ERROR(errors,OPS_E_R_BAD_FORMAT,"Bad ASCII armour trailer (3)"); + + if(!strncmp(buf,"BEGIN ",6)) + { + if (!set_lastseen_headerline(arg,buf,errors)) + return -1; + if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0) + return ret; + content.content.armour_header.type=buf; + content.content.armour_header.headers=arg->headers; + memset(&arg->headers,'\0',sizeof arg->headers); + CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content); + base64(arg); + } + else + { + content.content.armour_trailer.type=buf; + CB(cbinfo,OPS_PTAG_CT_ARMOUR_TRAILER,&content); + arg->state=OUTSIDE_BLOCK; + } + break; + } + reloop: + continue; + } + + return saved; + } + +static void armoured_data_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + * \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 ops_reader_pop_dearmour() + */ +void ops_reader_push_dearmour(ops_parse_info_t *parse_info) + /* + This function originally had these parameters 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 + ops_boolean_t without_gap, + + // Allow no blank line at the start of armoured data + ops_boolean_t no_gap, + + //Allow armoured data to have trailing whitespace where we strictly would not expect it + ops_boolean_t trailing_whitespace + */ + { + dearmour_arg_t *arg; + + arg=ops_mallocz(sizeof *arg); + arg->seen_nl=ops_true; +/* + arg->allow_headers_without_gap=without_gap; + arg->allow_no_gap=no_gap; + arg->allow_trailing_whitespace=trailing_whitespace; +*/ + arg->expect_sig=ops_false; + arg->got_sig=ops_false; + + ops_reader_push(parse_info,armoured_data_reader,armoured_data_destroyer,arg); + } + +/** + * \ingroup Core_Readers_Armour + * \brief Pops dearmour reader from stock + * \param pinfo + * \sa ops_reader_push_dearmour() + */ +void ops_reader_pop_dearmour(ops_parse_info_t *pinfo) + { + dearmour_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + free(arg); + ops_reader_pop(pinfo); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/reader_encrypted_se.c b/openpgpsdk/src/openpgpsdk/reader_encrypted_se.c new file mode 100644 index 000000000..5837eed8f --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_encrypted_se.c @@ -0,0 +1,213 @@ +/* + * 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 +#include +#include +#include +#ifndef OPENSSL_NO_IDEA +#include +#endif +#include +#include +#include "parse_local.h" + +#include +#include + +static int debug=0; + +#ifndef ATTRIBUTE_UNUSED + +#ifndef WIN32 +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#else +#define ATTRIBUTE_UNUSED +#endif // #ifndef WIN32 + +#endif /* ATTRIBUTE_UNUSED */ + + +// \todo there's also a encrypted_arg_t in adv_create.c +// which is used for *encrypting* whereas this is used +// for *decrypting* + +typedef struct + { + unsigned char decrypted[1024]; + size_t decrypted_count; + size_t decrypted_offset; + ops_crypt_t *decrypt; + ops_region_t *region; + ops_boolean_t prev_read_was_plain:1; + } encrypted_arg_t; + +static int encrypted_data_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + encrypted_arg_t *arg=ops_reader_get_arg(rinfo); + int saved=length; + + // V3 MPIs have the count plain and the cipher is reset after each count + if(arg->prev_read_was_plain && !rinfo->pinfo->reading_mpi_length) + { + assert(rinfo->pinfo->reading_v3_secret); + arg->decrypt->decrypt_resync(arg->decrypt); + arg->prev_read_was_plain=ops_false; + } + else if(rinfo->pinfo->reading_v3_secret + && rinfo->pinfo->reading_mpi_length) + { + arg->prev_read_was_plain=ops_true; + } + + while(length > 0) + { + if(arg->decrypted_count) + { + + unsigned n; + + // if we are reading v3 we should never read more than + // we're asked for + assert(length >= arg->decrypted_count + || (!rinfo->pinfo->reading_v3_secret + && !rinfo->pinfo->exact_read)); + + if(length > arg->decrypted_count) + n=arg->decrypted_count; + else + n=length; + + memcpy(dest,arg->decrypted+arg->decrypted_offset,n); + arg->decrypted_count-=n; + arg->decrypted_offset+=n; + length-=n; + dest+=n; + } + else + { + unsigned n=arg->region->length; + unsigned char buffer[1024]; + + if(!n) + { + return -1; + } + + if(!arg->region->indeterminate) + { + n-=arg->region->length_read; + if(n == 0) + return 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((rinfo->pinfo->reading_v3_secret || rinfo->pinfo->exact_read) + && n > length) + n=length; + + if(!ops_stacked_limited_read(buffer,n,arg->region,errors,rinfo, + cbinfo)) + { + return -1; + } + + if(!rinfo->pinfo->reading_v3_secret + || !rinfo->pinfo->reading_mpi_length) + { + arg->decrypted_count=ops_decrypt_se_ip(arg->decrypt, + arg->decrypted, + buffer,n); + + if (debug) + { + fprintf(stderr,"READING:\nencrypted: "); + int i=0; + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", buffer[i]); + fprintf(stderr,"\n"); + fprintf(stderr,"decrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", arg->decrypted[i]); + fprintf(stderr,"\n"); + } + } + else + { + memcpy(arg->decrypted,buffer,n); + arg->decrypted_count=n; + } + + assert(arg->decrypted_count > 0); + + arg->decrypted_offset=0; + } + } + + return saved; + } + +static void encrypted_data_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + * \ingroup Core_Readers_SE + * \brief Pushes decryption reader onto stack + * \sa ops_reader_pop_decrypt() + */ +void ops_reader_push_decrypt(ops_parse_info_t *pinfo,ops_crypt_t *decrypt, + ops_region_t *region) + { + encrypted_arg_t *arg=ops_mallocz(sizeof *arg); + + arg->decrypt=decrypt; + arg->region=region; + + ops_decrypt_init(arg->decrypt); + + ops_reader_push(pinfo,encrypted_data_reader,encrypted_data_destroyer,arg); + } + +/** + * \ingroup Core_Readers_Encrypted + * \brief Pops decryption reader from stack + * \sa ops_reader_push_decrypt() + */ +void ops_reader_pop_decrypt(ops_parse_info_t *pinfo) + { + encrypted_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + + arg->decrypt->decrypt_finish(arg->decrypt); + free(arg); + + ops_reader_pop(pinfo); + } + +// eof diff --git a/openpgpsdk/src/openpgpsdk/reader_encrypted_seip.c b/openpgpsdk/src/openpgpsdk/reader_encrypted_seip.c new file mode 100644 index 000000000..4df216697 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_encrypted_seip.c @@ -0,0 +1,248 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse_local.h" + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +#include + +static int debug=0; + +typedef struct + { + // boolean: false once we've done the preamble/MDC checks + // and are reading from the plaintext + int passed_checks; + unsigned char *plaintext; + size_t plaintext_available; + size_t plaintext_offset; + ops_region_t *region; + ops_crypt_t *decrypt; + } decrypt_se_ip_arg_t; + +static int se_ip_data_reader(void *dest_, size_t len, ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) + { + + /* + Gets entire SE_IP data packet. + Verifies leading preamble + Verifies trailing MDC packet + Then passes up plaintext as requested + */ + + unsigned int n=0; + + ops_region_t decrypted_region; + + decrypt_se_ip_arg_t *arg=ops_reader_get_arg(rinfo); + + if (!arg->passed_checks) + { + unsigned char*buf=NULL; + + ops_hash_t hash; + unsigned char hashed[SHA_DIGEST_LENGTH]; + + size_t b; + size_t sz_preamble; + size_t sz_mdc_hash; + size_t sz_mdc; + size_t sz_plaintext; + + unsigned char* preamble; + unsigned char* plaintext; + unsigned char* mdc; + unsigned char* mdc_hash; + + ops_hash_any(&hash,OPS_HASH_SHA1); + hash.init(&hash); + + ops_init_subregion(&decrypted_region,NULL); + decrypted_region.length = arg->region->length - arg->region->length_read; + buf=ops_mallocz(decrypted_region.length); + + // read entire SE IP packet + + if (!ops_stacked_limited_read(buf,decrypted_region.length, &decrypted_region,errors,rinfo,cbinfo)) + { + free (buf); + return -1; + } + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\n\nentire SE IP packet (len=%d):\n",decrypted_region.length); + for (i=0; idecrypt->blocksize+2;i++) + fprintf(stderr," 0x%02x", buf[i]); + fprintf(stderr,"\n"); + } + + b=arg->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]); + OPS_ERROR(errors, OPS_E_PROTO_BAD_SYMMETRIC_DECRYPT,"Bad symmetric decrypt when parsing SE IP packet"); + free(buf); + return -1; + } + + // Verify trailing MDC hash + + sz_preamble=arg->decrypt->blocksize+2; + sz_mdc_hash=OPS_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; + +#ifdef DEBUG + if (debug) + { + unsigned int i=0; + + fprintf(stderr,"\nplaintext (len=%ld): ",sz_plaintext); + for (i=0; iplaintext); + arg->plaintext=ops_mallocz(sz_plaintext); + memcpy(arg->plaintext, plaintext, sz_plaintext); + arg->plaintext_available=sz_plaintext; + + arg->passed_checks=1; + + free(buf); + } + + n=len; + if (n > arg->plaintext_available) + n=arg->plaintext_available; + + memcpy(dest_, arg->plaintext+arg->plaintext_offset, n); + arg->plaintext_available-=n; + arg->plaintext_offset+=n; + len-=n; + + return n; + } + +static void se_ip_data_destroyer(ops_reader_info_t *rinfo) + { + decrypt_se_ip_arg_t* arg=ops_reader_get_arg(rinfo); + free (arg->plaintext); + free (arg); + // free(ops_reader_get_arg(rinfo)); + } + +/** + \ingroup Internal_Readers_SEIP +*/ +void ops_reader_push_se_ip_data(ops_parse_info_t *pinfo, ops_crypt_t *decrypt, + ops_region_t *region) + { + decrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + arg->region=region; + arg->decrypt=decrypt; + + ops_reader_push(pinfo, se_ip_data_reader, se_ip_data_destroyer,arg); + } + +/** + \ingroup Internal_Readers_SEIP + */ +void ops_reader_pop_se_ip_data(ops_parse_info_t* pinfo) + { + // decrypt_se_ip_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + // free(arg); + ops_reader_pop(pinfo); + } + +// eof diff --git a/openpgpsdk/src/openpgpsdk/reader_fd.c b/openpgpsdk/src/openpgpsdk/reader_fd.c new file mode 100644 index 000000000..8681c65d7 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_fd.c @@ -0,0 +1,109 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +/** Arguments for reader_fd + */ +typedef struct + { + int fd; /*!< file descriptor */ + } reader_fd_arg_t; + +/** + * \ingroup Core_Readers + * + * ops_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 parse_info Gets cast to ops_reader_fd_arg_t + * + * \return OPS_R_EOF if no bytes were read + * \return OPS_R_PARTIAL_READ if not enough bytes were read, and OPS_RETURN_LENGTH is set in "flags" + * \return OPS_R_EARLY_EOF if not enough bytes were read, and OPS_RETURN_LENGTH was not set in "flags" + * \return OPS_R_OK if expected length was read + * \return OPS_R_ERROR if cannot read + * + * OPS_R_EARLY_EOF and OPS_R_ERROR push errors on the stack + * + * \sa enum opt_reader_ret_t + * + * \todo change arg_ to typesafe? + */ +static int fd_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + reader_fd_arg_t *arg=ops_reader_get_arg(rinfo); + int n=read(arg->fd,dest,length); + + OPS_USED(cbinfo); + + if(n == 0) + return 0; + + if(n < 0) + { + OPS_SYSTEM_ERROR_1(errors,OPS_E_R_READ_FAILED,"read", + "file descriptor %d",arg->fd); + return -1; + } + + return n; + } + +static void fd_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Core_Readers_First + \brief Starts stack with file reader +*/ + +void ops_reader_set_fd(ops_parse_info_t *pinfo,int fd) + { + reader_fd_arg_t *arg=malloc(sizeof *arg); + + arg->fd=fd; + ops_reader_set(pinfo,fd_reader,fd_destroyer,arg); + } + +// eof diff --git a/openpgpsdk/src/openpgpsdk/reader_hashed.c b/openpgpsdk/src/openpgpsdk/reader_hashed.c new file mode 100644 index 000000000..3cc6d2bc2 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_hashed.c @@ -0,0 +1,62 @@ +/* + * 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 +#include +#include + +#include + +static int hash_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + ops_hash_t *hash=ops_reader_get_arg(rinfo); + int r=ops_stacked_read(dest,length,errors,rinfo,cbinfo); + + if(r <= 0) + return r; + + hash->add(hash,dest,r); + + return r; + } + +/** + \ingroup Internal_Readers_Hash + \brief Push hashed data reader on stack +*/ +void ops_reader_push_hash(ops_parse_info_t *pinfo,ops_hash_t *hash) + { + hash->init(hash); + ops_reader_push(pinfo,hash_reader,NULL,hash); + } + +/** + \ingroup Internal_Readers_Hash + \brief Pop hashed data reader from stack +*/ +void ops_reader_pop_hash(ops_parse_info_t *pinfo) + { ops_reader_pop(pinfo); } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/reader_mem.c b/openpgpsdk/src/openpgpsdk/reader_mem.c new file mode 100644 index 000000000..2802673c8 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/reader_mem.c @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +typedef struct + { + const unsigned char *buffer; + size_t length; + size_t offset; + } reader_mem_arg_t; + +static int mem_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + reader_mem_arg_t *arg=ops_reader_get_arg(rinfo); + unsigned n; + + OPS_USED(cbinfo); + OPS_USED(errors); + + if(arg->offset+length > arg->length) + n=arg->length-arg->offset; + else + n=length; + + if(n == 0) + return 0; + + memcpy(dest,arg->buffer+arg->offset,n); + arg->offset+=n; + + return n; + } + +static void mem_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Core_Readers_First + \brief Starts stack with memory reader +*/ + +void ops_reader_set_memory(ops_parse_info_t *pinfo,const void *buffer, + size_t length) + { + reader_mem_arg_t *arg=malloc(sizeof *arg); + + arg->buffer=buffer; + arg->length=length; + arg->offset=0; + ops_reader_set(pinfo,mem_reader,mem_destroyer,arg); + } + +/* eof */ diff --git a/openpgpsdk/src/openpgpsdk/readerwriter.c b/openpgpsdk/src/openpgpsdk/readerwriter.c new file mode 100644 index 000000000..f29341cc7 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/readerwriter.c @@ -0,0 +1,494 @@ +/* + * 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 +#ifndef WIN32 +#include +#include +#else +#include +#endif +#include +#include +#include + +#include +#include + +#include "parse_local.h" + +#ifdef WIN32 +#include +#include + +#define PASS_MAX 512 + +char *getpass (const char *prompt) +{ + static char getpassbuf [PASS_MAX + 1]; + size_t i = 0; + int c; + + if (prompt) { + fputs (prompt, stderr); + fflush (stderr); + } + + for (;;) { + c = _getch (); + if (c == '\r') { + getpassbuf [i] = '\0'; + break; + } + else if (i < PASS_MAX) { + getpassbuf[i++] = c; + } + + if (i >= PASS_MAX) { + getpassbuf [i] = '\0'; + break; + } + } + + if (prompt) { + fputs ("\r\n", stderr); + fflush (stderr); + } + + return getpassbuf; +} +#endif + +/** + \ingroup Core_Writers + \brief Create and initialise cinfo and mem; Set for writing to mem + \param cinfo Address where new cinfo 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 cinfo and mem. + \sa ops_teardown_memory_write() +*/ +void ops_setup_memory_write(ops_create_info_t **cinfo, ops_memory_t **mem, size_t bufsz) + { + /* + * initialise needed structures for writing to memory + */ + + *cinfo=ops_create_info_new(); + *mem=ops_memory_new(); + + ops_memory_init(*mem,bufsz); + + ops_writer_set_memory(*cinfo,*mem); + } + +/** + \ingroup Core_Writers + \brief Closes writer and frees cinfo and mem + \param cinfo + \param mem + \sa ops_setup_memory_write() +*/ +void ops_teardown_memory_write(ops_create_info_t *cinfo, ops_memory_t *mem) + { + ops_writer_close(cinfo); // new + ops_create_info_delete(cinfo); + ops_memory_free(mem); + } + +/** + \ingroup Core_Readers + \brief Create parse_info and sets to read from memory + \param pinfo 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 false unless doing signature verification) + \note It is the caller's responsiblity to free parse_info + \sa ops_teardown_memory_read() +*/ +void ops_setup_memory_read(ops_parse_info_t **pinfo, ops_memory_t *mem, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), + ops_boolean_t accumulate) + { + /* + * initialise needed uctures for reading + */ + + *pinfo=ops_parse_info_new(); + ops_parse_cb_set(*pinfo,callback,arg); + ops_reader_set_memory(*pinfo, + ops_memory_get_data(mem), + ops_memory_get_length(mem)); + + if (accumulate) + (*pinfo)->rinfo.accumulate=ops_true; + } + +/** + \ingroup Core_Readers + \brief Frees pinfo and mem + \param pinfo + \param mem + \sa ops_setup_memory_read() +*/ +void ops_teardown_memory_read(ops_parse_info_t *pinfo, ops_memory_t *mem) + { + ops_parse_info_delete(pinfo); + ops_memory_free(mem); + } + +/** + \ingroup Core_Writers + \brief Create and initialise cinfo and mem; Set for writing to file + \param cinfo Address where new cinfo 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 cinfo and to close fd. + \sa ops_teardown_file_write() +*/ +int ops_setup_file_write(ops_create_info_t **cinfo, const char* filename, ops_boolean_t allow_overwrite) + { + int fd=0; + int flags=0; + + /* + * initialise needed structures for writing to file + */ + + flags=O_WRONLY | O_CREAT; + if (allow_overwrite==ops_true) + flags |= O_TRUNC; + else + flags |= O_EXCL; + flags |= O_BINARY; + + fd=open(filename, flags, 0600); + if(fd < 0) + { + perror(filename); + return fd; + } + + *cinfo=ops_create_info_new(); + + ops_writer_set_fd(*cinfo,fd); + + return fd; + } + +/** + \ingroup Core_Writers + \brief Closes writer, frees info, closes fd + \param cinfo + \param fd +*/ +void ops_teardown_file_write(ops_create_info_t *cinfo, int fd) + { + ops_writer_close(cinfo); + close(fd); + ops_create_info_delete(cinfo); + } + +/** + \ingroup Core_Writers + \brief As ops_setup_file_write, but appends to file +*/ +int ops_setup_file_append(ops_create_info_t **cinfo, const char* filename) + { + int fd; + /* + * initialise needed structures for writing to file + */ + + fd=open(filename,O_WRONLY | O_APPEND | O_BINARY | O_CREAT, 0600); + + if(fd < 0) + { + perror(filename); + return fd; + } + + *cinfo=ops_create_info_new(); + + ops_writer_set_fd(*cinfo,fd); + + return fd; + } + +/** + \ingroup Core_Writers + \brief As ops_teardown_file_write() +*/ +void ops_teardown_file_append(ops_create_info_t *cinfo, int fd) + { + ops_teardown_file_write(cinfo,fd); + } + +/** + \ingroup Core_Readers + \brief Creates parse_info, opens file, and sets to read from file + \param pinfo Address where new parse_info will be set + \param filename Name of file to read + \param arg Reader-specific arg + \param callback Callback to use when reading + \param accumulate Set if we need to accumulate as we read. (Usually false unless doing signature verification) + \note It is the caller's responsiblity to free parse_info and to close fd + \sa ops_teardown_file_read() +*/ + +int ops_setup_file_read(ops_parse_info_t **pinfo, const char *filename, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), + ops_boolean_t accumulate) + { + int fd=0; + /* + * initialise needed structures for reading + */ + + fd=open(filename,O_RDONLY | O_BINARY); + + if (fd < 0) + { + perror(filename); + return fd; + } + + *pinfo=ops_parse_info_new(); + ops_parse_cb_set(*pinfo,callback,arg); + ops_reader_set_fd(*pinfo,fd); + + if (accumulate) + (*pinfo)->rinfo.accumulate=ops_true; + + return fd; + } + +/** + \ingroup Core_Readers + \brief Frees pinfo and closes fd + \param pinfo + \param fd + \sa ops_setup_file_read() +*/ +void ops_teardown_file_read(ops_parse_info_t *pinfo, int fd) + { + close(fd); + ops_parse_info_delete(pinfo); + } + +ops_parse_cb_return_t +callback_literal_data(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + + // ops_print_packet(content_); + + // Read data from packet into static buffer + switch(content_->tag) + { + case OPS_PTAG_CT_LITERAL_DATA_BODY: + // if writer enabled, use it + if (cbinfo->cinfo) + { + ops_write(content->literal_data_body.data, + content->literal_data_body.length, + cbinfo->cinfo); + } + /* + ops_memory_add(mem_literal_data, + content->literal_data_body.data, + content->literal_data_body.length); + */ + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + // ignore + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +ops_parse_cb_return_t +callback_pk_session_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + + // ops_print_packet(content_); + + // Read data from packet into static buffer + switch(content_->tag) + { + case OPS_PTAG_CT_PK_SESSION_KEY: + // printf ("OPS_PTAG_CT_PK_SESSION_KEY\n"); + assert(cbinfo->cryptinfo.keyring); + cbinfo->cryptinfo.keydata=ops_keyring_find_key_by_id(cbinfo->cryptinfo.keyring, + content->pk_session_key.key_id); + if(!cbinfo->cryptinfo.keydata) + break; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_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 secret_key in the content struct +@endverbatim +*/ + +ops_parse_cb_return_t +callback_cmd_get_secret_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) +{ + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + const ops_secret_key_t *secret; + ops_parser_content_t pc; + + OPS_USED(cbinfo); + + // ops_print_packet(content_); + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SECRET_KEY: + cbinfo->cryptinfo.keydata=ops_keyring_find_key_by_id(cbinfo->cryptinfo.keyring,content->get_secret_key.pk_session_key->key_id); + if (!cbinfo->cryptinfo.keydata || !ops_is_key_secret(cbinfo->cryptinfo.keydata)) + return 0; + + /* now get the key from the data */ + secret=ops_get_secret_key_from_data(cbinfo->cryptinfo.keydata); + int tag_to_use = OPS_PARSER_CMD_GET_SK_PASSPHRASE ; + int nbtries = 0 ; + + while( (!secret) && nbtries++ < 3) + { + if (!cbinfo->cryptinfo.passphrase) + { + memset(&pc,'\0',sizeof pc); + pc.content.secret_key_passphrase.passphrase=&cbinfo->cryptinfo.passphrase; + CB(cbinfo,tag_to_use,&pc); + if (!cbinfo->cryptinfo.passphrase) + { + fprintf(stderr,"can't get passphrase\n"); + assert(0); + } + } + /* then it must be encrypted */ + secret=ops_decrypt_secret_key_from_data(cbinfo->cryptinfo.keydata,cbinfo->cryptinfo.passphrase); + + free(cbinfo->cryptinfo.passphrase) ; + cbinfo->cryptinfo.passphrase = NULL ; + tag_to_use = OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD ; + } + + if(!secret) + return 0 ; + + *content->get_secret_key.secret_key=secret; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; +} + +char *ops_get_passphrase(void) + { + return ops_malloc_passphrase(getpass("Passphrase: ")); + } + +char *ops_malloc_passphrase(char *pp) + { + char *passphrase; + size_t n; + + n=strlen(pp); + passphrase=malloc(n+1); + strncpy(passphrase,pp,n+1); + + return passphrase; + } + +/** + \ingroup HighLevel_Callbacks + \brief Callback to use when you need to prompt user for passphrase + \param content_ + \param cbinfo +*/ +ops_parse_cb_return_t +callback_cmd_get_passphrase_from_cmdline(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) + { + ops_parser_content_union_t* content=(ops_parser_content_union_t *)&content_->content; + + OPS_USED(cbinfo); + +// ops_print_packet(content_); + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + *(content->secret_key_passphrase.passphrase)=ops_get_passphrase(); + return OPS_KEEP_MEMORY; + break; + + default: + // return callback_general(content_,cbinfo); + break; + } + + return OPS_RELEASE_MEMORY; + } + +ops_boolean_t ops_reader_set_accumulate(ops_parse_info_t* pinfo, ops_boolean_t state) + { + pinfo->rinfo.accumulate=state; + return state; + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/readerwriter.h b/openpgpsdk/src/openpgpsdk/readerwriter.h new file mode 100644 index 000000000..6961bafef --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/readerwriter.h @@ -0,0 +1,93 @@ +/* + * 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 __OPS_READERWRITER_H__ +#define __OPS_READERWRITER_H__ + +#include +#include + + +void ops_reader_set_fd(ops_parse_info_t *pinfo,int fd); +void ops_reader_set_memory(ops_parse_info_t *pinfo,const void *buffer, + size_t length); + +// Do a sum mod 65536 of all bytes read (as needed for secret keys) +void ops_reader_push_sum16(ops_parse_info_t *pinfo); +unsigned short ops_reader_pop_sum16(ops_parse_info_t *pinfo); + +void ops_reader_push_se_ip_data(ops_parse_info_t *pinfo, ops_crypt_t *decrypt, + ops_region_t *region); +void ops_reader_pop_se_ip_data(ops_parse_info_t* pinfo); + +// +ops_boolean_t ops_write_mdc(const unsigned char *hashed, + ops_create_info_t* info); +ops_boolean_t ops_write_se_ip_pktset(const unsigned char *data, + const unsigned int len, + ops_crypt_t *crypt, + ops_create_info_t *info); +void ops_writer_push_encrypt_crypt(ops_create_info_t *cinfo, + ops_crypt_t *crypt); +void ops_writer_push_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key); +// Secret Key checksum + +void ops_push_skey_checksum_writer(ops_create_info_t *cinfo, ops_secret_key_t *skey); +ops_boolean_t ops_pop_skey_checksum_writer(ops_create_info_t *cinfo); + + +// memory writing +void ops_setup_memory_write(ops_create_info_t **cinfo, ops_memory_t **mem, size_t bufsz); +void ops_teardown_memory_write(ops_create_info_t *cinfo, ops_memory_t *mem); + +// memory reading +void ops_setup_memory_read(ops_parse_info_t **pinfo, ops_memory_t *mem, + void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *),ops_boolean_t accumulate); +void ops_teardown_memory_read(ops_parse_info_t *pinfo, ops_memory_t *mem); + +// file writing +int ops_setup_file_write(ops_create_info_t **cinfo, const char* filename, ops_boolean_t allow_overwrite); +void ops_teardown_file_write(ops_create_info_t *cinfo, int fd); + +// file appending +int ops_setup_file_append(ops_create_info_t **cinfo, const char* filename); +void ops_teardown_file_append(ops_create_info_t *cinfo, int fd); + +// file reading +int ops_setup_file_read(ops_parse_info_t **pinfo, const char *filename, void* arg, + ops_parse_cb_return_t callback(const ops_parser_content_t *, ops_parse_cb_info_t *), ops_boolean_t accumulate); +void ops_teardown_file_read(ops_parse_info_t *pinfo, int fd); + +ops_boolean_t ops_reader_set_accumulate(ops_parse_info_t* pinfo, ops_boolean_t state); + +// useful callbacks +ops_parse_cb_return_t +callback_literal_data(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_pk_session_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_cmd_get_secret_key(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); +ops_parse_cb_return_t +callback_cmd_get_passphrase_from_cmdline(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo); + +#endif /*OPS_READERWRITER_H__*/ diff --git a/openpgpsdk/src/openpgpsdk/signature.c b/openpgpsdk/src/openpgpsdk/signature.c new file mode 100644 index 000000000..7634df450 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/signature.c @@ -0,0 +1,1466 @@ +/* + * Copyright (c) 2005-2009 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer, Alasdair Mackintosh. + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static int debug=0; +#define MAXBUF 1024 /*info); + sig->info=NULL; + free(sig); + } + +static unsigned char prefix_md5[]={ 0x30,0x20,0x30,0x0C,0x06,0x08,0x2A,0x86, + 0x48,0x86,0xF7,0x0D,0x02,0x05,0x05,0x00, + 0x04,0x10 }; + +static unsigned char prefix_sha1[]={ 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0E, + 0x03,0x02,0x1A,0x05,0x00,0x04,0x14 }; + +static unsigned char prefix_sha224[]={ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, + 0x00, 0x04, 0x1C }; + +static unsigned char prefix_sha256[]={ 0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, + 0x00,0x04,0x20 }; + +static unsigned char prefix_sha384[]={ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30 }; + +static unsigned char prefix_sha512[]={ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40 }; + +static unsigned char prefix_ripemd[]={ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, + 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; +/** + \ingroup Core_Create + implementation of EMSA-PKCS1-v1_5, as defined in OpenPGP RFC + \param M + \param mLen + \param hash_alg Hash algorithm to use + \param EM + \return ops_true if OK; else ops_false +*/ +ops_boolean_t encode_hash_buf(const unsigned char *M, size_t mLen, + const ops_hash_algorithm_t hash_alg, + unsigned char* EM) + { + // implementation of EMSA-PKCS1-v1_5, as defined in OpenPGP RFC + + unsigned i; + int n; + ops_hash_t hash; + int hash_sz=0; + // int encoded_hash_sz=0; + int prefix_sz=0; + unsigned padding_sz=0; + unsigned encoded_msg_sz=0; + unsigned char* prefix=NULL; + + assert(hash_alg == OPS_HASH_SHA1); + + // 1. Apply hash function to M + + ops_hash_any(&hash, hash_alg); + hash.init(&hash); + hash.add(&hash, M, mLen); + + // \todo combine with rsa_sign + + // 2. Get hash prefix + + switch(hash_alg) + { + case OPS_HASH_SHA1: + prefix=prefix_sha1; + prefix_sz=sizeof prefix_sha1; + hash_sz=OPS_SHA1_HASH_SIZE; + // encoded_hash_sz=hash_sz+prefix_sz; + // \todo why is Ben using a PS size of 90 in rsa_sign? + // (keysize-hashsize-1-2) + padding_sz=90; + break; + + default: + assert(0); + } + + // \todo 3. Test for len being too short + + // 4 and 5. Generate PS and EM + + EM[0]=0x00; + EM[1]=0x01; + + for (i=0; in); + assert(keysize <= sizeof hashbuf); + assert(10+hashsize <= keysize); + + hashbuf[0]=0; + hashbuf[1]=1; + if (debug) + 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; + + memcpy(&hashbuf[n], prefix_sha1, sizeof prefix_sha1); + n+=sizeof prefix_sha1; + + t=hash->finish(hash, &hashbuf[n]); + assert(t == 20); + + ops_write(&hashbuf[n], 2, opt); + + n+=t; + assert(n == keysize); + + t=ops_rsa_private_encrypt(sigbuf, hashbuf, keysize, srsa, rsa); + bn=BN_bin2bn(sigbuf, t, NULL); + ops_write_mpi(bn, opt); + BN_free(bn); + } + +static void dsa_sign(ops_hash_t *hash, const ops_dsa_public_key_t *dsa, + const ops_dsa_secret_key_t *sdsa, ops_create_info_t *cinfo) + { + unsigned char hashbuf[8192]; + unsigned hashsize; + unsigned t; + + // 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]); + assert(t == 20); + + ops_write(&hashbuf[0], 2, cinfo); + + // write signature to buf + DSA_SIG* dsasig; + dsasig=ops_dsa_sign(hashbuf, hashsize, sdsa, dsa); + + // convert and write the sig out to memory + ops_write_mpi(dsasig->r, cinfo); + ops_write_mpi(dsasig->s, cinfo); + DSA_SIG_free(dsasig); + } + +static ops_boolean_t rsa_verify(ops_hash_algorithm_t type, + const unsigned char *hash, size_t hash_length, + const ops_rsa_signature_t *sig, + const ops_rsa_public_key_t *rsa) +{ + unsigned char sigbuf[8192]; + unsigned char hashbuf_from_sig[8192]; + unsigned n; + unsigned keysize; + unsigned char *prefix; + int plen; + + keysize=BN_num_bytes(rsa->n); + /* RSA key can't be bigger than 65535 bits, so... */ + assert(keysize <= sizeof hashbuf_from_sig); + assert((unsigned)BN_num_bits(sig->sig) <= 8*sizeof sigbuf); + BN_bn2bin(sig->sig, sigbuf); + + n=ops_rsa_public_decrypt(hashbuf_from_sig, sigbuf, BN_num_bytes(sig->sig), + rsa); + int debug_len_decrypted=n; + + if(n != keysize) // obviously, this includes error returns + return ops_false; + + // 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 ops_false; + + switch(type) + { + case OPS_HASH_MD5 : prefix=prefix_md5 ; plen=sizeof prefix_md5; break; + case OPS_HASH_SHA1 : prefix=prefix_sha1 ; plen=sizeof prefix_sha1; break; + case OPS_HASH_SHA224 : prefix=prefix_sha224 ; plen=sizeof prefix_sha224; break; + case OPS_HASH_SHA256 : prefix=prefix_sha256 ; plen=sizeof prefix_sha256; break; + case OPS_HASH_SHA384 : prefix=prefix_sha384 ; plen=sizeof prefix_sha384; break; + case OPS_HASH_SHA512 : prefix=prefix_sha512 ; plen=sizeof prefix_sha512; break; + case OPS_HASH_RIPEMD : prefix=prefix_ripemd ; plen=sizeof prefix_ripemd; break; + + default: + fprintf(stderr,"Warning: unhandled hash type in signature verification code: %d\n",type) ; + return ops_false ; + } + + if(keysize-plen-hash_length < 10) + return ops_false; + + for(n=2 ; n < keysize-plen-hash_length-1 ; ++n) + if(hashbuf_from_sig[n] != 0xff) + return ops_false; + + if(hashbuf_from_sig[n++] != 0) + return ops_false; + + if (debug) + { + int zz; + + printf("\n"); + printf("hashbuf_from_sig\n"); + for (zz=0; zzadd(hash, ops_memory_get_data(mem), l); + + ops_memory_free(mem); + } + +static void initialise_hash(ops_hash_t *hash, const ops_signature_t *sig) + { + ops_hash_any(hash, sig->info.hash_algorithm); + hash->init(hash); + } + +static void init_key_signature(ops_hash_t *hash, const ops_signature_t *sig, + const ops_public_key_t *key) + { + initialise_hash(hash, sig); + hash_add_key(hash, key); + } + +static void hash_add_trailer(ops_hash_t *hash, const ops_signature_t *sig, + const unsigned char *raw_packet) + { + if(sig->info.version == OPS_V4) + { + if(raw_packet) + hash->add(hash, raw_packet+sig->v4_hashed_data_start, + sig->info.v4_hashed_data_length); + ops_hash_add_int(hash, sig->info.version, 1); + ops_hash_add_int(hash, 0xff, 1); + ops_hash_add_int(hash, sig->info.v4_hashed_data_length, 4); + } + else + { + ops_hash_add_int(hash, sig->info.type, 1); + ops_hash_add_int(hash, sig->info.creation_time, 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 ops_true if good; else ops_false +*/ +ops_boolean_t ops_check_signature(const unsigned char *hash, unsigned length, + const ops_signature_t *sig, + const ops_public_key_t *signer) +{ + ops_boolean_t ret; + + /* + printf(" hash="); + // hashout[0]=0; + hexdump(hash,length); + */ + + switch(sig->info.key_algorithm) + { + case OPS_PKA_DSA: + ret=ops_dsa_verify(hash, length, &sig->info.signature.dsa, + &signer->key.dsa); + /* fprintf(stderr,"Cannot verify DSA signature. skipping.\n") ; + ret = ops_false ; */ + break; + + case OPS_PKA_RSA: + ret=rsa_verify(sig->info.hash_algorithm, hash, length, + &sig->info.signature.rsa, &signer->key.rsa); + break; + + default: + fprintf(stderr,"Cannot verify signature. Unknown key signing algorithm %d. skipping.\n",sig->info.key_algorithm) ; + ret = ops_false ; + + } + + return ret; +} + +static ops_boolean_t hash_and_check_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer) + { + int n; + unsigned char hashout[OPS_MAX_HASH_SIZE]; + + n=hash->finish(hash, hashout); + + return ops_check_signature(hashout, n, sig, signer); + } + +static ops_boolean_t finalise_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + hash_add_trailer(hash, sig, raw_packet); + return hash_and_check_signature(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 ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_user_id_certification_signature(const ops_public_key_t *key, + const ops_user_id_t *id, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + size_t user_id_len=strlen((char *)id->user_id); + + init_key_signature(&hash, sig, key); + + if(sig->info.version == OPS_V4) + { + ops_hash_add_int(&hash, 0xb4, 1); + ops_hash_add_int(&hash, user_id_len, 4); + } + hash.add(&hash, id->user_id, user_id_len); + + return finalise_signature(&hash, sig, signer, raw_packet); + } + +/** + * \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 ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_user_attribute_certification_signature(const ops_public_key_t *key, + const ops_user_attribute_t *attribute, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash, sig, key); + + if(sig->info.version == OPS_V4) + { + ops_hash_add_int(&hash, 0xd1, 1); + ops_hash_add_int(&hash, attribute->data.len, 4); + } + hash.add(&hash, attribute->data.contents, attribute->data.len); + + return finalise_signature(&hash, sig, signer, raw_packet); + } + +/** + * \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 ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_subkey_signature(const ops_public_key_t *key, + const ops_public_key_t *subkey, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash, sig, key); + hash_add_key(&hash, subkey); + + return finalise_signature(&hash, sig, signer, raw_packet); + } + +/** + * \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 ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_direct_signature(const ops_public_key_t *key, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet) + { + ops_hash_t hash; + + init_key_signature(&hash, sig, key); + return finalise_signature(&hash, sig, signer, raw_packet); + } + +/** + * \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 ops_true if OK; else ops_false + */ +ops_boolean_t +ops_check_hash_signature(ops_hash_t *hash, const ops_signature_t *sig, + const ops_public_key_t *signer) + { + if(sig->info.hash_algorithm != hash->algorithm) + return ops_false; + + return finalise_signature(hash, sig, signer, NULL); + } + +static void start_signature_in_mem(ops_create_signature_t *sig) + { + // since this has subpackets and stuff, we have to buffer the whole + // thing to get counts before writing. + sig->mem=ops_memory_new(); + ops_memory_init(sig->mem, 100); + ops_writer_set_memory(sig->info, sig->mem); + + // write nearly up to the first subpacket + ops_write_scalar(sig->sig.info.version, 1, sig->info); + ops_write_scalar(sig->sig.info.type, 1, sig->info); + ops_write_scalar(sig->sig.info.key_algorithm, 1, sig->info); + ops_write_scalar(sig->sig.info.hash_algorithm, 1, sig->info); + + // dummy hashed subpacket count + sig->hashed_count_offset=ops_memory_get_length(sig->mem); + ops_write_scalar(0, 2, sig->info); + } + +/** + * \ingroup Core_Signature + * + * ops_signature_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 ops_signature_start_key_signature(ops_create_signature_t *sig, + const ops_public_key_t *key, + const ops_user_id_t *id, + ops_sig_type_t type) + { + sig->info=ops_create_info_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=OPS_V4; + sig->sig.info.hash_algorithm=OPS_HASH_SHA1; + sig->sig.info.key_algorithm=key->algorithm; + sig->sig.info.type=type; + + sig->hashed_data_length=-1; + + init_key_signature(&sig->hash, &sig->sig, key); + + ops_hash_add_int(&sig->hash, 0xb4, 1); + ops_hash_add_int(&sig->hash, strlen((char *)id->user_id), 4); + sig->hash.add(&sig->hash, id->user_id, strlen((char *)id->user_id)); + + start_signature_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. + */ + +static void ops_signature_start_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + sig->info=ops_create_info_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=OPS_V4; + sig->sig.info.key_algorithm=key->public_key.algorithm; + sig->sig.info.hash_algorithm=hash; + sig->sig.info.type=type; + + sig->hashed_data_length=-1; + + if (debug) + { fprintf(stderr, "initialising hash for sig in mem\n"); } + initialise_hash(&sig->hash, &sig->sig); + start_signature_in_mem(sig); + } + +/** + * \ingroup Core_Signature + * \brief Setup to start a cleartext's signature + */ +void ops_signature_start_cleartext_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + ops_signature_start_signature(sig, key, hash, type); + } + +/** + * \ingroup Core_Signature + * \brief Setup to start a message's signature + */ +void ops_signature_start_message_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type) + { + ops_signature_start_signature(sig, key, hash, type); + } + +/** + * \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 ops_signature_add_data(ops_create_signature_t *sig, const void *buf, + size_t length) + { + if (debug) + { fprintf(stderr, "ops_signature_add_data adds to hash\n"); } + sig->hash.add(&sig->hash, buf, length); + } + +/** + * \ingroup Core_Signature + * + * Mark the end of the hashed subpackets in the signature + * + * \param sig + */ + +ops_boolean_t ops_signature_hashed_subpackets_end(ops_create_signature_t *sig) + { + sig->hashed_data_length=ops_memory_get_length(sig->mem) + -sig->hashed_count_offset-2; + ops_memory_place_int(sig->mem, sig->hashed_count_offset, + sig->hashed_data_length, 2); + // dummy unhashed subpacket count + sig->unhashed_count_offset=ops_memory_get_length(sig->mem); + return ops_write_scalar(0, 2, sig->info); + } + +/** + * \ingroup Core_Signature + * + * Write out a signature + * + * \param sig + * \param key + * \param skey + * \param info + * + */ + +ops_boolean_t ops_write_signature(ops_create_signature_t *sig, + const ops_public_key_t *key, + const ops_secret_key_t *skey, + ops_create_info_t *info) +{ + ops_boolean_t rtn=ops_false; + size_t l=ops_memory_get_length(sig->mem); + + // check key not decrypted + switch (skey->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + assert(skey->key.rsa.d); + break; + + case OPS_PKA_DSA: + assert(skey->key.dsa.x); + break; + + default: + fprintf(stderr, "Unsupported algorithm %d\n", + skey->public_key.algorithm); + assert(0); + } + + assert(sig->hashed_data_length != (unsigned)-1); + + ops_memory_place_int(sig->mem, sig->unhashed_count_offset, + l-sig->unhashed_count_offset-2, 2); + + // add the packet from version number to end of hashed subpackets + + if (debug) + { fprintf(stderr, "--- Adding packet to hash from version number to" + " hashed subpkts\n"); } + + sig->hash.add(&sig->hash, ops_memory_get_data(sig->mem), + sig->unhashed_count_offset); + + // add final trailer + ops_hash_add_int(&sig->hash, sig->sig.info.version, 1); + ops_hash_add_int(&sig->hash, 0xff, 1); + // +6 for version, type, pk alg, hash alg, hashed subpacket length + ops_hash_add_int(&sig->hash, sig->hashed_data_length+6, 4); + + if (debug) + { fprintf(stderr, "--- Finished adding packet to hash from version" + " number to hashed subpkts\n"); } + + // XXX: technically, we could figure out how big the signature is + // and write it directly to the output instead of via memory. + switch(skey->public_key.algorithm) + { + case OPS_PKA_RSA: + case OPS_PKA_RSA_ENCRYPT_ONLY: + case OPS_PKA_RSA_SIGN_ONLY: + rsa_sign(&sig->hash, &key->key.rsa, &skey->key.rsa, sig->info); + break; + + case OPS_PKA_DSA: + dsa_sign(&sig->hash, &key->key.dsa, &skey->key.dsa, sig->info); + break; + + default: + fprintf(stderr, "Unsupported algorithm %d\n", + skey->public_key.algorithm); + assert(0); + } + + rtn=ops_write_ptag(OPS_PTAG_CT_SIGNATURE, info); + if (rtn) + { + l=ops_memory_get_length(sig->mem); + rtn = ops_write_length(l, info) + && ops_write(ops_memory_get_data(sig->mem), l, info); + } + + ops_memory_free(sig->mem); + + if (!rtn) + OPS_ERROR(&info->errors, OPS_E_W, "Cannot write signature"); + return rtn; +} + +/** + * \ingroup Core_Signature + * + * ops_signature_add_creation_time() adds a creation time to the signature. + * + * \param sig + * \param when + */ +ops_boolean_t ops_signature_add_creation_time(ops_create_signature_t *sig, + time_t when) + { + return ops_write_ss_header(5, OPS_PTAG_SS_CREATION_TIME, sig->info) + && ops_write_scalar(when, 4, sig->info); + } + +/** + * \ingroup Core_Signature + * + * Adds issuer's key ID to the signature + * + * \param sig + * \param keyid + */ + +ops_boolean_t +ops_signature_add_issuer_key_id(ops_create_signature_t *sig, + const unsigned char keyid[OPS_KEY_ID_SIZE]) + { + return ops_write_ss_header(OPS_KEY_ID_SIZE+1, OPS_PTAG_SS_ISSUER_KEY_ID, + sig->info) + && ops_write(keyid, OPS_KEY_ID_SIZE, sig->info); + } + +/** + * \ingroup Core_Signature + * + * Adds primary user ID to the signature + * + * \param sig + * \param primary + */ +void ops_signature_add_primary_user_id(ops_create_signature_t *sig, + ops_boolean_t primary) + { + ops_write_ss_header(2, OPS_PTAG_SS_PRIMARY_USER_ID, sig->info); + ops_write_scalar(primary, 1, sig->info); + } + +/** + * \ingroup Core_Signature + * + * Get the hash structure in use for the signature. + * + * \param sig The signature structure. + * \return The hash structure. + */ +ops_hash_t *ops_signature_get_hash(ops_create_signature_t *sig) + { return &sig->hash; } + +static int open_output_file(ops_create_info_t **cinfo, + const char* input_filename, + const char* output_filename, + const ops_boolean_t use_armour, + const ops_boolean_t overwrite) + { + int fd_out; + + // setup output file + + if (output_filename) + fd_out=ops_setup_file_write(cinfo, output_filename, overwrite); + else + { + char *myfilename=NULL; + unsigned filenamelen=strlen(input_filename)+4+1; + myfilename=ops_mallocz(filenamelen); + if (use_armour) + snprintf(myfilename, filenamelen, "%s.asc", input_filename); + else + snprintf(myfilename, filenamelen, "%s.gpg", input_filename); + fd_out=ops_setup_file_write(cinfo, myfilename, overwrite); + free(myfilename); + } + + return fd_out; + } + +/** + \ingroup HighLevel_Sign + \brief Sign a file with a Cleartext Signature + \param input_filename Name of file to be signed + \param output_filename Filename to be created. If NULL, filename will be constructed from the input_filename. + \param skey Secret Key to sign with + \param overwrite Allow output file to be overwritten, if set + \return ops_true if OK, else ops_false + + Example code: + \code + void example(const ops_secret_key_t *skey, ops_boolean_t overwrite) + { + if (ops_sign_file_as_cleartext("mytestfile.txt",NULL,skey,overwrite)==ops_true) + printf("OK"); + else + printf("ERR"); + } + \endcode +*/ +ops_boolean_t ops_sign_file_as_cleartext(const char* input_filename, + const char* output_filename, + const ops_secret_key_t *skey, + const ops_boolean_t overwrite) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + int fd_in=0; + int fd_out=0; + ops_create_info_t *cinfo=NULL; + unsigned char buf[MAXBUF]; + //int flags=0; + ops_boolean_t rtn=ops_false; + ops_boolean_t use_armour=ops_true; + + // open file to sign + + fd_in=open(input_filename, O_RDONLY | O_BINARY); + + if(fd_in < 0) + { + return ops_false; + } + + // set up output file + + fd_out=open_output_file(&cinfo, input_filename, output_filename, use_armour, + overwrite); + + if (fd_out < 0) + { + close(fd_in); + return ops_false; + } + + // set up signature + sig=ops_create_signature_new(); + if (!sig) + { + close (fd_in); + ops_teardown_file_write(cinfo, fd_out); + return ops_false; + } + + // \todo could add more error detection here + ops_signature_start_cleartext_signature(sig, skey, + OPS_HASH_SHA1, OPS_SIG_BINARY); + if (!ops_writer_push_clearsigned(cinfo, sig)) + return ops_false; + + // Do the signing + + for (;;) + { + int n=0; + + n=read(fd_in, buf, sizeof(buf)); + if (!n) + break; + assert(n>=0); + ops_write(buf, n, cinfo); + } + close(fd_in); + + // add signature with subpackets: + // - creation time + // - key id + rtn = ops_writer_switch_to_armoured_signature(cinfo) + && ops_signature_add_creation_time(sig, time(NULL)); + if (!rtn) + { + ops_teardown_file_write(cinfo, fd_out); + return ops_false; + } + + ops_keyid(keyid, &skey->public_key); + + rtn = ops_signature_add_issuer_key_id(sig, keyid) + && ops_signature_hashed_subpackets_end(sig) + && ops_write_signature(sig, &skey->public_key, skey, cinfo); + + ops_teardown_file_write(cinfo, fd_out); + + if (!rtn) + OPS_ERROR(&cinfo->errors, OPS_E_W, "Cannot sign file as cleartext"); + return rtn; + } + + +/** + * \ingroup HighLevel_Sign + * \brief Sign a buffer with a Cleartext signature + * \param cleartext Text to be signed + * \param len Length of text + * \param signed_cleartext ops_memory_t struct in which to write the signed cleartext + * \param skey Secret key with which to sign the cleartext + * \return ops_true if OK; else ops_false + + * \note It is the calling function's responsibility to free signed_cleartext + * \note signed_cleartext should be a NULL pointer when passed in + + Example code: + \code + void example(const ops_secret_key_t *skey) + { + ops_memory_t* mem=NULL; + const char* buf="Some example text"; + size_t len=strlen(buf); + if (ops_sign_buf_as_cleartext(buf,len, &mem, skey)==ops_true) + printf("OK"); + else + printf("ERR"); + // free signed cleartext after use + ops_memory_free(mem); + } + \endcode + */ +ops_boolean_t ops_sign_buf_as_cleartext(const char* cleartext, const size_t len, + ops_memory_t** signed_cleartext, + const ops_secret_key_t *skey) + { + ops_boolean_t rtn=ops_false; + + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + ops_create_info_t *cinfo=NULL; + + assert(*signed_cleartext == NULL); + + // set up signature + sig=ops_create_signature_new(); + if (!sig) + return ops_false; + + // \todo could add more error detection here + ops_signature_start_cleartext_signature(sig, skey, OPS_HASH_SHA1, + OPS_SIG_BINARY); + + // set up output file + ops_setup_memory_write(&cinfo, signed_cleartext, len); + + // Do the signing + // add signature with subpackets: + // - creation time + // - key id + rtn = ops_writer_push_clearsigned(cinfo, sig) + && ops_write(cleartext, len, cinfo) + && ops_writer_switch_to_armoured_signature(cinfo) + && ops_signature_add_creation_time(sig, time(NULL)); + + if (!rtn) + return ops_false; + + ops_keyid(keyid, &skey->public_key); + + rtn = ops_signature_add_issuer_key_id(sig, keyid) + && ops_signature_hashed_subpackets_end(sig) + && ops_write_signature(sig, &skey->public_key, skey, cinfo) + && ops_writer_close(cinfo); + + // Note: the calling function must free signed_cleartext + ops_create_info_delete(cinfo); + + return rtn; + } + +/** +\ingroup HighLevel_Sign +\brief Sign a file +\param input_filename Input filename +\param output_filename Output filename. If NULL, a name is constructed from the input filename. +\param skey Secret Key to use for signing +\param use_armour Write armoured text, if set. +\param overwrite May overwrite existing file, if set. +\return ops_true if OK; else ops_false; + +Example code: +\code +void example(const ops_secret_key_t *skey) +{ + const char* filename="mytestfile"; + const ops_boolean_t use_armour=ops_false; + const ops_boolean_t overwrite=ops_false; + if (ops_sign_file(filename, NULL, skey, use_armour, overwrite)==ops_true) + printf("OK"); + else + printf("ERR"); +} +\endcode +*/ +ops_boolean_t ops_sign_file(const char* input_filename, + const char* output_filename, + const ops_secret_key_t *skey, + const ops_boolean_t use_armour, + const ops_boolean_t overwrite) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + int fd_out=0; + ops_create_info_t *cinfo=NULL; + + ops_hash_algorithm_t hash_alg=OPS_HASH_SHA1; + ops_sig_type_t sig_type=OPS_SIG_BINARY; + + ops_memory_t* mem_buf=NULL; + ops_hash_t* hash=NULL; + + // read input file into buf + + int errnum; + mem_buf=ops_write_mem_from_file(input_filename, &errnum); + if (errnum) + return ops_false; + + // setup output file + + fd_out=open_output_file(&cinfo, input_filename, output_filename, use_armour, + overwrite); + + if (fd_out < 0) + { + ops_memory_free(mem_buf); + return ops_false; + } + + // set up signature + sig=ops_create_signature_new(); + ops_signature_start_message_signature(sig, skey, hash_alg, sig_type); + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + if (debug) + { fprintf(stderr, "** Writing out one pass sig\n"); } + + // write one_pass_sig + ops_write_one_pass_sig(skey, hash_alg, sig_type, cinfo); + + // hash file contents + hash=ops_signature_get_hash(sig); + hash->add(hash, ops_memory_get_data(mem_buf), + ops_memory_get_length(mem_buf)); + + // output file contents as Literal Data packet + + if (debug) + fprintf(stderr,"** Writing out data now\n"); + + ops_write_literal_data_from_buf(ops_memory_get_data(mem_buf), + ops_memory_get_length(mem_buf), + OPS_LDT_BINARY, cinfo); + + if (debug) + fprintf(stderr, "** After Writing out data now\n"); + + // add subpackets to signature + // - creation time + // - key id + + ops_signature_add_creation_time(sig, time(NULL)); + + ops_keyid(keyid, &skey->public_key); + ops_signature_add_issuer_key_id(sig, keyid); + + ops_signature_hashed_subpackets_end(sig); + + // write out sig + ops_write_signature(sig, &skey->public_key, skey, cinfo); + + ops_teardown_file_write(cinfo, fd_out); + + // tidy up + ops_create_signature_delete(sig); + ops_memory_free(mem_buf); + + return ops_true; + } + +/** +\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 skey Secret Key +\param use_armour Write armoured text, if set +\param include_data Includes the signed data in the output message. If not, creates a detached signature. +\return New ops_memory_t struct containing signed text +\note It is the caller's responsibility to call ops_memory_free(me) + +Example Code: +\code +void example(const ops_secret_key_t *skey) +{ + const char* buf="Some example text"; + const size_t len=strlen(buf); + const ops_boolean_t use_armour=ops_true; + + ops_memory_t* mem=NULL; + + mem=ops_sign_buf(buf,len,OPS_SIG_BINARY,skey,use_armour); + if (mem) + { + printf ("OK"); + ops_memory_free(mem); + } + else + { + printf("ERR"); + } +} +\endcode +*/ +ops_memory_t* ops_sign_buf(const void* input, const size_t input_len, + const ops_sig_type_t sig_type, + const ops_secret_key_t *skey, + const ops_boolean_t use_armour, + ops_boolean_t include_data) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + unsigned char keyid[OPS_KEY_ID_SIZE]; + ops_create_signature_t *sig=NULL; + + ops_create_info_t *cinfo=NULL; + ops_memory_t *mem=ops_memory_new(); + + ops_hash_algorithm_t hash_alg=OPS_HASH_SHA1; + ops_literal_data_type_t ld_type; + ops_hash_t* hash=NULL; + + // setup literal data packet type + if (sig_type == OPS_SIG_BINARY) + ld_type=OPS_LDT_BINARY; + else + ld_type=OPS_LDT_TEXT; + + // set up signature + sig=ops_create_signature_new(); + ops_signature_start_message_signature(sig, skey, hash_alg, sig_type); + + // setup writer + ops_setup_memory_write(&cinfo, &mem, input_len); + + // set armoured/not armoured here + if (use_armour) + ops_writer_push_armoured_message(cinfo); + + if (debug) + fprintf(stderr, "** Writing out one pass sig\n"); + + // write one_pass_sig + if(include_data) + ops_write_one_pass_sig(skey, hash_alg, sig_type, cinfo); + + // hash file contents + hash=ops_signature_get_hash(sig); + hash->add(hash, input, input_len); + + // output file contents as Literal Data packet + + if (debug) + fprintf(stderr,"** Writing out data now\n"); + + if(include_data) + ops_write_literal_data_from_buf(input, input_len, ld_type, cinfo); + + if (debug) + fprintf(stderr,"** After Writing out data now\n"); + + // add subpackets to signature + // - creation time + // - key id + + ops_signature_add_creation_time(sig, time(NULL)); + + ops_keyid(keyid, &skey->public_key); + ops_signature_add_issuer_key_id(sig, keyid); + + ops_signature_hashed_subpackets_end(sig); + + // write out sig + ops_write_signature(sig, &skey->public_key, skey, cinfo); + + // tidy up + ops_writer_close(cinfo); + free(cinfo) ; + ops_create_signature_delete(sig); + + return mem; + } + +typedef struct { + ops_create_signature_t *signature; + const ops_secret_key_t *skey; + ops_hash_algorithm_t hash_alg; + ops_sig_type_t sig_type; +} signature_arg_t; + +static ops_boolean_t stream_signature_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + signature_arg_t* arg = ops_writer_get_arg(winfo); + // Add the input data to the hash. At the end, we will use the hash + // to generate a signature packet. + ops_hash_t* hash = ops_signature_get_hash(arg->signature); + hash->add(hash, src, length); + + return ops_stacked_write(src, length, errors, winfo); + } + +static ops_boolean_t stream_signature_write_trailer(ops_create_info_t *cinfo, + void *data) + { + signature_arg_t* arg = data; + unsigned char keyid[OPS_KEY_ID_SIZE]; + + // add subpackets to signature + // - creation time + // - key id + ops_signature_add_creation_time(arg->signature,time(NULL)); + ops_keyid(keyid, &arg->skey->public_key); + ops_signature_add_issuer_key_id(arg->signature, keyid); + ops_signature_hashed_subpackets_end(arg->signature); + + // write out signature + return ops_write_signature(arg->signature, &arg->skey->public_key, + arg->skey, cinfo); + } + +static void stream_signature_destroyer(ops_writer_info_t *winfo) + { + signature_arg_t* arg = ops_writer_get_arg(winfo); + ops_create_signature_delete(arg->signature); + free(arg); + } + +/** +\ingroup Core_WritePackets +\brief Pushes a signed writer onto the stack. + +Data written will be encoded as a onepass signature packet, followed +by a literal packet, followed by a signature packet. Once this writer +has been added to the stack, cleartext can be written straight to the +output, and it will be encoded as a literal packet and signed. + +\param cinfo Write settings +\param sig_type the type of input to be signed (text or binary) +\param skey the key used to sign the stream. +\return false if the initial onepass packet could not be created. +*/ +ops_boolean_t ops_writer_push_signed(ops_create_info_t *cinfo, + const ops_sig_type_t sig_type, + const ops_secret_key_t *skey) + { + // \todo allow choice of hash algorithams + // enforce use of SHA1 for now + + // Create arg to be used with this writer + // Remember to free this in the destroyer + signature_arg_t *signature_arg = ops_mallocz(sizeof *signature_arg); + signature_arg->signature = ops_create_signature_new(); + signature_arg->hash_alg = OPS_HASH_SHA1; + signature_arg->skey = skey; + signature_arg->sig_type = sig_type; + ops_signature_start_message_signature(signature_arg->signature, + signature_arg->skey, + signature_arg->hash_alg, + signature_arg->sig_type); + + if (!ops_write_one_pass_sig(signature_arg->skey, + signature_arg->hash_alg, + signature_arg->sig_type, + cinfo)) + return ops_false; + + ops_writer_push_partial_with_trailer(0, cinfo, OPS_PTAG_CT_LITERAL_DATA, + write_literal_header, NULL, + stream_signature_write_trailer, + signature_arg); + // And push writer on stack + ops_writer_push(cinfo, stream_signature_writer, NULL, + stream_signature_destroyer,signature_arg); + return ops_true; + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/signature.h b/openpgpsdk/src/openpgpsdk/signature.h new file mode 100644 index 000000000..0a8db71ea --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/signature.h @@ -0,0 +1,96 @@ +/* + * 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 OPS_SIGNATURE_H +#define OPS_SIGNATURE_H + +#include "packet.h" +#include "util.h" +#include "create.h" + +typedef struct ops_create_signature ops_create_signature_t; + +ops_create_signature_t *ops_create_signature_new(void); +void ops_create_signature_delete(ops_create_signature_t *sig); + +ops_boolean_t +ops_check_user_id_certification_signature(const ops_public_key_t *key, + const ops_user_id_t *id, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_user_attribute_certification_signature(const ops_public_key_t *key, + const ops_user_attribute_t *attribute, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_subkey_signature(const ops_public_key_t *key, + const ops_public_key_t *subkey, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_direct_signature(const ops_public_key_t *key, + const ops_signature_t *sig, + const ops_public_key_t *signer, + const unsigned char *raw_packet); +ops_boolean_t +ops_check_hash_signature(ops_hash_t *hash, + const ops_signature_t *sig, + const ops_public_key_t *signer); +void ops_signature_start_key_signature(ops_create_signature_t *sig, + const ops_public_key_t *key, + const ops_user_id_t *id, + ops_sig_type_t type); +void ops_signature_start_cleartext_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type); +void ops_signature_start_message_signature(ops_create_signature_t *sig, + const ops_secret_key_t *key, + const ops_hash_algorithm_t hash, + const ops_sig_type_t type); + +void ops_signature_add_data(ops_create_signature_t *sig,const void *buf, + size_t length); +ops_hash_t *ops_signature_get_hash(ops_create_signature_t *sig); +ops_boolean_t ops_signature_hashed_subpackets_end(ops_create_signature_t *sig); +ops_boolean_t ops_write_signature(ops_create_signature_t *sig,const ops_public_key_t *key, + const ops_secret_key_t *skey, ops_create_info_t *opt); +ops_boolean_t ops_signature_add_creation_time(ops_create_signature_t *sig,time_t when); +ops_boolean_t ops_signature_add_issuer_key_id(ops_create_signature_t *sig, + const unsigned char keyid[OPS_KEY_ID_SIZE]); +void ops_signature_add_primary_user_id(ops_create_signature_t *sig, + ops_boolean_t primary); + +// Standard Interface +ops_boolean_t ops_sign_file_as_cleartext(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t overwrite); +ops_boolean_t ops_sign_buf_as_cleartext(const char* input, const size_t len, ops_memory_t** output, const ops_secret_key_t *skey); +ops_boolean_t ops_sign_file(const char* input_filename, const char* output_filename, const ops_secret_key_t *skey, const ops_boolean_t use_armour, const ops_boolean_t overwrite); +ops_memory_t * ops_sign_buf(const void* input, const size_t input_len, const ops_sig_type_t sig_type, const ops_secret_key_t *skey, const ops_boolean_t use_armour,ops_boolean_t include_data); +ops_boolean_t ops_writer_push_signed(ops_create_info_t *cinfo, const ops_sig_type_t sig_type, const ops_secret_key_t *skey); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/std_print.h b/openpgpsdk/src/openpgpsdk/std_print.h new file mode 100644 index 000000000..f6ae7dba1 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/std_print.h @@ -0,0 +1,47 @@ +/* + * 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 OPS_STD_PRINT_H +#define OPS_STD_PRINT_H + +#include "openpgpsdk/packet.h" +#include "openpgpsdk/packet-parse.h" +#include "openpgpsdk/keyring.h" + +void print_bn( const char *name, + const BIGNUM *bn); +void ops_print_pk_session_key(ops_content_tag_t tag, + const ops_pk_session_key_t *key); +void ops_print_public_keydata(const ops_keydata_t *key); + +void ops_print_public_keydata_verbose(const ops_keydata_t *key); +void ops_print_public_key(const ops_public_key_t *pkey); + +void ops_print_secret_keydata(const ops_keydata_t *key); +void ops_print_secret_keydata_verbose(const ops_keydata_t *key); +//void ops_print_secret_key(const ops_content_tag_t type, const ops_secret_key_t* skey); +int ops_print_packet(const ops_parser_content_t *content_); +void ops_list_packets(char *filename, ops_boolean_t armour, ops_keyring_t* pubring, ops_parse_cb_t* cb_get_passphrase); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/streamwriter.h b/openpgpsdk/src/openpgpsdk/streamwriter.h new file mode 100644 index 000000000..70f77990d --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/streamwriter.h @@ -0,0 +1,30 @@ +/* + * 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 __OPS_STREAMWRITER_H__ +#define __OPS_STREAMWRITER_H__ + +#include + +void ops_writer_push_stream_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key); + +#endif /*__OPS_STREAMWRITER_H__*/ diff --git a/openpgpsdk/src/openpgpsdk/symmetric.c b/openpgpsdk/src/openpgpsdk/symmetric.c new file mode 100644 index 000000000..30dc0b74b --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/symmetric.c @@ -0,0 +1,683 @@ +/* + * 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 +#include +#include +#include +#ifndef OPENSSL_NO_IDEA +# include +#endif +#include +#ifndef OPENSSL_NO_CAMELLIA +# include +#endif +#include +#include "parse_local.h" + +#include +#include + +//static int debug=0; + +#ifndef ATTRIBUTE_UNUSED + +#ifndef WIN32 +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#else +#define ATTRIBUTE_UNUSED +#endif // #ifndef WIN32 + +#endif /* ATTRIBUTE_UNUSED */ + +static void std_set_iv(ops_crypt_t *crypt,const unsigned char *iv) + { + memcpy(crypt->iv,iv,crypt->blocksize); + crypt->num=0; + } + +static void std_set_key(ops_crypt_t *crypt,const unsigned char *key) + { memcpy(crypt->key,key,crypt->keysize); } + +static void std_resync(ops_crypt_t *decrypt) + { + if(decrypt->num == decrypt->blocksize) + return; + + memmove(decrypt->civ+decrypt->blocksize-decrypt->num,decrypt->civ, + decrypt->num); + memcpy(decrypt->civ,decrypt->siv+decrypt->num, + decrypt->blocksize-decrypt->num); + decrypt->num=0; + } + +static void std_finish(ops_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 void cast5_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(CAST_KEY)); + CAST_set_key(crypt->encrypt_key,crypt->keysize,crypt->key); + crypt->decrypt_key=malloc(sizeof(CAST_KEY)); + CAST_set_key(crypt->decrypt_key,crypt->keysize,crypt->key); + } + +static void cast5_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { CAST_ecb_encrypt(in,out,crypt->encrypt_key,CAST_ENCRYPT); } + +static void cast5_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { CAST_ecb_encrypt(in,out,crypt->encrypt_key,CAST_DECRYPT); } + +static void cast5_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + CAST_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_ENCRYPT); + } + +static void cast5_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + CAST_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_DECRYPT); + } + +#define TRAILER "","","","",0,NULL,NULL + +static ops_crypt_t cast5= + { + OPS_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 void idea_init(ops_crypt_t *crypt) + { + assert(crypt->keysize == IDEA_KEY_LENGTH); + + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(IDEA_KEY_SCHEDULE)); + + // 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); + crypt->decrypt_key=malloc(sizeof(IDEA_KEY_SCHEDULE)); + + idea_set_decrypt_key(crypt->encrypt_key,crypt->decrypt_key); + } + +static void idea_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { idea_ecb_encrypt(in,out,crypt->encrypt_key); } + +static void idea_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { idea_ecb_encrypt(in,out,crypt->decrypt_key); } + +static void idea_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + idea_cfb64_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAST_ENCRYPT); + } + +static void idea_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + idea_cfb64_encrypt(in,out,count, + crypt->decrypt_key, crypt->iv, (int *)&crypt->num, + CAST_DECRYPT); + } + +static const ops_crypt_t idea= + { + OPS_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 void aes128_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(AES_KEY)); + 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); + crypt->decrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_decrypt_key(crypt->key,KEYBITS_AES128,crypt->decrypt_key)) + fprintf(stderr,"aes128_init: Error setting decrypt_key\n"); + } + +static void aes_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { AES_encrypt(in,out,crypt->encrypt_key); } + +static void aes_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { AES_decrypt(in,out,crypt->decrypt_key); } + +static void aes_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + AES_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + AES_ENCRYPT); + } + +static void aes_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + AES_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + AES_DECRYPT); + } + +static const ops_crypt_t aes128= + { + OPS_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 void aes256_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_encrypt_key(crypt->key,KEYBITS_AES256,crypt->encrypt_key)) + fprintf(stderr,"aes256_init: Error setting encrypt_key\n"); + + if (crypt->decrypt_key) + free(crypt->decrypt_key); + crypt->decrypt_key=malloc(sizeof(AES_KEY)); + if (AES_set_decrypt_key(crypt->key,KEYBITS_AES256,crypt->decrypt_key)) + fprintf(stderr,"aes256_init: Error setting decrypt_key\n"); + } + +static const ops_crypt_t aes256= + { + OPS_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 + }; + +#ifndef OPENSSL_NO_CAMELLIA + +// CAMELLIA with 128-bit key + +#define KEYBITS_CAMELLIA128 128 + +static void camellia128_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(CAMELLIA_KEY)); + 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); + crypt->decrypt_key=malloc(sizeof(CAMELLIA_KEY)); + if (Camellia_set_key(crypt->key,KEYBITS_CAMELLIA128,crypt->decrypt_key)) + fprintf(stderr,"camellia128_init: Error setting decrypt_key\n"); + } + +static void camellia_block_encrypt(ops_crypt_t *crypt,void *out,const void *in) + { Camellia_encrypt(in,out,crypt->encrypt_key); } + +static void camellia_block_decrypt(ops_crypt_t *crypt,void *out,const void *in) + { Camellia_decrypt(in,out,crypt->decrypt_key); } + +static void camellia_cfb_encrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + Camellia_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAMELLIA_ENCRYPT); + } + +static void camellia_cfb_decrypt(ops_crypt_t *crypt,void *out,const void *in, size_t count) + { + Camellia_cfb128_encrypt(in,out,count, + crypt->encrypt_key, crypt->iv, (int *)&crypt->num, + CAMELLIA_DECRYPT); + } + +static const ops_crypt_t camellia128= + { + OPS_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 192-bit key + +#define KEYBITS_CAMELLIA192 192 + +static void camellia192_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(CAMELLIA_KEY)); + if (Camellia_set_key(crypt->key,KEYBITS_CAMELLIA192,crypt->encrypt_key)) + fprintf(stderr,"camellia192_init: Error setting encrypt_key\n"); + + if (crypt->decrypt_key) + free(crypt->decrypt_key); + crypt->decrypt_key=malloc(sizeof(CAMELLIA_KEY)); + if (Camellia_set_key(crypt->key,KEYBITS_CAMELLIA192,crypt->decrypt_key)) + fprintf(stderr,"camellia192_init: Error setting decrypt_key\n"); + } + +static const ops_crypt_t camellia192= + { + OPS_SA_CAMELLIA_192, + CAMELLIA_BLOCK_SIZE, + KEYBITS_CAMELLIA192/8, + std_set_iv, + std_set_key, + camellia192_init, + std_resync, + camellia_block_encrypt, + camellia_block_decrypt, + camellia_cfb_encrypt, + camellia_cfb_decrypt, + std_finish, + TRAILER + }; + +// CAMELLIA with 256-bit key + +#define KEYBITS_CAMELLIA256 256 + +static void camellia256_init(ops_crypt_t *crypt) + { + if (crypt->encrypt_key) + free(crypt->encrypt_key); + crypt->encrypt_key=malloc(sizeof(CAMELLIA_KEY)); + 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); + crypt->decrypt_key=malloc(sizeof(CAMELLIA_KEY)); + if (Camellia_set_key(crypt->key,KEYBITS_CAMELLIA256,crypt->decrypt_key)) + fprintf(stderr,"camellia256_init: Error setting decrypt_key\n"); + } + +static const ops_crypt_t camellia256= + { + OPS_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 // ndef OPENSSL_NO_CAMELLIA + +// Triple DES + +static void tripledes_init(ops_crypt_t *crypt) + { + DES_key_schedule *keys; + int n; + + if (crypt->encrypt_key) + free(crypt->encrypt_key); + keys=crypt->encrypt_key=malloc(3*sizeof(DES_key_schedule)); + + for(n=0 ; n < 3 ; ++n) + DES_set_key((DES_cblock *)(crypt->key+n*8),&keys[n]); + } + +static void tripledes_block_encrypt(ops_crypt_t *crypt,void *out, + const void *in) + { + DES_key_schedule *keys=crypt->encrypt_key; + + DES_ecb3_encrypt((void *)in,out,&keys[0],&keys[1],&keys[2],DES_ENCRYPT); + } + +static void tripledes_block_decrypt(ops_crypt_t *crypt,void *out, + const void *in) + { + DES_key_schedule *keys=crypt->encrypt_key; + + DES_ecb3_encrypt((void *)in,out,&keys[0],&keys[1],&keys[2],DES_DECRYPT); + } + +static void tripledes_cfb_encrypt(ops_crypt_t *crypt ATTRIBUTE_UNUSED, + void *out ATTRIBUTE_UNUSED, + const void *in ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) + { + DES_key_schedule *keys=crypt->encrypt_key; + DES_ede3_cfb64_encrypt(in,out,count, + &keys[0],&keys[1],&keys[2], + (DES_cblock *)crypt->iv, (int *)&crypt->num, + DES_ENCRYPT); + } + +static void tripledes_cfb_decrypt(ops_crypt_t *crypt ATTRIBUTE_UNUSED, + void *out ATTRIBUTE_UNUSED, + const void *in ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) + { + DES_key_schedule *keys=crypt->encrypt_key; + DES_ede3_cfb64_encrypt(in,out,count, + &keys[0],&keys[1],&keys[2], + (DES_cblock *)crypt->iv, (int *)&crypt->num, + DES_DECRYPT); + } + +static const ops_crypt_t tripledes= + { + OPS_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 + }; + +static const ops_crypt_t *get_proto(ops_symmetric_algorithm_t alg) + { + switch(alg) + { + case OPS_SA_CAST5: + return &cast5; + +#ifndef OPENSSL_NO_IDEA + case OPS_SA_IDEA: + return &idea; +#endif /* OPENSSL_NO_IDEA */ + + case OPS_SA_AES_128: + return &aes128; + + case OPS_SA_AES_256: + return &aes256; + +#ifndef OPENSSL_NO_CAMELLIA + case OPS_SA_CAMELLIA_128: + return &camellia128; + + case OPS_SA_CAMELLIA_192: + return &camellia192; + + case OPS_SA_CAMELLIA_256: + return &camellia256; +#endif // ndef OPENSSL_NO_CAMELLIA + + case OPS_SA_TRIPLEDES: + return &tripledes; + + default: + fprintf(stderr,"Unknown algorithm: %d (%s)\n",alg, + ops_show_symmetric_algorithm(alg)); + // assert(0); + } + + return NULL; + } + +int ops_crypt_any(ops_crypt_t *crypt,ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *ptr=get_proto(alg); + if (ptr) + { + *crypt=*ptr; + return 1; + } + else + { + memset(crypt,'\0',sizeof *crypt); + return 0; + } + } + +unsigned ops_block_size(ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *p=get_proto(alg); + + if(!p) + return 0; + + return p->blocksize; + } + +unsigned ops_key_size(ops_symmetric_algorithm_t alg) + { + const ops_crypt_t *p=get_proto(alg); + + if(!p) + return 0; + + return p->keysize; + } + +void ops_encrypt_init(ops_crypt_t * encrypt) + { + // \todo should there be a separate ops_encrypt_init? + ops_decrypt_init(encrypt); + } + +void ops_decrypt_init(ops_crypt_t *decrypt) + { + decrypt->base_init(decrypt); + decrypt->block_encrypt(decrypt,decrypt->siv,decrypt->iv); + memcpy(decrypt->civ,decrypt->siv,decrypt->blocksize); + decrypt->num=0; + } + +size_t ops_decrypt_se +(ops_crypt_t *decrypt,void *out_,const void *in_, + size_t count) + { + unsigned char *out=out_; + const unsigned char *in=in_; + int saved=count; + + /* in order to support v3's weird resyncing we have to implement CFB mode + ourselves */ + while(count-- > 0) + { + unsigned char t; + + if(decrypt->num == decrypt->blocksize) + { + 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 saved; + } + +size_t ops_encrypt_se(ops_crypt_t *encrypt,void *out_,const void *in_, + size_t count) + { + unsigned char *out=out_; + const unsigned char *in=in_; + int saved=count; + + /* in order to support v3's weird resyncing we have to implement CFB mode + ourselves */ + while(count-- > 0) + { + if(encrypt->num == encrypt->blocksize) + { + 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 saved; + } + +/** +\ingroup HighLevel_Supported +\brief Is this Symmetric Algorithm supported? +\param alg Symmetric Algorithm to check +\return ops_true if supported; else ops_false +*/ +ops_boolean_t ops_is_sa_supported(ops_symmetric_algorithm_t alg) + { + switch (alg) + { + case OPS_SA_AES_128: + case OPS_SA_AES_256: + case OPS_SA_CAMELLIA_128: + case OPS_SA_CAMELLIA_192: + case OPS_SA_CAMELLIA_256: + case OPS_SA_CAST5: + case OPS_SA_TRIPLEDES: +#ifndef OPENSSL_NO_IDEA + case OPS_SA_IDEA: +#endif + return ops_true; + break; + + default: + fprintf(stderr,"\nWarning: %s not supported\n", + ops_show_symmetric_algorithm(alg)); + return ops_false; + } + } + +size_t ops_encrypt_se_ip(ops_crypt_t *crypt,void *out_,const void *in_, + size_t count) + { + if (!ops_is_sa_supported(crypt->algorithm)) + return -1; + + crypt->cfb_encrypt(crypt, out_, in_, count); + + // \todo test this number was encrypted + return count; + } + +size_t ops_decrypt_se_ip(ops_crypt_t *crypt,void *out_,const void *in_, + size_t count) + { + if (!ops_is_sa_supported(crypt->algorithm)) + return -1; + + crypt->cfb_decrypt(crypt, out_, in_, count); + + // \todo check this number was in fact decrypted + return count; + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/types.h b/openpgpsdk/src/openpgpsdk/types.h new file mode 100644 index 000000000..4fea73343 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/types.h @@ -0,0 +1,223 @@ +/* + * 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 OPS_TYPES_H +#define OPS_TYPES_H + +/** Special type for intermediate function casting, avoids warnings on + some platforms +*/ +typedef void (*ops_void_fptr)(void); +#define ops_fcast(f) ((ops_void_fptr)f) + +/** ops_map_t + */ +typedef struct + { + int type; + char *string; + } ops_map_t; + +/** Boolean type */ +typedef unsigned ops_boolean_t; + +/** ops_content_tag_t */ + +/* 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 + */ + +#ifndef __cplusplus +typedef enum ops_content_tag_t ops_content_tag_t ; +#endif + +enum ops_content_tag_t + { + OPS_PTAG_CT_RESERVED = 0, /*!< Reserved - a packet tag must not have this value */ + OPS_PTAG_CT_PK_SESSION_KEY = 1, /*!< Public-Key Encrypted Session Key Packet */ + OPS_PTAG_CT_SIGNATURE = 2, /*!< Signature Packet */ + OPS_PTAG_CT_SK_SESSION_KEY = 3, /*!< Symmetric-Key Encrypted Session Key Packet */ + OPS_PTAG_CT_ONE_PASS_SIGNATURE = 4, /*!< One-Pass Signature Packet */ + OPS_PTAG_CT_SECRET_KEY = 5, /*!< Secret Key Packet */ + OPS_PTAG_CT_PUBLIC_KEY = 6, /*!< Public Key Packet */ + OPS_PTAG_CT_SECRET_SUBKEY = 7, /*!< Secret Subkey Packet */ + OPS_PTAG_CT_COMPRESSED = 8, /*!< Compressed Data Packet */ + OPS_PTAG_CT_SE_DATA = 9, /*!< Symmetrically Encrypted Data Packet */ + OPS_PTAG_CT_MARKER =10, /*!< Marker Packet */ + OPS_PTAG_CT_LITERAL_DATA =11, /*!< Literal Data Packet */ + OPS_PTAG_CT_TRUST =12, /*!< Trust Packet */ + OPS_PTAG_CT_USER_ID =13, /*!< User ID Packet */ + OPS_PTAG_CT_PUBLIC_SUBKEY =14, /*!< Public Subkey Packet */ + OPS_PTAG_CT_RESERVED2 =15, /*!< reserved */ + OPS_PTAG_CT_RESERVED3 =16, /*!< reserved */ + OPS_PTAG_CT_USER_ATTRIBUTE =17, /*!< User Attribute Packet */ + OPS_PTAG_CT_SE_IP_DATA =18, /*!< Sym. Encrypted and Integrity Protected Data Packet */ + OPS_PTAG_CT_MDC =19, /*!< Modification Detection Code Packet */ + + OPS_PARSER_PTAG =0x100, /*!< Internal Use: The packet is the "Packet Tag" itself - used when + callback sends back the PTag. */ + OPS_PTAG_RAW_SS =0x101, /*!< Internal Use: content is raw sig subtag */ + OPS_PTAG_SS_ALL =0x102, /*!< Internal Use: select all subtags */ + OPS_PARSER_PACKET_END =0x103, + + /* signature subpackets (0x200-2ff) (type+0x200) */ + /* only those we can parse are listed here */ + OPS_PTAG_SIGNATURE_SUBPACKET_BASE =0x200, /*!< Base for signature subpacket types - All signature type + values are relative to this value. */ + OPS_PTAG_SS_CREATION_TIME =0x200+2, /*!< signature creation time */ + OPS_PTAG_SS_EXPIRATION_TIME =0x200+3, /*!< signature expiration time */ + + OPS_PTAG_SS_EXPORTABLE_CERTIFICATION =0x200+4, /*!< exportable certification */ + OPS_PTAG_SS_TRUST =0x200+5, /*!< trust signature */ + OPS_PTAG_SS_REGEXP =0x200+6, /*!< regular expression */ + OPS_PTAG_SS_REVOCABLE =0x200+7, /*!< revocable */ + OPS_PTAG_SS_KEY_EXPIRATION_TIME =0x200+9, /*!< key expiration time */ + OPS_PTAG_SS_RESERVED =0x200+10, /*!< reserved */ + OPS_PTAG_SS_PREFERRED_SKA =0x200+11, /*!< preferred symmetric algorithms */ + OPS_PTAG_SS_REVOCATION_KEY =0x200+12, /*!< revocation key */ + OPS_PTAG_SS_ISSUER_KEY_ID =0x200+16, /*!< issuer key ID */ + OPS_PTAG_SS_NOTATION_DATA =0x200+20, /*!< notation data */ + OPS_PTAG_SS_PREFERRED_HASH =0x200+21, /*!< preferred hash algorithms */ + OPS_PTAG_SS_PREFERRED_COMPRESSION =0x200+22, /*!< preferred compression algorithms */ + OPS_PTAG_SS_KEY_SERVER_PREFS =0x200+23, /*!< key server preferences */ + OPS_PTAG_SS_PREFERRED_KEY_SERVER =0x200+24, /*!< Preferred Key Server */ + OPS_PTAG_SS_PRIMARY_USER_ID =0x200+25, /*!< primary User ID */ + OPS_PTAG_SS_POLICY_URI =0x200+26, /*!< Policy URI */ + OPS_PTAG_SS_KEY_FLAGS =0x200+27, /*!< key flags */ + OPS_PTAG_SS_SIGNERS_USER_ID =0x200+28, /*!< Signer's User ID */ + OPS_PTAG_SS_REVOCATION_REASON =0x200+29, /*!< reason for revocation */ + OPS_PTAG_SS_FEATURES =0x200+30, /*!< features */ + OPS_PTAG_SS_SIGNATURE_TARGET =0x200+31, /*!< signature target */ + OPS_PTAG_SS_EMBEDDED_SIGNATURE=0x200+32, /*!< embedded signature */ + + OPS_PTAG_SS_USERDEFINED00 =0x200+100, /*!< internal or user-defined */ + OPS_PTAG_SS_USERDEFINED01 =0x200+101, + OPS_PTAG_SS_USERDEFINED02 =0x200+102, + OPS_PTAG_SS_USERDEFINED03 =0x200+103, + OPS_PTAG_SS_USERDEFINED04 =0x200+104, + OPS_PTAG_SS_USERDEFINED05 =0x200+105, + OPS_PTAG_SS_USERDEFINED06 =0x200+106, + OPS_PTAG_SS_USERDEFINED07 =0x200+107, + OPS_PTAG_SS_USERDEFINED08 =0x200+108, + OPS_PTAG_SS_USERDEFINED09 =0x200+109, + OPS_PTAG_SS_USERDEFINED10 =0x200+110, + + + /* pseudo content types */ + OPS_PTAG_CT_LITERAL_DATA_HEADER =0x300, + OPS_PTAG_CT_LITERAL_DATA_BODY =0x300+1, + OPS_PTAG_CT_SIGNATURE_HEADER =0x300+2, + OPS_PTAG_CT_SIGNATURE_FOOTER =0x300+3, + OPS_PTAG_CT_ARMOUR_HEADER =0x300+4, + OPS_PTAG_CT_ARMOUR_TRAILER =0x300+5, + OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER =0x300+6, + OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY =0x300+7, + OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER=0x300+8, + OPS_PTAG_CT_UNARMOURED_TEXT =0x300+9, + OPS_PTAG_CT_ENCRYPTED_SECRET_KEY =0x300+10, // In this case the algorithm specific fields will not be initialised + OPS_PTAG_CT_SE_DATA_HEADER =0x300+11, + OPS_PTAG_CT_SE_DATA_BODY =0x300+12, + OPS_PTAG_CT_SE_IP_DATA_HEADER =0x300+13, + OPS_PTAG_CT_SE_IP_DATA_BODY =0x300+14, + OPS_PTAG_CT_ENCRYPTED_PK_SESSION_KEY=0x300+15, + + /* commands to the callback */ + OPS_PARSER_CMD_GET_SK_PASSPHRASE =0x400, + OPS_PARSER_CMD_GET_SECRET_KEY =0x400+1, + OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD =0x400+2, + + + /* Errors */ + OPS_PARSER_ERROR =0x500, /*!< Internal Use: Parser Error */ + OPS_PARSER_ERRCODE =0x500+1, /*! < Internal Use: Parser Error with errcode returned */ + }; + +/** Used to specify whether subpackets should be returned raw, parsed or ignored. + */ + +enum ops_parse_type_t + { + OPS_PARSE_RAW, /*!< Callback Raw */ + OPS_PARSE_PARSED, /*!< Callback Parsed */ + OPS_PARSE_IGNORE, /*!< Don't callback */ + }; + +typedef struct _ops_crypt_t ops_crypt_t; + +/** ops_hash_t */ +typedef struct _ops_hash_t ops_hash_t; + +/** + keep both ops_content_tag_t and ops_packet_tag_t because we might + want to introduce some bounds checking i.e. is this really a valid value + for a packet tag? +*/ +typedef enum ops_content_tag_t ops_packet_tag_t; +/** SS types are a subset of all content types. +*/ +typedef enum ops_content_tag_t ops_ss_type_t; +/* typedef enum ops_sig_type_t ops_sig_type_t; */ + +/** Revocation Reason type */ +typedef unsigned char ops_ss_rr_code_t; + +/** ops_parse_type_t */ + +/** Used to specify whether subpackets should be returned raw, parsed or ignored. + */ + +typedef enum ops_parse_type_t ops_parse_type_t; + +/** ops_parser_content_t */ +typedef struct ops_parser_content_t ops_parser_content_t; + +/** Reader Flags */ +/* +typedef enum + { + OPS_RETURN_LENGTH=1, + } ops_reader_flags_t; +typedef enum ops_reader_ret_t ops_reader_ret_t; +*/ + +/** Writer flags */ +typedef enum + { + OPS_WF_DUMMY, + } ops_writer_flags_t; + +/** + * \ingroup Create + * Contains the required information about how to write + */ +typedef struct ops_create_info ops_create_info_t; + +#endif diff --git a/openpgpsdk/src/openpgpsdk/util.c b/openpgpsdk/src/openpgpsdk/util.c new file mode 100644 index 000000000..88fe3f100 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/util.c @@ -0,0 +1,176 @@ + /* + * 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 +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include + +/** + * 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 char *str_from_map_or_null(int type, ops_map_t *map) + { + ops_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. + */ + +char *ops_str_from_map(int type, ops_map_t *map) + { + char *str; + str=str_from_map_or_null(type,map); + if (str) + return(str); + else + return("Unknown"); + } + +void hexdump(const unsigned char *src,size_t length) + { + while(length--) + printf("%02X",*src++); + } + +/** + * \ingroup HighLevel_Functions + * \brief Initialises OpenPGP::SDK. To be called before any other OPS function. + * + * Initialises OpenPGP::SDK and the underlying openssl library. + */ + +void ops_init(void) + { + ops_crypto_init(); + } + +/** + * \ingroup HighLevel_Functions + * \brief Closes down OpenPGP::SDK. + * + * Close down OpenPGP:SDK, release any resources under the control of + * the library. No OpenPGP:SDK function other than ops_init() should + * be called after this function. + */ + +void ops_finish(void) + { + ops_crypto_finish(); + } + +/** + \ingroup HighLevel_Misc + \brief mallocs and zeros memory + \param n Number of bytes to be alloc-ed. + \return Pointer to new memory. + \note Should be freed after use with free(). +*/ +void *ops_mallocz(size_t n) + { + void *m=malloc(n); + + memset(m,'\0',n); + + return m; + } + +typedef struct + { + unsigned short sum; + } sum16_arg_t; + +static int sum16_reader(void *dest_,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo) + { + const unsigned char *dest=dest_; + sum16_arg_t *arg=ops_reader_get_arg(rinfo); + int r=ops_stacked_read(dest_,length,errors,rinfo,cbinfo); + int n; + + if(r < 0) + return r; + + for(n=0 ; n < r ; ++n) + arg->sum=(arg->sum+dest[n])&0xffff; + + return r; + } + +static void sum16_destroyer(ops_reader_info_t *rinfo) + { free(ops_reader_get_arg(rinfo)); } + +/** + \ingroup Internal_Readers_Sum16 + \param pinfo Parse settings +*/ + +void ops_reader_push_sum16(ops_parse_info_t *pinfo) + { + sum16_arg_t *arg=ops_mallocz(sizeof *arg); + + ops_reader_push(pinfo,sum16_reader,sum16_destroyer,arg); + } + +/** + \ingroup Internal_Readers_Sum16 + \param pinfo Parse settings + \return sum +*/ +unsigned short ops_reader_pop_sum16(ops_parse_info_t *pinfo) + { + sum16_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(pinfo)); + unsigned short sum=arg->sum; + + ops_reader_pop(pinfo); + free(arg); + + return sum; + } diff --git a/openpgpsdk/src/openpgpsdk/util.h b/openpgpsdk/src/openpgpsdk/util.h new file mode 100644 index 000000000..88d7b10a8 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/util.h @@ -0,0 +1,54 @@ +/* + * 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 OPS_UTIL_H +#define OPS_UTIL_H + +#include "openpgpsdk/types.h" +#include "openpgpsdk/create.h" +#include "openpgpsdk/packet-parse.h" +#include + +#define ops_false 0 +#define ops_true 1 + +void hexdump(const unsigned char *src,size_t length); + +/* + * These macros code ensures that you are casting what you intend to cast. + * It works because in "a ? b : c", b and c must have the same type. + * This is a copy of the macro defined in openssl/asn1.h. + */ +#ifndef CHECKED_PTR_OF +#define CHECKED_PTR_OF(type, p) ((void*) (1 ? p : (type *)0)) +#endif +#define CHECKED_INSTANCE_OF(type, p) (1 ? p : (type)0) +#define DECONST(type,p) ((type *)CHECKED_PTR_OF(const type, p)) + +/* number of elements in an array */ +#define OPS_ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) + +void *ops_mallocz(size_t n); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/validate.c b/openpgpsdk/src/openpgpsdk/validate.c new file mode 100644 index 000000000..e6d3256ac --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/validate.c @@ -0,0 +1,826 @@ +/* + * 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 +#include +#include +#include "keyring_local.h" +#include "parse_local.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug=0; + +static ops_boolean_t check_binary_signature(const unsigned len, + const unsigned char *data, + const ops_signature_t *sig, + const ops_public_key_t *signer __attribute__((unused))) +{ + // Does the signed hash match the given hash? + + int n=0; + ops_hash_t hash; + unsigned char hashout[OPS_MAX_HASH_SIZE]; + unsigned char trailer[6]; + unsigned int hashedlen; + + //common_init_signature(&hash,sig); + ops_hash_any(&hash,sig->info.hash_algorithm); + hash.init(&hash); + hash.add(&hash,data,len); + switch (sig->info.version) + { + case OPS_V3: + trailer[0]=sig->info.type; + trailer[1]=sig->info.creation_time >> 24; + trailer[2]=sig->info.creation_time >> 16; + trailer[3]=sig->info.creation_time >> 8; + trailer[4]=sig->info.creation_time; + hash.add(&hash,&trailer[0],5); + break; + + case OPS_V4: + hash.add(&hash,sig->info.v4_hashed_data,sig->info.v4_hashed_data_length); + + trailer[0]=0x04; // version + trailer[1]=0xFF; + hashedlen=sig->info.v4_hashed_data_length; + trailer[2]=hashedlen >> 24; + trailer[3]=hashedlen >> 16; + trailer[4]=hashedlen >> 8; + trailer[5]=hashedlen; + hash.add(&hash,&trailer[0],6); + + break; + + default: + fprintf(stderr,"Invalid signature version %d\n", sig->info.version); + return ops_false; + } + + n=hash.finish(&hash,hashout); + + // return ops_false; + return ops_check_signature(hashout,n,sig,signer); +} + +static int keydata_reader(void *dest,size_t length,ops_error_t **errors, + ops_reader_info_t *rinfo, + ops_parse_cb_info_t *cbinfo) +{ + validate_reader_arg_t *arg=ops_reader_get_arg(rinfo); + + OPS_USED(errors); + OPS_USED(cbinfo); + if(arg->offset == arg->key->packets[arg->packet].length) + { + ++arg->packet; + arg->offset=0; + } + + if(arg->packet == arg->key->npackets) + return 0; + + // we should never be asked to cross a packet boundary in a single read + assert(arg->key->packets[arg->packet].length >= arg->offset+length); + + memcpy(dest,&arg->key->packets[arg->packet].raw[arg->offset],length); + arg->offset+=length; + + return length; +} + +static void free_signature_info(ops_signature_info_t *sig,int n) +{ + int i ; + for(i=0;iv4_hashed_data=ops_mallocz(src->v4_hashed_data_length); + memcpy(dst->v4_hashed_data,src->v4_hashed_data,src->v4_hashed_data_length); +} + +static void add_sig_to_valid_list(ops_validate_result_t * result, const ops_signature_info_t* sig) +{ + size_t newsize; + size_t start; + + // increment count + ++result->valid_count; + + // increase size of array + newsize=(sizeof *sig) * result->valid_count; + if (!result->valid_sigs) + result->valid_sigs=malloc(newsize); + else + result->valid_sigs=realloc(result->valid_sigs, newsize); + + // copy key ptr to array + copy_signature_info(&result->valid_sigs[result->valid_count-1],sig); +} + +static void add_sig_to_invalid_list(ops_validate_result_t * result, const ops_signature_info_t *sig) +{ + size_t newsize; + size_t start; + + // increment count + ++result->invalid_count; + + // increase size of array + newsize=(sizeof *sig) * result->invalid_count; + if (!result->invalid_sigs) + result->invalid_sigs=malloc(newsize); + else + result->invalid_sigs=realloc(result->invalid_sigs, newsize); + + // copy key ptr to array + copy_signature_info(&result->invalid_sigs[result->invalid_count-1],sig); +} + +static void add_sig_to_unknown_list(ops_validate_result_t * result, const ops_signature_info_t *sig) +{ + size_t newsize; + size_t start; + + // increment count + ++result->unknown_signer_count; + + // increase size of array + newsize=(sizeof *sig) * result->unknown_signer_count; + if (!result->unknown_sigs) + result->unknown_sigs=malloc(newsize); + else + result->unknown_sigs=realloc(result->unknown_sigs, newsize); + + // copy key id to array + copy_signature_info(&result->unknown_sigs[result->unknown_signer_count-1],sig); +} + + ops_parse_cb_return_t +ops_validate_key_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) +{ + const ops_parser_content_union_t *content=&content_->content; + validate_key_cb_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + ops_error_t **errors=ops_parse_cb_get_errors(cbinfo); + const ops_keydata_t *signer; + ops_boolean_t valid=ops_false; + + if (debug) + printf("%s\n",ops_show_packet_tag(content_->tag)); + + switch(content_->tag) + { + case OPS_PTAG_CT_PUBLIC_KEY: + assert(arg->pkey.version == 0); + arg->pkey=content->public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_PUBLIC_SUBKEY: + if(arg->subkey.version) + ops_public_key_free(&arg->subkey); + arg->subkey=content->public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_SECRET_KEY: + arg->skey=content->secret_key; + arg->pkey=arg->skey.public_key; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ID: + if(arg->user_id.user_id) + ops_user_id_free(&arg->user_id); + arg->user_id=content->user_id; + arg->last_seen=ID; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_USER_ATTRIBUTE: + assert(content->user_attribute.data.len); + printf("user attribute, length=%d\n",(int)content->user_attribute.data.len); + if(arg->user_attribute.data.len) + ops_user_attribute_free(&arg->user_attribute); + arg->user_attribute=content->user_attribute; + arg->last_seen=ATTRIBUTE; + return OPS_KEEP_MEMORY; + + case OPS_PTAG_CT_SIGNATURE: // V3 sigs + case OPS_PTAG_CT_SIGNATURE_FOOTER: // V4 sigs + + if(debug) + { + printf(" type=%02x signer_id=",content->signature.info.type); + hexdump(content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + printf("\n"); + } + + signer=ops_keyring_find_key_by_id(arg->keyring, + content->signature.info.signer_id); + if(!signer) + { + add_sig_to_unknown_list(arg->result, &content->signature.info); + break; + } + + switch(content->signature.info.type) + { + case OPS_CERT_GENERIC: + case OPS_CERT_PERSONA: + case OPS_CERT_CASUAL: + case OPS_CERT_POSITIVE: + case OPS_SIG_REV_CERT: + if(arg->last_seen == ID) + valid=ops_check_user_id_certification_signature(&arg->pkey, + &arg->user_id, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + else + valid=ops_check_user_attribute_certification_signature(&arg->pkey, + &arg->user_attribute, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + + break; + + case OPS_SIG_SUBKEY: + // XXX: we should also check that the signer is the key we are validating, I think. + valid=ops_check_subkey_signature(&arg->pkey,&arg->subkey, + &content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + break; + + case OPS_SIG_DIRECT: + valid=ops_check_direct_signature(&arg->pkey,&content->signature, + ops_get_public_key_from_data(signer), + arg->rarg->key->packets[arg->rarg->packet].raw); + break; + + case OPS_SIG_STANDALONE: + case OPS_SIG_PRIMARY: + case OPS_SIG_REV_KEY: + case OPS_SIG_REV_SUBKEY: + case OPS_SIG_TIMESTAMP: + case OPS_SIG_3RD_PARTY: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Verification of signature type 0x%02x not yet implemented\n", content->signature.info.type); + break; + + default: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Unexpected signature type 0x%02x\n", content->signature.info.type); + } + + if(valid) + { + // printf(" validated\n"); + //++arg->result->valid_count; + add_sig_to_valid_list(arg->result, &content->signature.info); + } + else + { + OPS_ERROR(errors,OPS_E_V_BAD_SIGNATURE,"Bad Signature"); + // printf(" BAD SIGNATURE\n"); + // ++arg->result->invalid_count; + add_sig_to_invalid_list(arg->result, &content->signature.info); + } + break; + + // ignore these + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PARSER_PACKET_END: + case OPS_PTAG_CT_TRUST: + break; + + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + if (arg->cb_get_passphrase) + { + return arg->cb_get_passphrase(content_,cbinfo); + } + break; + + default: + fprintf(stderr,"unexpected tag=0x%x\n",content_->tag); + assert(0); + break; + } + return OPS_RELEASE_MEMORY; +} + + ops_parse_cb_return_t +validate_data_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo) +{ + const ops_parser_content_union_t *content=&content_->content; + validate_data_cb_arg_t *arg=ops_parse_cb_get_arg(cbinfo); + ops_error_t **errors=ops_parse_cb_get_errors(cbinfo); + const ops_keydata_t *signer; + ops_boolean_t valid=ops_false; + ops_memory_t* mem=NULL; + + if (debug) + printf("%s\n",ops_show_packet_tag(content_->tag)); + + switch(content_->tag) + { + case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER: + // ignore - this gives us the "Armor Header" line "Hash: SHA1" or similar + break; + + case OPS_PTAG_CT_LITERAL_DATA_HEADER: + // ignore + break; + + case OPS_PTAG_CT_LITERAL_DATA_BODY: + arg->literal_data_body=content->literal_data_body; + arg->use=LITERAL_DATA; + return OPS_KEEP_MEMORY; + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY: + arg->signed_cleartext_body=content->signed_cleartext_body; + arg->use=SIGNED_CLEARTEXT; + return OPS_KEEP_MEMORY; + break; + + case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER: + // this gives us an ops_hash_t struct + break; + + case OPS_PTAG_CT_SIGNATURE: // V3 sigs + case OPS_PTAG_CT_SIGNATURE_FOOTER: // V4 sigs + + if (debug) + { + printf("\n*** hashed data:\n"); + unsigned int zzz=0; + for (zzz=0; zzzsignature.info.v4_hashed_data_length; zzz++) + printf("0x%02x ", content->signature.info.v4_hashed_data[zzz]); + printf("\n"); + printf(" type=%02x signer_id=",content->signature.info.type); + hexdump(content->signature.info.signer_id, + sizeof content->signature.info.signer_id); + } + + signer=ops_keyring_find_key_by_id(arg->keyring, + content->signature.info.signer_id); + if(!signer) + { + OPS_ERROR(errors,OPS_E_V_UNKNOWN_SIGNER,"Unknown Signer"); + add_sig_to_unknown_list(arg->result, &content->signature.info); + break; + } + + mem=ops_memory_new(); + ops_memory_init(mem,128); + + switch(content->signature.info.type) + { + case OPS_SIG_BINARY: + case OPS_SIG_TEXT: + switch(arg->use) + { + case LITERAL_DATA: + ops_memory_add(mem, + arg->literal_data_body.data, + arg->literal_data_body.length); + break; + + case SIGNED_CLEARTEXT: + ops_memory_add(mem, + arg->signed_cleartext_body.data, + arg->signed_cleartext_body.length); + break; + + default: + OPS_ERROR_1(errors,OPS_E_UNIMPLEMENTED,"Unimplemented Sig Use %d", arg->use); + printf(" Unimplemented Sig Use %d\n", arg->use); + break; + } + + valid=check_binary_signature(ops_memory_get_length(mem), + ops_memory_get_data(mem), + &content->signature, + ops_get_public_key_from_data(signer)); + break; + + default: + OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED, + "Verification of signature type 0x%02x not yet implemented\n", content->signature.info.type); + break; + + } + ops_memory_free(mem); + + if(valid) + { + add_sig_to_valid_list(arg->result, &content->signature.info); + } + else + { + OPS_ERROR(errors,OPS_E_V_BAD_SIGNATURE,"Bad Signature"); + add_sig_to_invalid_list(arg->result, &content->signature.info); + } + break; + + // ignore these + case OPS_PARSER_PTAG: + case OPS_PTAG_CT_SIGNATURE_HEADER: + case OPS_PTAG_CT_ARMOUR_HEADER: + case OPS_PTAG_CT_ARMOUR_TRAILER: + case OPS_PTAG_CT_ONE_PASS_SIGNATURE: + case OPS_PARSER_PACKET_END: + break; + + default: + fprintf(stderr,"unexpected tag=0x%x\n",content_->tag); + assert(0); + break; + } + return OPS_RELEASE_MEMORY; +} + +static void keydata_destroyer(ops_reader_info_t *rinfo) +{ free(ops_reader_get_arg(rinfo)); } + +void ops_keydata_reader_set(ops_parse_info_t *pinfo,const ops_keydata_t *key) +{ + validate_reader_arg_t *arg=malloc(sizeof *arg); + + memset(arg,'\0',sizeof *arg); + + arg->key=key; + arg->packet=0; + arg->offset=0; + + ops_reader_set(pinfo,keydata_reader,keydata_destroyer,arg); +} + +/** + * \ingroup HighLevel_Verify + * \brief Indicicates whether any errors were found + * \param result Validation result to check + * \return ops_false if any invalid signatures or unknown signers or no valid signatures; else ops_true + */ +ops_boolean_t validate_result_status(ops_validate_result_t* result) +{ + if (result->invalid_count || result->unknown_signer_count || !result->valid_count) + return ops_false; + else + return ops_true; +} + +/** + * \ingroup HighLevel_Verify + * \brief Validate all signatures on a single key against the given keyring + * \param result Where to put the result + * \param key Key to validate + * \param keyring Keyring to use for validation + * \param cb_get_passphrase Callback to use to get passphrase + * \return ops_true if all signatures OK; else ops_false + * \note It is the caller's responsiblity to free result after use. + * \sa ops_validate_result_free() + + Example Code: + \code + void example(const ops_keydata_t* key, const ops_keyring_t *keyring) + { + ops_validate_result_t *result=NULL; + if (ops_validate_key_signatures(result, key, keyring, callback_cmd_get_passphrase_from_cmdline)==ops_true) + printf("OK"); + else + printf("ERR"); + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + ops_validate_result_free(result); + } + + \endcode + */ +ops_boolean_t ops_validate_key_signatures(ops_validate_result_t *result,const ops_keydata_t *key, + const ops_keyring_t *keyring, + ops_parse_cb_return_t cb_get_passphrase (const ops_parser_content_t *, ops_parse_cb_info_t *) + ) +{ + ops_parse_info_t *pinfo; + validate_key_cb_arg_t carg; + + memset(&carg,'\0',sizeof carg); + carg.result=result; + carg.cb_get_passphrase=cb_get_passphrase; + + pinfo=ops_parse_info_new(); + // ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); + + carg.keyring=keyring; + + ops_parse_cb_set(pinfo,ops_validate_key_cb,&carg); + pinfo->rinfo.accumulate=ops_true; + ops_keydata_reader_set(pinfo,key); + + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + carg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + ops_parse(pinfo); + + ops_public_key_free(&carg.pkey); + if(carg.subkey.version) + ops_public_key_free(&carg.subkey); + ops_user_id_free(&carg.user_id); + ops_user_attribute_free(&carg.user_attribute); + + ops_parse_info_delete(pinfo); + +/* if(carg.literal_data_body.data != NULL) + free(carg.literal_data_body.data) ; */ + + if (result->invalid_count || result->unknown_signer_count || !result->valid_count) + return ops_false; + else + return ops_true; +} + +/** + \ingroup HighLevel_Verify + \param result Where to put the result + \param ring Keyring to use + \param cb_get_passphrase Callback to use to get passphrase + \note It is the caller's responsibility to free result after use. + \sa ops_validate_result_free() + */ +ops_boolean_t ops_validate_all_signatures(ops_validate_result_t *result, + const ops_keyring_t *ring, + ops_parse_cb_return_t cb_get_passphrase (const ops_parser_content_t *, ops_parse_cb_info_t *) + ) +{ + int n; + + memset(result,'\0',sizeof *result); + for(n=0 ; n < ring->nkeys ; ++n) + ops_validate_key_signatures(result,&ring->keys[n],ring, cb_get_passphrase); + return validate_result_status(result); +} + +/** + \ingroup HighLevel_Verify + \brief Frees validation result and associated memory + \param result Struct to be freed + \note Must be called after validation functions + */ +void ops_validate_result_free(ops_validate_result_t *result) +{ + if (!result) + return; + + if (result->valid_sigs) + free_signature_info(result->valid_sigs,result->valid_count); + if (result->invalid_sigs) + free_signature_info(result->invalid_sigs,result->invalid_count); + if (result->unknown_sigs) + free_signature_info(result->unknown_sigs,result->unknown_signer_count); + + free(result); + result=NULL; +} + +/** + \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 ops_true if signatures validate successfully; ops_false 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 ops_validate_result_free(result) after use. + + Example code: + \code + void example(const char* filename, const int armoured, const ops_keyring_t* keyring) + { + ops_validate_result_t* result=ops_mallocz(sizeof *result); + + if (ops_validate_file(result, filename, armoured, keyring)==ops_true) + { + printf("OK"); +// look at result for details of keys with good signatures +} +else +{ +printf("ERR"); +// look at result for details of failed signatures or unknown signers +} + +ops_validate_result_free(result); +} +\endcode + */ +ops_boolean_t ops_validate_file(ops_validate_result_t *result, const char* filename, const int armoured, const ops_keyring_t* keyring) +{ + ops_parse_info_t *pinfo=NULL; + validate_data_cb_arg_t validate_arg; + + int fd=0; + + // + fd=ops_setup_file_read(&pinfo, filename, &validate_arg, validate_data_cb, ops_true); + if (fd < 0) + return ops_false; + + // Set verification reader and handling options + + memset(&validate_arg,'\0',sizeof validate_arg); + validate_arg.result=result; + validate_arg.keyring=keyring; + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + validate_arg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + if (armoured) + ops_reader_push_dearmour(pinfo); + + // Do the verification + + ops_parse(pinfo); + + if (debug) + { + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + } + + // Tidy up + if (armoured) + ops_reader_pop_dearmour(pinfo); + ops_teardown_file_read(pinfo, fd); + + if(validate_arg.literal_data_body.data != NULL) free(validate_arg.literal_data_body.data) ; + + return validate_result_status(result); +} + +/** + \ingroup HighLevel_Verify + \brief Verifies the signatures in a ops_memory_t struct + \param result Where to put the result + \param mem Memory to be validated + \param armoured Treat data as armoured, if set + \param keyring Keyring to use + \return ops_true if signature validates successfully; ops_false 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 ops_validate_result_free(result) after use. + */ + +ops_boolean_t ops_validate_mem(ops_validate_result_t *result, ops_memory_t* mem, const int armoured, const ops_keyring_t* keyring) +{ + ops_parse_info_t *pinfo=NULL; + validate_data_cb_arg_t validate_arg; + + // + ops_setup_memory_read(&pinfo, mem, &validate_arg, validate_data_cb, ops_true); + + // Set verification reader and handling options + + memset(&validate_arg,'\0',sizeof validate_arg); + validate_arg.result=result; + validate_arg.keyring=keyring; + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + validate_arg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + if (armoured) + ops_reader_push_dearmour(pinfo); + + // Do the verification + + ops_parse(pinfo); + + if (debug) + { + printf("valid=%d, invalid=%d, unknown=%d\n", + result->valid_count, + result->invalid_count, + result->unknown_signer_count); + } + + // Tidy up + if (armoured) + ops_reader_pop_dearmour(pinfo); + ops_teardown_memory_read(pinfo, mem); + + if(validate_arg.literal_data_body.data != NULL) free(validate_arg.literal_data_body.data) ; + if(validate_arg.signed_cleartext_body.data != NULL) free(validate_arg.signed_cleartext_body.data) ; + + return validate_result_status(result); +} + +/** + \ingroup HighLevel_Verify + \brief Verifies the signature in a detached signature data packet, given the literal data + \param literal_data Literal data that is signed + \param literal_data_length length of the literal data that is signed + \param signature_packet signature packet in binary PGP format + \param signature_packet_length length of the signature packet + \param signers_key Public key of the signer to check the signature for. + \return ops_true if signature validates successfully; ops_false if not + */ + +ops_boolean_t ops_validate_detached_signature(const void *literal_data, unsigned int literal_data_length, const unsigned char *signature_packet, unsigned int signature_packet_length,const ops_keydata_t *signers_key) +{ + ops_validate_result_t *result = (ops_validate_result_t*)ops_mallocz(sizeof(ops_validate_result_t)); + + ops_memory_t *mem = ops_memory_new() ; + ops_memory_add(mem,signature_packet,signature_packet_length) ; + + ops_parse_info_t *pinfo=NULL; + validate_data_cb_arg_t validate_arg; + + ops_setup_memory_read(&pinfo, mem, &validate_arg, validate_data_cb, ops_true); + + // Set verification reader and handling options + + ops_keyring_t tmp_keyring ; + tmp_keyring.nkeys = 1 ; + tmp_keyring.nkeys_allocated = 1 ; + tmp_keyring.keys = (ops_keydata_t *)signers_key ; // this is a const_cast, somehow + + memset(&validate_arg,'\0',sizeof validate_arg); + + validate_arg.result=result; + validate_arg.keyring=&tmp_keyring; + + int length = literal_data_length ; + + validate_arg.literal_data_body.data = (unsigned char *)malloc(length) ; + memcpy(validate_arg.literal_data_body.data, literal_data, length) ; + validate_arg.literal_data_body.length = length ; + + // Note: Coverity incorrectly reports an error that carg.rarg + // is never used. + validate_arg.rarg=ops_reader_get_arg_from_pinfo(pinfo); + + //if (armoured) + // ops_reader_push_dearmour(pinfo); + + // Do the verification + + ops_parse(pinfo); + + printf("valid=%d, invalid=%d, unknown=%d\n", result->valid_count, result->invalid_count, result->unknown_signer_count); + + // Tidy up + //if (armoured) + // ops_reader_pop_dearmour(pinfo); + + ops_teardown_memory_read(pinfo, mem); + + ops_boolean_t res = validate_result_status(result); + ops_validate_result_free(result) ; + + if(validate_arg.literal_data_body.data != NULL) free(validate_arg.literal_data_body.data) ; + if(validate_arg.signed_cleartext_body.data != NULL) free(validate_arg.signed_cleartext_body.data) ; + + return res ; +} + +// eof diff --git a/openpgpsdk/src/openpgpsdk/validate.h b/openpgpsdk/src/openpgpsdk/validate.h new file mode 100644 index 000000000..a078b7916 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/validate.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + + +typedef struct + { + unsigned int valid_count; + ops_signature_info_t * valid_sigs; + unsigned int invalid_count; + ops_signature_info_t * invalid_sigs; + unsigned int unknown_signer_count; + ops_signature_info_t * unknown_sigs; + } ops_validate_result_t; + +void ops_validate_result_free(ops_validate_result_t *result); + +ops_boolean_t ops_validate_key_signatures(ops_validate_result_t *result, + const ops_keydata_t* keydata, + const ops_keyring_t *ring, + ops_parse_cb_return_t cb (const ops_parser_content_t *, ops_parse_cb_info_t *)); +ops_boolean_t ops_validate_all_signatures(ops_validate_result_t *result, + const ops_keyring_t *ring, + ops_parse_cb_return_t (const ops_parser_content_t *, ops_parse_cb_info_t *)); + +void ops_keydata_reader_set(ops_parse_info_t *pinfo, + const ops_keydata_t *key); + +typedef struct + { + const ops_keydata_t *key; + unsigned packet; + unsigned offset; + } validate_reader_arg_t; + +/** Struct used with the validate_key_cb callback */ +typedef struct + { + ops_public_key_t pkey; + ops_public_key_t subkey; + ops_secret_key_t skey; + enum + { + ATTRIBUTE=1, + ID, + } last_seen; + ops_user_id_t user_id; + ops_user_attribute_t user_attribute; + unsigned char hash[OPS_MAX_HASH_SIZE]; + const ops_keyring_t *keyring; + validate_reader_arg_t *rarg; + ops_validate_result_t *result; + ops_parse_cb_return_t (*cb_get_passphrase) (const ops_parser_content_t *, ops_parse_cb_info_t *); + } validate_key_cb_arg_t; + +/** Struct use with the validate_data_cb callback */ +typedef struct validate_data_cb_arg + { + enum + { + LITERAL_DATA, + SIGNED_CLEARTEXT + } use; /* + +#include +#include +#include +#include +#include +#include +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#include + +//static int debug=0; + +/* + * return true if OK, otherwise false + */ +static ops_boolean_t base_write(const void *src,unsigned length, + ops_create_info_t *info) + { + return info->winfo.writer(src,length,&info->errors,&info->winfo); + } + +/** + * \ingroup Core_WritePackets + * + * \param src + * \param length + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write(const void *src,unsigned length, + ops_create_info_t *info) + { + return base_write(src,length,info); + } + +/** + * \ingroup Core_WritePackets + * \param n + * \param length + * \param info + * \return ops_true if OK, otherwise ops_false + */ + +ops_boolean_t ops_write_scalar(unsigned n,unsigned length, + ops_create_info_t *info) + { + while(length-- > 0) + { + unsigned char c[1]; + + c[0]=n >> (length*8); + if(!base_write(c,1,info)) + return ops_false; + } + return ops_true; + } + +/** + * \ingroup Core_WritePackets + * \param bn + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_mpi(const BIGNUM *bn,ops_create_info_t *info) + { + unsigned char buf[8192]; + int bits=BN_num_bits(bn); + + assert(bits <= 65535); + BN_bn2bin(bn,buf); + return ops_write_scalar(bits,2,info) + && ops_write(buf,(bits+7)/8,info); + } + +/** + * \ingroup Core_WritePackets + * \param tag + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_ptag(ops_content_tag_t tag,ops_create_info_t *info) + { + unsigned char c[1]; + + c[0]=tag|OPS_PTAG_ALWAYS_SET|OPS_PTAG_NEW_FORMAT; + + return base_write(c,1,info); + } + +/** + * \ingroup Core_WritePackets + * \param length + * \param info + * \return 1 if OK, otherwise 0 + */ + +ops_boolean_t ops_write_length(unsigned length,ops_create_info_t *info) + { + unsigned char c[2]; + + if(length < 192) + { + c[0]=length; + return base_write(c,1,info); + } + else if(length < 8384) + { + c[0]=((length-192) >> 8)+192; + c[1]=(length-192)%256; + return base_write(c,2,info); + } + return ops_write_scalar(0xff,1,info) && ops_write_scalar(length,4,info); + } + +/* Note that we finalise from the top down, so we don't use writers below + * that have already been finalised + */ +ops_boolean_t writer_info_finalise(ops_error_t **errors, + ops_writer_info_t *winfo) + { + ops_boolean_t ret=ops_true; + + if(winfo->finaliser) + { + ret=winfo->finaliser(errors,winfo); + winfo->finaliser=NULL; + } + if(winfo->next && !writer_info_finalise(errors,winfo->next)) + { + winfo->finaliser=NULL; + return ops_false; + } + return ret; + } + +void writer_info_delete(ops_writer_info_t *winfo) + { + // we should have finalised before deleting + assert(!winfo->finaliser); + if(winfo->next) + { + writer_info_delete(winfo->next); + free(winfo->next); + winfo->next=NULL; + } + if(winfo->destroyer) + { + winfo->destroyer(winfo); + winfo->destroyer=NULL; + } + winfo->writer=NULL; + } + +/** + * \ingroup Core_Writers + * + * Set a writer in info. There should not be another writer set. + * + * \param info The info structure + * \param writer + * \param finaliser + * \param destroyer + * \param arg The argument for the writer and destroyer + */ +void ops_writer_set(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg) + { + assert(!info->winfo.writer); + info->winfo.writer=writer; + info->winfo.finaliser=finaliser; + info->winfo.destroyer=destroyer; + info->winfo.arg=arg; + } + +/** + * \ingroup Core_Writers + * + * Push a writer in info. There must already be another writer set. + * + * \param info The info structure + * \param writer + * \param finaliser + * \param destroyer + * \param arg The argument for the writer and destroyer + */ +void ops_writer_push(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg) + { + ops_writer_info_t *copy=ops_mallocz(sizeof *copy); + + assert(info->winfo.writer); + *copy=info->winfo; + info->winfo.next=copy; + + info->winfo.writer=writer; + info->winfo.finaliser=finaliser; + info->winfo.destroyer=destroyer; + info->winfo.arg=arg; + } + +void ops_writer_pop(ops_create_info_t *info) + { + ops_writer_info_t *next; + + // Make sure the finaliser has been called. + assert(!info->winfo.finaliser); + // Make sure this is a stacked writer + assert(info->winfo.next); + if(info->winfo.destroyer) + info->winfo.destroyer(&info->winfo); + + next=info->winfo.next; + info->winfo=*next; + + free(next); + } + +/** + * \ingroup Core_Writers + * + * Close the writer currently set in info. + * + * \param info The info structure + */ +ops_boolean_t ops_writer_close(ops_create_info_t *info) + { + ops_boolean_t ret=writer_info_finalise(&info->errors,&info->winfo); + + writer_info_delete(&info->winfo); + + return ret; + } + +/** + * \ingroup Core_Writers + * + * Get the arg supplied to ops_create_info_set_writer(). + * + * \param winfo The writer_info structure + * \return The arg + */ +void *ops_writer_get_arg(ops_writer_info_t *winfo) + { return winfo->arg; } + +/** + * \ingroup Core_Writers + * + * Write to the next writer down in the stack. + * + * \param src The data to write. + * \param length The length of src. + * \param errors A place to store errors. + * \param winfo The writer_info structure. + * \return Success - if ops_false, then errors should contain the error. + */ +ops_boolean_t ops_stacked_write(const void *src,unsigned length, + ops_error_t **errors,ops_writer_info_t *winfo) + { + return winfo->next->writer(src,length,errors,winfo->next); + } + +/** + * \ingroup Core_Writers + * + * Free the arg. Many writers just have a malloc()ed lump of storage, this + * function releases it. + * + * \param winfo the info structure. + */ +void ops_writer_generic_destroyer(ops_writer_info_t *winfo) + { free(ops_writer_get_arg(winfo)); } + +/** + * \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. + */ +ops_boolean_t ops_writer_passthrough(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { return ops_stacked_write(src,length,errors,winfo); } + + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer.h b/openpgpsdk/src/openpgpsdk/writer.h new file mode 100644 index 000000000..2b15c8206 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer.h @@ -0,0 +1,100 @@ +/* + * 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 OPS_WRITER_H +#define OPS_WRITER_H + +#include "types.h" +#include "packet.h" +#include "crypto.h" +#include "memory.h" +#include "errors.h" +#include "keyring.h" + +/** + * \ingroup Create + * This struct contains the required information about one writer + */ +/** + * \ingroup Writer + * the writer function prototype + */ + +typedef struct ops_writer_info ops_writer_info_t; +typedef ops_boolean_t ops_writer_t(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); +typedef ops_boolean_t ops_writer_finaliser_t(ops_error_t **errors, + ops_writer_info_t *winfo); +typedef void ops_writer_destroyer_t(ops_writer_info_t *winfo); +/** Writer settings */ +struct ops_writer_info + { + ops_writer_t *writer; /*!< the writer itself */ + ops_writer_finaliser_t *finaliser; /*!< the writer's finaliser */ + ops_writer_destroyer_t *destroyer; /*!< the writer's destroyer */ + void *arg; /* writer-specific argument */ + ops_writer_info_t *next; /*!< next writer in the stack */ + }; + + +void *ops_writer_get_arg(ops_writer_info_t *winfo); +ops_boolean_t ops_stacked_write(const void *src,unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); + +void ops_writer_set(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg); +void ops_writer_push(ops_create_info_t *info, + ops_writer_t *writer, + ops_writer_finaliser_t *finaliser, + ops_writer_destroyer_t *destroyer, + void *arg); +void ops_writer_pop(ops_create_info_t *info); +void ops_writer_generic_destroyer(ops_writer_info_t *winfo); +ops_boolean_t ops_writer_passthrough(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); + +void ops_writer_set_fd(ops_create_info_t *info,int fd); +ops_boolean_t ops_writer_close(ops_create_info_t *info); + +ops_boolean_t ops_write(const void *src,unsigned length, + ops_create_info_t *opt); +ops_boolean_t ops_write_length(unsigned length,ops_create_info_t *opt); +ops_boolean_t ops_write_ptag(ops_content_tag_t tag,ops_create_info_t *opt); +ops_boolean_t ops_write_scalar(unsigned n,unsigned length, + ops_create_info_t *opt); +ops_boolean_t ops_write_mpi(const BIGNUM *bn,ops_create_info_t *opt); +ops_boolean_t ops_write_encrypted_mpi(const BIGNUM *bn, ops_crypt_t* crypt, ops_create_info_t *info); + +void writer_info_delete(ops_writer_info_t *winfo); +ops_boolean_t writer_info_finalise(ops_error_t **errors, ops_writer_info_t *winfo); + +#endif diff --git a/openpgpsdk/src/openpgpsdk/writer_armour.c b/openpgpsdk/src/openpgpsdk/writer_armour.c new file mode 100644 index 000000000..cd828e6a6 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_armour.c @@ -0,0 +1,517 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#include + +static int debug=0; + +#define LINE_LENGTH 64 + +static const char newline[] = "\r\n"; + +/** + * \struct dash_escaped_arg_t + */ +typedef struct + { + ops_boolean_t seen_nl:1; + ops_boolean_t seen_cr:1; + ops_create_signature_t *sig; + ops_memory_t *trailing; + } dash_escaped_arg_t; + +static ops_boolean_t dash_escaped_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + dash_escaped_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"dash_escaped_writer writing %d:\n", length); + for (i=0; iseen_nl) + { + if(src[n] == '-' && !ops_stacked_write("- ",2,errors,winfo)) + return ops_false; + arg->seen_nl=ops_false; + } + + arg->seen_nl=src[n] == '\n'; + + if(arg->seen_nl && !arg->seen_cr) + { + if(!ops_stacked_write("\r",1,errors,winfo)) + return ops_false; + ops_signature_add_data(arg->sig,"\r",1); + } + + arg->seen_cr=src[n] == '\r'; + + if(!ops_stacked_write(&src[n],1,errors,winfo)) + return ops_false; + + /* trailing whitespace isn't included in the signature */ + if(src[n] == ' ' || src[n] == '\t') + ops_memory_add(arg->trailing,&src[n],1); + else + { + if((l=ops_memory_get_length(arg->trailing))) + { + if(!arg->seen_nl && !arg->seen_cr) + ops_signature_add_data(arg->sig, + ops_memory_get_data(arg->trailing), + l); + ops_memory_clear(arg->trailing); + } + ops_signature_add_data(arg->sig,&src[n],1); + } + } + + return ops_true; + } + +/** + * \param winfo + */ +static void dash_escaped_destroyer(ops_writer_info_t *winfo) + { + dash_escaped_arg_t *arg=ops_writer_get_arg(winfo); + + ops_memory_free(arg->trailing); + free(arg); + } + +/** + * \ingroup Core_WritersNext + * \brief Push Clearsigned Writer onto stack + * \param info + * \param sig + */ +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig) + { + static char header[]="-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: "; + const char *hash=ops_text_from_hash(ops_signature_get_hash(sig)); + dash_escaped_arg_t *arg=ops_mallocz(sizeof *arg); + + ops_boolean_t rtn; + + rtn= ( ops_write(header,sizeof header-1,info) + && ops_write(hash,strlen(hash),info) + && ops_write("\r\n\r\n",4,info)); + + if (rtn==ops_false) + { + OPS_ERROR(&info->errors, OPS_E_W, "Error pushing clearsigned header"); + free(arg); + return rtn; + } + + arg->seen_nl=ops_true; + arg->sig=sig; + arg->trailing=ops_memory_new(); + ops_writer_push(info,dash_escaped_writer,NULL,dash_escaped_destroyer,arg); + return rtn; + } + + +/** + * \struct base64_arg_t + */ +typedef struct + { + size_t chars_written; + ops_boolean_t writing_trailer; + unsigned pos; + unsigned char t; + unsigned checksum; + } base64_arg_t; + +static char b64map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +"0123456789+/"; + +static ops_boolean_t check_newline(base64_arg_t* arg, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + arg->chars_written++; + if (!arg->writing_trailer && arg->chars_written % LINE_LENGTH == 0) + if (!ops_stacked_write(newline, strlen(newline), errors, winfo)) + return ops_false; + return ops_true; + } + +static ops_boolean_t base64_writer(const unsigned char *src, + unsigned length,ops_error_t **errors, + ops_writer_info_t *winfo) + { + base64_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + for(n=0 ; n < length ; ) + { + arg->checksum=ops_crc24(arg->checksum,src[n]); + if(arg->pos == 0) + { + /* XXXXXX00 00000000 00000000 */ + if(!ops_stacked_write(&b64map[src[n] >> 2],1,errors,winfo)) + return ops_false; + + /* 000000XX xxxx0000 00000000 */ + arg->t=(src[n++]&3) << 4; + arg->pos=1; + } + else if(arg->pos == 1) + { + /* 000000xx XXXX0000 00000000 */ + arg->t+=src[n] >> 4; + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + + /* 00000000 0000XXXX xx000000 */ + arg->t=(src[n++]&0xf) << 2; + arg->pos=2; + } + else if(arg->pos == 2) + { + /* 00000000 0000xxxx XX000000 */ + arg->t+=src[n] >> 6; + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(!check_newline(arg, errors, winfo)) + return ops_false; + + /* 00000000 00000000 00XXXXXX */ + if(!ops_stacked_write(&b64map[src[n++]&0x3f],1,errors,winfo)) + return ops_false; + + arg->pos=0; + } + if (!check_newline(arg, errors, winfo)) + return ops_false; + } + return ops_true; + } + +static ops_boolean_t signature_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + base64_arg_t *arg=ops_writer_get_arg(winfo); + static char trailer[]="\r\n-----END PGP SIGNATURE-----\r\n"; + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer. Turn off the + writing_body flag so we don't put a newline in the trailer. + */ + arg->writing_trailer = ops_true; + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(trailer,sizeof trailer-1,errors,winfo); + } + +/** + * \struct linebreak_arg_t + */ +typedef struct + { + unsigned pos; + } linebreak_arg_t; + +#define BREAKPOS 65 + +static ops_boolean_t linebreak_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + linebreak_arg_t *arg=ops_writer_get_arg(winfo); + unsigned n; + + for(n=0 ; n < length ; ++n,++arg->pos) + { + if(src[n] == '\r' || src[n] == '\n') + arg->pos=0; + + if(arg->pos == BREAKPOS) + { + if(!ops_stacked_write(newline,strlen(newline),errors,winfo)) + return ops_false; + arg->pos=0; + } + if(!ops_stacked_write(&src[n],1,errors,winfo)) + return ops_false; + } + + return ops_true; + } + +/** + * \ingroup Core_WritersNext + * \brief Push armoured signature on stack + * \param info + */ +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info) + { + static char header[]="\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + base64_arg_t *base64; + + ops_writer_pop(info); + if (ops_write(header,sizeof header-1,info)==ops_false) + { + OPS_ERROR(&info->errors, OPS_E_W, "Error switching to armoured signature"); + return ops_false; + } + + ops_writer_push(info,linebreak_writer,NULL,ops_writer_generic_destroyer, + ops_mallocz(sizeof(linebreak_arg_t))); + + base64=ops_mallocz(sizeof *base64); + if (!base64) + { + OPS_MEMORY_ERROR(&info->errors); + return ops_false; + } + base64->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,signature_finaliser, + ops_writer_generic_destroyer,base64); + return ops_true; + } + +static ops_boolean_t armoured_message_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + // TODO: This is same as signature_finaliser apart from trailer. + base64_arg_t *arg=ops_writer_get_arg(winfo); + static char trailer[]="\r\n-----END PGP MESSAGE-----\r\n"; + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer. Turn off the + writing_body flag so we don't put a newline in the trailer. + */ + arg->writing_trailer = ops_true; + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(trailer,sizeof trailer-1,errors,winfo); + } + +/** + \ingroup Core_WritersNext + \brief Write a PGP MESSAGE + \todo replace with generic function +*/ +void ops_writer_push_armoured_message(ops_create_info_t *info) +// ops_create_signature_t *sig) + { + static char header[]="-----BEGIN PGP MESSAGE-----\r\n"; + + base64_arg_t *base64; + + ops_write(header,sizeof header-1,info); + ops_write(newline,strlen(newline),info); + base64=ops_mallocz(sizeof *base64); + base64->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,armoured_message_finaliser,ops_writer_generic_destroyer,base64); + } + +static ops_boolean_t armoured_finaliser(ops_armor_type_t type, ops_error_t **errors, + ops_writer_info_t *winfo) + { + static char tail_public_key[]="\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"; + static char tail_private_key[]="\r\n-----END PGP PRIVATE KEY BLOCK-----\r\n"; + + char* tail=NULL; + unsigned int sz_tail=0; + + switch(type) + { + case OPS_PGP_PUBLIC_KEY_BLOCK: + tail=tail_public_key; + sz_tail=sizeof tail_public_key-1; + break; + + case OPS_PGP_PRIVATE_KEY_BLOCK: + tail=tail_private_key; + sz_tail=sizeof tail_private_key-1; + break; + + default: + assert(0); + } + + base64_arg_t *arg=ops_writer_get_arg(winfo); + unsigned char c[3]; + + if(arg->pos) + { + if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo)) + return ops_false; + if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo)) + return ops_false; + if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo)) + return ops_false; + } + + /* Ready for the checksum */ + if(!ops_stacked_write("\r\n=",3,errors,winfo)) + return ops_false; + + arg->pos=0; /* get ready to write the checksum */ + + c[0]=arg->checksum >> 16; + c[1]=arg->checksum >> 8; + c[2]=arg->checksum; + /* push the checksum through our own writer. Turn off the + writing_body flag so we don't put a newline in the trailer. + */ + arg->writing_trailer = ops_true; + if(!base64_writer(c,3,errors,winfo)) + return ops_false; + + return ops_stacked_write(tail,sz_tail,errors,winfo); + } + +static ops_boolean_t armoured_public_key_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + return armoured_finaliser(OPS_PGP_PUBLIC_KEY_BLOCK,errors,winfo); + } + +static ops_boolean_t armoured_private_key_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + return armoured_finaliser(OPS_PGP_PRIVATE_KEY_BLOCK,errors,winfo); + } + +// \todo use this for other armoured types +/** + \ingroup Core_WritersNext + \brief Push Armoured Writer on stack (generic) +*/ +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type) + { + static char hdr_public_key[]="-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + static char hdr_private_key[]="-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: " + OPS_VERSION_STRING "\r\n\r\n"; + + char* header=NULL; + unsigned int sz_hdr=0; + ops_boolean_t (* finaliser)(ops_error_t **errors, ops_writer_info_t *winfo); + + switch(type) + { + case OPS_PGP_PUBLIC_KEY_BLOCK: + header=hdr_public_key; + sz_hdr=sizeof hdr_public_key-1; + finaliser=armoured_public_key_finaliser; + break; + + case OPS_PGP_PRIVATE_KEY_BLOCK: + header=hdr_private_key; + sz_hdr=sizeof hdr_private_key-1; + finaliser=armoured_private_key_finaliser; + break; + + default: + assert(0); + } + + ops_write(header,sz_hdr,info); + + ops_writer_push(info,linebreak_writer,NULL,ops_writer_generic_destroyer, + ops_mallocz(sizeof(linebreak_arg_t))); + + base64_arg_t *arg=ops_mallocz(sizeof *arg); + arg->checksum=CRC24_INIT; + ops_writer_push(info,base64_writer,finaliser,ops_writer_generic_destroyer,arg); + } + + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_armoured.h b/openpgpsdk/src/openpgpsdk/writer_armoured.h new file mode 100644 index 000000000..bf81141b8 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_armoured.h @@ -0,0 +1,31 @@ +/* + * 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 + +ops_boolean_t ops_writer_push_clearsigned(ops_create_info_t *info, + ops_create_signature_t *sig); +void ops_writer_push_armoured_message(ops_create_info_t *info); +ops_boolean_t ops_writer_switch_to_armoured_signature(ops_create_info_t *info); + +void ops_writer_push_armoured(ops_create_info_t *info, ops_armor_type_t type); + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_encrypt.c b/openpgpsdk/src/openpgpsdk/writer_encrypt.c new file mode 100644 index 000000000..4224fca42 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_encrypt.c @@ -0,0 +1,122 @@ +/* + * 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 +#include +#include + +static int debug=0; + +typedef struct + { + ops_crypt_t* crypt; + int free_crypt; + } crypt_arg_t; + +/* + * This writer simply takes plaintext as input, + * encrypts it with the given key + * and outputs the resulting encrypted text + */ +static ops_boolean_t encrypt_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + +#define BUFSZ 1024 // arbitrary number + unsigned char encbuf[BUFSZ]; + unsigned remaining=length; + unsigned done=0; + + crypt_arg_t *arg=(crypt_arg_t *)ops_writer_get_arg(winfo); + + if (!ops_is_sa_supported(arg->crypt->algorithm)) + assert(0); // \todo proper error handling + + while (remaining) + { + unsigned len = remaining < BUFSZ ? remaining : BUFSZ; + // memcpy(buf,src,len); // \todo copy needed here? + + arg->crypt->cfb_encrypt(arg->crypt, encbuf, src+done, len); + + if (debug) + { + int i=0; + fprintf(stderr,"WRITING:\nunencrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", src[done+i]); + fprintf(stderr,"\n"); + fprintf(stderr,"encrypted: "); + for (i=0; i<16; i++) + fprintf(stderr,"%2x ", encbuf[i]); + fprintf(stderr,"\n"); + } + + if (!ops_stacked_write(encbuf,len,errors,winfo)) + { + if (debug) + { fprintf(stderr, "encrypted_writer got error from stacked write, returning\n"); } + return ops_false; + } + remaining-=len; + done+=len; + } + + return ops_true; + } + +static void encrypt_destroyer (ops_writer_info_t *winfo) + + { + crypt_arg_t *arg=(crypt_arg_t *)ops_writer_get_arg(winfo); + if (arg->free_crypt) + free(arg->crypt); + free (arg); + } + +/** +\ingroup Core_WritersNext +\brief Push Encrypted Writer onto stack (create SE packets) +*/ +void ops_writer_push_encrypt_crypt(ops_create_info_t *cinfo, + ops_crypt_t *crypt) + { + // Create arg to be used with this writer + // Remember to free this in the destroyer + + crypt_arg_t *arg=ops_mallocz(sizeof *arg); + + // Setup the arg + + arg->crypt=crypt; + arg->free_crypt=0; + + // And push writer on stack + ops_writer_push(cinfo,encrypt_writer,NULL,encrypt_destroyer,arg); + + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_encrypt_se_ip.c b/openpgpsdk/src/openpgpsdk/writer_encrypt_se_ip.c new file mode 100644 index 000000000..0c1b2c78d --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_encrypt_se_ip.c @@ -0,0 +1,247 @@ +/* + * 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 +#include + +#ifndef WIN32 + #include +#endif + +#include + +#include "keyring_local.h" +#include +#include +#include +#include +#include +#include + +static int debug=0; + +typedef struct + { + ops_crypt_t* crypt; + } encrypt_se_ip_arg_t; + +static ops_boolean_t encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); +static void encrypt_se_ip_destroyer(ops_writer_info_t *winfo); + +// + +/** +\ingroup Core_WritersNext +\brief Push Encrypted SE IP Writer onto stack +*/ +void ops_writer_push_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key) + { + ops_crypt_t *encrypt; + unsigned char *iv=NULL; + + // Create arg to be used with this writer + // Remember to free this in the destroyer + encrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + + // Create and write encrypted PK session key + ops_pk_session_key_t *encrypted_pk_session_key; + encrypted_pk_session_key=ops_create_pk_session_key(pub_key); + ops_write_pk_session_key(cinfo, encrypted_pk_session_key); + + // Setup the arg + encrypt=ops_mallocz(sizeof *encrypt); + ops_crypt_any(encrypt, encrypted_pk_session_key->symmetric_algorithm); + iv=ops_mallocz(encrypt->blocksize); + encrypt->set_iv(encrypt, iv); + encrypt->set_key(encrypt, &encrypted_pk_session_key->key[0]); + ops_encrypt_init(encrypt); + + arg->crypt=encrypt; + + // And push writer on stack + ops_writer_push(cinfo, encrypt_se_ip_writer, NULL, encrypt_se_ip_destroyer, + arg); + // tidy up + ops_pk_session_key_free(encrypted_pk_session_key); + free(encrypted_pk_session_key); + free(iv); + } + +static ops_boolean_t encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_boolean_t rtn=ops_true; + + ops_memory_t *mem_literal; + ops_create_info_t *cinfo_literal; + + ops_memory_t *mem_compressed; + ops_create_info_t *cinfo_compressed; + + ops_memory_t *my_mem; + ops_create_info_t *my_cinfo; + + const unsigned int bufsz=128; // initial value; gets expanded as necessary + ops_setup_memory_write(&cinfo_literal, &mem_literal, bufsz); + ops_setup_memory_write(&cinfo_compressed, &mem_compressed, bufsz); + ops_setup_memory_write(&my_cinfo, &my_mem, bufsz); + + // create literal data packet from source data + ops_write_literal_data_from_buf(src, length, OPS_LDT_BINARY, cinfo_literal); + assert(ops_memory_get_length(mem_literal) > length); + + // create compressed packet from literal data packet + ops_write_compressed(ops_memory_get_data(mem_literal), + ops_memory_get_length(mem_literal), + cinfo_compressed); + + // create SE IP packet set from this compressed literal data + ops_write_se_ip_pktset(ops_memory_get_data(mem_compressed), + ops_memory_get_length(mem_compressed), + arg->crypt, my_cinfo); + assert(ops_memory_get_length(my_mem) + > ops_memory_get_length(mem_compressed)); + + // now write memory to next writer + rtn=ops_stacked_write(ops_memory_get_data(my_mem), + ops_memory_get_length(my_mem), + errors, winfo); + + ops_memory_free(my_mem); + ops_memory_free(mem_compressed); + ops_memory_free(mem_literal); + + return rtn; + } + +static void encrypt_se_ip_destroyer (ops_writer_info_t *winfo) + + { + encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + free(arg->crypt); + free(arg); + } + +ops_boolean_t ops_write_se_ip_pktset(const unsigned char *data, + const unsigned int len, + ops_crypt_t *crypt, + ops_create_info_t *cinfo) + { + unsigned char hashed[SHA_DIGEST_LENGTH]; + const size_t sz_mdc=1+1+SHA_DIGEST_LENGTH; + + size_t sz_preamble=crypt->blocksize+2; + unsigned char* preamble=ops_mallocz(sz_preamble); + + size_t sz_buf=sz_preamble+len+sz_mdc; + + ops_memory_t *mem_mdc; + ops_create_info_t *cinfo_mdc; + + if (!ops_write_ptag(OPS_PTAG_CT_SE_IP_DATA, cinfo) + || !ops_write_length(1+sz_buf, cinfo) + || !ops_write_scalar(SE_IP_DATA_VERSION, 1, cinfo)) + { + free (preamble); + return 0; + } + + ops_random(preamble, crypt->blocksize); + preamble[crypt->blocksize]=preamble[crypt->blocksize-2]; + preamble[crypt->blocksize+1]=preamble[crypt->blocksize-1]; + + if (debug) + { + unsigned int i=0; + fprintf(stderr,"\npreamble: "); + for (i=0; i +#include +#include +#endif + +#include + +#include + +typedef struct + { + int fd; + } writer_fd_arg_t; + +static ops_boolean_t fd_writer(const unsigned char *src,unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + writer_fd_arg_t *arg=ops_writer_get_arg(winfo); + int n=write(arg->fd,src,length); + + if(n == -1) + { + OPS_SYSTEM_ERROR_1(errors,OPS_E_W_WRITE_FAILED,"write", + "file descriptor %d",arg->fd); + return ops_false; + } + + if((unsigned)n != length) + { + OPS_ERROR_1(errors,OPS_E_W_WRITE_TOO_SHORT, + "file descriptor %d",arg->fd); + return ops_false; + } + + return ops_true; + } + +static void fd_destroyer(ops_writer_info_t *winfo) + { + free(ops_writer_get_arg(winfo)); + } + +/** + * \ingroup Core_WritersFirst + * \brief Write to a File + * + * Set the writer in info to be a stock writer that writes to a file + * descriptor. If another writer has already been set, then that is + * first destroyed. + * + * \param info The info structure + * \param fd The file descriptor + * + */ + +void ops_writer_set_fd(ops_create_info_t *info,int fd) + { + writer_fd_arg_t *arg=malloc(sizeof *arg); + + arg->fd=fd; + ops_writer_set(info,fd_writer,NULL,fd_destroyer,arg); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_literal.c b/openpgpsdk/src/openpgpsdk/writer_literal.c new file mode 100644 index 000000000..320962ce4 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_literal.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2005-2009 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer, Alasdair Mackintosh. + * 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. + */ + +/** Writes a literal data packet, using the partial data length encoding. + */ + +#include +#include + +#ifndef WIN32 +#include +#endif + +#include +#include +#include + +#define MIN_PARTIAL_DATA_LENGTH 512 +#define MAX_PARTIAL_DATA_LENGTH 1073741824 + + +ops_boolean_t write_literal_header(ops_create_info_t *info, + void *header_data) + { + OPS_USED(header_data); + // \todo add the literal type as a header_data argument + // \todo add filename + // \todo add date + // \todo do we need to check text data for line endings ? + + ops_write_scalar(OPS_LDT_BINARY, 1, info); // data type + ops_write_scalar(0, 1, info); // Filename (length = 0) + ops_write_scalar(0, 4, info); // Date (unspecified) + return ops_true; + } + +/** + * \ingroup InternalAPI + * \brief Pushes a literal writer onto the stack. + * \param cinfo the writer info + * \param buf_size the size of the internal buffer. For best + * throughput, write data in multiples of buf_size + */ +void ops_writer_push_literal_with_opts(ops_create_info_t *cinfo, + unsigned int buf_size) + { + // The literal writer doesn't need to transform the data, so we just + // push a partial packet writer onto the stack. This will handle + // the packet length encoding. All we need to provide is a function + // to write the header. + ops_writer_push_partial(buf_size, cinfo, OPS_PTAG_CT_LITERAL_DATA, + write_literal_header, NULL); + +} + +/** + * \ingroup InternalAPI + * \brief Pushes a literal writer onto the stack. + * \param cinfo the writer info + */ +void ops_writer_push_literal(ops_create_info_t *cinfo) + { + ops_writer_push_literal_with_opts(cinfo, 0); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_memory.c b/openpgpsdk/src/openpgpsdk/writer_memory.c new file mode 100644 index 000000000..0c8fa57c4 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_memory.c @@ -0,0 +1,61 @@ +/* + * 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 +#include +#include +#include + +#include + +static ops_boolean_t memory_writer(const unsigned char *src,unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + ops_memory_t *mem=ops_writer_get_arg(winfo); + + OPS_USED(errors); + ops_memory_add(mem,src,length); + return ops_true; + } + +/** + * \ingroup Core_WritersFirst + * \brief Write to memory + * + * Set a memory writer. + * + * \param info The info structure + * \param mem The memory structure + * \note It is the caller's responsiblity to call ops_memory_free(mem) + * \sa ops_memory_free() + */ + +void ops_writer_set_memory(ops_create_info_t *info,ops_memory_t *mem) + { + ops_writer_set(info,memory_writer,NULL,NULL,mem); + } + + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_partial.c b/openpgpsdk/src/openpgpsdk/writer_partial.c new file mode 100644 index 000000000..4e9c62a84 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_partial.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2005-2009 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer, Alasdair Mackintosh. + * 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. + */ + +/** Writes data using a series of partial body length headers. + * (See RFC 4880 4.2.2.4). This is normally used in conjunction + * with a streaming writer of some kind that needs to write out + * data packets of unknown length. + */ + +#include +#include + +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include + +static const int debug = 0; + +#define PACKET_SIZE 2048 +#define MIN_PARTIAL_DATA_LENGTH 512 +#define MAX_PARTIAL_DATA_LENGTH 1073741824 + +typedef struct + { + size_t packet_size; // size of packets + ops_memory_t *buffer; // Data is buffered here until written + ops_content_tag_t tag; // Packet tag + ops_memory_t *header; // Header is written here + ops_boolean_t written_first; // Has the first packet been written? + ops_write_partial_trailer_t *trailer_fn; // Custom end-of-packet fn + void *trailer_data; // data for end-of-packet fn + } stream_partial_arg_t; + + + +static unsigned int ops_calc_partial_data_length(unsigned int len) + { + int i; + unsigned int mask = MAX_PARTIAL_DATA_LENGTH; + assert( len > 0 ); + + if (len > MAX_PARTIAL_DATA_LENGTH) + return MAX_PARTIAL_DATA_LENGTH; + + for (i = 0 ; i <= 30 ; i++) + { + if (mask & len) + break; + mask >>= 1; + } + + return mask; + } + +static ops_boolean_t ops_write_partial_data_length(unsigned int len, + ops_create_info_t *info) + { + // len must be a power of 2 from 0 to 30 + unsigned i; + unsigned char c[1]; + + for (i = 0 ; i <= 30 ; i++) + if ((len >> i) & 1) + break; + + assert((1u << i) == len); + + c[0] = 224 + i; + + return ops_write(c, 1, info); + } + +static ops_boolean_t write_partial_data(const unsigned char *data, + size_t len, + ops_create_info_t *info) + { + if (debug) + fprintf(stderr, "Writing %zu bytes\n", len); + while (len > 0) + { + size_t pdlen = ops_calc_partial_data_length(len); + ops_write_partial_data_length(pdlen, info); + ops_write(data, pdlen, info); + data += pdlen; + len -= pdlen; + } + return ops_true; + } + +static ops_boolean_t write_partial_data_first(stream_partial_arg_t *arg, + const unsigned char *data, + unsigned int len, + ops_create_info_t *info) + { + size_t header_len = ops_memory_get_length(arg->header); + + size_t sz_towrite = len + header_len; + size_t sz_pd = ops_calc_partial_data_length(sz_towrite); + size_t first_data_len = (sz_pd - header_len); + assert(sz_pd >= MIN_PARTIAL_DATA_LENGTH); + + if (debug) + fprintf(stderr, "Writing first packet of len %zu (%zu + %u)\n", + sz_towrite, header_len, len); + + // Write the packet tag, the partial size and the header, followed + // by the first chunk of data and then the remainder of the data. + // (We have to do this in two chunks, as the partial length may not + // match the number of bytes to write.) + return ops_write_ptag(arg->tag, info) && + ops_write_partial_data_length(sz_pd, info) && + ops_write(ops_memory_get_data(arg->header), header_len, info) && + ops_write(data, first_data_len, info) && + write_partial_data(data + first_data_len, len - first_data_len, info); + } + +/* + * Writes out the last packet. The length is encoded as a fixed-length + * packet. Note that even if there is no data accumulated in the + * buffer, we stil lneed to write out a packet, as the final packet in + * a partially-encoded stream must be a fixed-lngth packet. + */ +static ops_boolean_t write_partial_data_last(stream_partial_arg_t *arg, + ops_create_info_t *info) + { + size_t buffer_length = ops_memory_get_length(arg->buffer); + if (debug) + fprintf(stderr, "writing final packet of %zu bytes\n", buffer_length); + return ops_write_length(buffer_length, info) && + ops_write(ops_memory_get_data(arg->buffer), buffer_length, info); + } + +/* + * Writes out the data accumulated in the in-memory buffer. + */ +static ops_boolean_t flush_buffer(stream_partial_arg_t *arg, + ops_create_info_t *info) + { + ops_boolean_t result = ops_true; + size_t buffer_length = ops_memory_get_length(arg->buffer); + if (buffer_length > 0) + { + if (debug) + fprintf(stderr, "Flushing %zu bytes\n", buffer_length); + + result = write_partial_data(ops_memory_get_data(arg->buffer), + buffer_length, + info); + ops_memory_clear(arg->buffer); + } + return result; + } + +static ops_boolean_t stream_partial_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + stream_partial_arg_t *arg = ops_writer_get_arg(winfo); + + // For the first write operation, we need to write out the header + // plus the data. The total size that we write out must be at least + // MIN_PARTIAL_DATA_LENGTH bytes. (See RFC 4880, sec 4.2.2.4, + // Partial Body Lengths.) If we are given less than this, + // then we need to store the data in the buffer until we have the + // minumum + if (!arg->written_first) + { + ops_memory_add(arg->buffer, src, length); + size_t buffer_length = ops_memory_get_length(arg->buffer); + size_t header_length = ops_memory_get_length(arg->header); + if (header_length + buffer_length < MIN_PARTIAL_DATA_LENGTH) + { + if (debug) + fprintf(stderr, "Storing %zu (%zu + %zu) bytes\n", + header_length + buffer_length, header_length, + buffer_length); + return ops_true; // will wait for more data or end of stream + } + arg->written_first = ops_true; + + // Create a writer that will write to the parent stream. Allows + // useage of ops_write_ptag, etc. + ops_create_info_t parent_info; + ops_prepare_parent_info(&parent_info, winfo); + ops_boolean_t result = + write_partial_data_first(arg, ops_memory_get_data(arg->buffer), + buffer_length, &parent_info); + ops_memory_clear(arg->buffer); + ops_move_errors(&parent_info, errors); + return result; + } + else + { + size_t buffer_length = ops_memory_get_length(arg->buffer); + if (buffer_length + length < arg->packet_size) + { + ops_memory_add(arg->buffer, src, length); + if (debug) + fprintf(stderr, "Storing %u bytes (total %zu)\n", + length, buffer_length); + return ops_true; + } + else + { + ops_create_info_t parent_info; + parent_info.winfo = *winfo->next; + parent_info.errors = *errors; + return flush_buffer(arg, &parent_info) && + write_partial_data(src, length, &parent_info); + } + } + return ops_true; + } + +/* + * Invoked when the total packet size is less than + * MIN_PARTIAL_DATA_LENGTH. In that case, we write out the whole + * packet in a single operation, without using partial body length + * packets. + */ +static ops_boolean_t write_complete_packet(stream_partial_arg_t *arg, + ops_create_info_t *info) + { + size_t data_len = ops_memory_get_length(arg->buffer); + size_t header_len = ops_memory_get_length(arg->header); + + // Write the header tag, the length of the packet, and the + // packet. Note that the packet includes the header + // bytes. + size_t total = data_len + header_len; + if (debug) + fprintf(stderr, "writing entire packet with length %zu (%zu + %zu)\n", + total, data_len, header_len); + return ops_write_ptag(arg->tag, info) && + ops_write_length(total, info) && + ops_write(ops_memory_get_data(arg->header), header_len, info) && + ops_write(ops_memory_get_data(arg->buffer), data_len, info); + } + +static ops_boolean_t stream_partial_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + stream_partial_arg_t *arg = ops_writer_get_arg(winfo); + // write last chunk of data + + // Create a writer that will write to the parent stream. Allows + // useage of ops_write_ptag, etc. + ops_create_info_t parent_info; + ops_prepare_parent_info(&parent_info, winfo); + ops_boolean_t result; + if (!arg->written_first) + result = write_complete_packet(arg, &parent_info); + else + // finish writing + result = write_partial_data_last(arg, &parent_info); + if (result && arg->trailer_fn != NULL) + result = arg->trailer_fn(&parent_info, arg->trailer_data); + ops_move_errors(&parent_info, errors); + return result; + } + +static void stream_partial_destroyer(ops_writer_info_t *winfo) + { + stream_partial_arg_t *arg = ops_writer_get_arg(winfo); + ops_memory_free(arg->buffer); + ops_memory_free(arg->header); + free(arg); + } + +/** + * \ingroup InternalAPI + * \brief Pushes a partial packet writer onto the stack. + * + * This writer is used in conjunction with another writer that + * generates streaming data of unknown length. The partial writer + * handles the various partial body length packets. When writing the + * initial packet header, the partial writer will write out the given + * tag, write out an initial length, and then invoke the 'header' + * function to write the remainder of the header. Note that the header + * function should not write a packet tag or a length. + * + * \param packet_size the expected size of the incoming packets. Must + * be >= 512 bytes. Must be a power of 2. The partial writer + * will buffer incoming writes into packets of this size. Note + * that writes will be most efficient if done in chunks of + * packet_size. If the packet size is unknown, specify 0, and + * the default size will be used. + * \param cinfo the writer info + * \param tag the packet tag + * \param header_writer a function that writes the packet header. + * \param header_data passed into header_writer + */ +void ops_writer_push_partial(size_t packet_size, + ops_create_info_t *cinfo, + ops_content_tag_t tag, + ops_write_partial_header_t *header_writer, + void *header_data) + { + ops_writer_push_partial_with_trailer(packet_size, cinfo, tag, header_writer, + header_data, NULL, NULL); + } + +/** + * \ingroup InternalAPI + * \brief Pushes a partial packet writer onto the stack. Adds a trailer + * function that will be invoked after writing out the partial + * packet. + * + * This writer is primarily used by the signature writer, which needs + * to append a signature packet after the literal data packet. + * + * \param trailer_writer a function that writes the trailer + * \param trailer_data passed into trailer_data + * \see ops_writer_push_partial + * \see ops_writer_push_signed + */ +void ops_writer_push_partial_with_trailer( + size_t packet_size, + ops_create_info_t *cinfo, + ops_content_tag_t tag, + ops_write_partial_header_t *header_writer, + void *header_data, + ops_write_partial_trailer_t *trailer_writer, + void *trailer_data) + { + if (packet_size == 0) + packet_size = PACKET_SIZE; + assert(packet_size >= MIN_PARTIAL_DATA_LENGTH); + // Verify that the packet size is a valid power of 2. + assert(ops_calc_partial_data_length(packet_size) == packet_size); + + // Create arg to be used with this writer + // Remember to free this in the destroyer + stream_partial_arg_t *arg = ops_mallocz(sizeof *arg); + arg->tag = tag; + arg->written_first = ops_false; + arg->packet_size = packet_size; + arg->buffer = ops_memory_new(); + ops_memory_init(arg->buffer, arg->packet_size); + arg->trailer_fn = trailer_writer; + arg->trailer_data = trailer_data; + + // Write out the header into the memory buffer. Later we will write + // this buffer to the underlying output stream. + ops_create_info_t *header_info; + ops_setup_memory_write(&header_info, &arg->header, 128); + header_writer(header_info, header_data); + ops_writer_close(header_info); + ops_create_info_delete(header_info); + + // And push writer on stack + ops_writer_push(cinfo, stream_partial_writer, stream_partial_finaliser, + stream_partial_destroyer, arg); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_skey_checksum.c b/openpgpsdk/src/openpgpsdk/writer_skey_checksum.c new file mode 100644 index 000000000..6b8a480de --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_skey_checksum.c @@ -0,0 +1,88 @@ +/* + * 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 + +#include + +//static int debug=0; + +typedef struct + { + ops_hash_algorithm_t hash_algorithm; + ops_hash_t hash; + unsigned char *hashed; + } skey_checksum_arg_t; + +static ops_boolean_t skey_checksum_writer(const unsigned char *src, const unsigned length, ops_error_t **errors, ops_writer_info_t *winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + ops_boolean_t rtn=ops_true; + + // add contents to hash + arg->hash.add(&arg->hash, src, length); + + // write to next stacked writer + rtn=ops_stacked_write(src,length,errors,winfo); + + // tidy up and return + return rtn; + } + +static ops_boolean_t skey_checksum_finaliser(ops_error_t **errors __attribute__((unused)), ops_writer_info_t *winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + arg->hash.finish(&arg->hash, arg->hashed); + return ops_true; + } + +static void skey_checksum_destroyer(ops_writer_info_t* winfo) + { + skey_checksum_arg_t *arg=ops_writer_get_arg(winfo); + free(arg); + } + +/** +\ingroup Core_WritersNext +\param cinfo +\param skey +*/ +void ops_push_skey_checksum_writer(ops_create_info_t *cinfo, ops_secret_key_t *skey) + { + // OPS_USED(info); + // XXX: push a SHA-1 checksum writer (and change s2k to 254). + skey_checksum_arg_t *arg=ops_mallocz(sizeof *arg); + + // configure the arg + arg->hash_algorithm=skey->hash_algorithm; + arg->hashed=&skey->checkhash[0]; + + // init the hash + ops_hash_any(&arg->hash, arg->hash_algorithm); + arg->hash.init(&arg->hash); + + ops_writer_push(cinfo, skey_checksum_writer, skey_checksum_finaliser, skey_checksum_destroyer, arg); + } + +// EOF diff --git a/openpgpsdk/src/openpgpsdk/writer_stream_encrypt_se_ip.c b/openpgpsdk/src/openpgpsdk/writer_stream_encrypt_se_ip.c new file mode 100644 index 000000000..5cda68fe2 --- /dev/null +++ b/openpgpsdk/src/openpgpsdk/writer_stream_encrypt_se_ip.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2005-2009 Nominet UK (www.nic.uk) + * All rights reserved. + * Contributors: Ben Laurie, Rachel Willmer, Alasdair Mackintosh. + * 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 +#include + +#ifndef WIN32 +#include +#endif + +#include + +#include "keyring_local.h" +#include +#include +#include +#include +#include + +typedef struct + { + ops_crypt_t*crypt; + ops_memory_t *mem_se_ip; + ops_create_info_t *cinfo_se_ip; + ops_hash_t hash; + } stream_encrypt_se_ip_arg_t; + +static ops_boolean_t write_encrypt_se_ip_header(ops_create_info_t *info, + void *header_data); + + +static ops_boolean_t stream_encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo); + +static ops_boolean_t stream_encrypt_se_ip_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo); + +static void stream_encrypt_se_ip_destroyer (ops_writer_info_t *winfo); + + +/** +\ingroup Core_WritersNext +\brief Pushes a streaming encryption writer onto the stack. + +Data written to the stream will be encoded in a Symmetrically +Encrypted Integrity Protected packet. Note that this writer must be +used in conjunction with a literal writer or a signed writer. + +\param cinfo +\param pub_key +Example Code: +\code + ops_writer_push_stream_encrypt_se_ip(cinfo, public_key); + if (compress) + ops_writer_push_compressed(cinfo); + if (sign) + ops_writer_push_signed(cinfo, OPS_SIG_BINARY, secret_key); + else + ops_writer_push_literal(cinfo); +\endcode +*/ + +void ops_writer_push_stream_encrypt_se_ip(ops_create_info_t *cinfo, + const ops_keydata_t *pub_key) + { + ops_crypt_t *encrypt; + unsigned char *iv=NULL; + const unsigned int bufsz=1024; // initial value; gets expanded as necessary + + // Create arg to be used with this writer + // Remember to free this in the destroyer + stream_encrypt_se_ip_arg_t *arg=ops_mallocz(sizeof *arg); + + // Create and write encrypted PK session key + ops_pk_session_key_t *encrypted_pk_session_key; + encrypted_pk_session_key=ops_create_pk_session_key(pub_key); + ops_write_pk_session_key(cinfo, encrypted_pk_session_key); + + // Setup the arg + encrypt=ops_mallocz(sizeof *encrypt); + ops_crypt_any(encrypt, encrypted_pk_session_key->symmetric_algorithm); + iv=ops_mallocz(encrypt->blocksize); + encrypt->set_iv(encrypt, iv); + encrypt->set_key(encrypt, &encrypted_pk_session_key->key[0]); + ops_encrypt_init(encrypt); + + arg->crypt=encrypt; + + ops_setup_memory_write(&arg->cinfo_se_ip, &arg->mem_se_ip, bufsz); + + ops_hash_any(&arg->hash, OPS_HASH_SHA1); + arg->hash.init(&arg->hash); + + // This is a streaming writer, so we don't know the length in + // advance. Use a partial writer to handle the partial body + // packet lengths. + ops_writer_push_partial(2048, cinfo, OPS_PTAG_CT_SE_IP_DATA, + write_encrypt_se_ip_header, arg); + + // And push encryption writer on stack + ops_writer_push(cinfo, + stream_encrypt_se_ip_writer, + stream_encrypt_se_ip_finaliser, + stream_encrypt_se_ip_destroyer, arg); + // tidy up + ops_pk_session_key_free(encrypted_pk_session_key); + free(encrypted_pk_session_key); + free(iv); + } + +static ops_boolean_t ops_stream_write_se_ip(const unsigned char *data, + unsigned int len, + stream_encrypt_se_ip_arg_t *arg, + ops_create_info_t *cinfo) + { + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + ops_write(data, len, cinfo); + ops_writer_pop(cinfo); + arg->hash.add(&arg->hash, data, len); + return ops_true; + } + +// Writes out the header for the encrypted packet. Invoked by the +// partial stream writer. Note that writing the packet tag and the +// packet length is handled by the partial stream writer. +static ops_boolean_t write_encrypt_se_ip_header(ops_create_info_t *cinfo, + void *data) + { + stream_encrypt_se_ip_arg_t *arg = data; + size_t sz_preamble = arg->crypt->blocksize + 2; + unsigned char* preamble = ops_mallocz(sz_preamble); + + ops_write_scalar(SE_IP_DATA_VERSION, 1, cinfo); + + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + + ops_random(preamble, arg->crypt->blocksize); + preamble[arg->crypt->blocksize]=preamble[arg->crypt->blocksize-2]; + preamble[arg->crypt->blocksize+1]=preamble[arg->crypt->blocksize-1]; + + ops_write(preamble, sz_preamble, cinfo); + arg->hash.add(&arg->hash, preamble, sz_preamble); + ops_writer_pop(cinfo); + free(preamble); + + return ops_true; + } + +static ops_boolean_t +ops_stream_write_se_ip_last(stream_encrypt_se_ip_arg_t *arg, + ops_create_info_t *cinfo) + { + unsigned char c[1]; + unsigned char hashed[SHA_DIGEST_LENGTH]; + const size_t sz_mdc = 1 + 1 + SHA_DIGEST_LENGTH; + + ops_memory_t *mem_mdc; + ops_create_info_t *cinfo_mdc; + + // MDC packet tag + c[0]=0xD3; + arg->hash.add(&arg->hash, &c[0], 1); + + // MDC packet len + c[0]=0x14; + arg->hash.add(&arg->hash, &c[0], 1); + + //finish + arg->hash.finish(&arg->hash, hashed); + + ops_setup_memory_write(&cinfo_mdc, &mem_mdc, sz_mdc); + ops_write_mdc(hashed, cinfo_mdc); + + // encode everthing + ops_writer_push_encrypt_crypt(cinfo, arg->crypt); + + ops_write(ops_memory_get_data(mem_mdc), ops_memory_get_length(mem_mdc), + cinfo); + + ops_writer_pop(cinfo); + + ops_teardown_memory_write(cinfo_mdc, mem_mdc); + + return ops_true; + } + +static ops_boolean_t stream_encrypt_se_ip_writer(const unsigned char *src, + unsigned length, + ops_error_t **errors, + ops_writer_info_t *winfo) + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_boolean_t rtn=ops_true; + + ops_stream_write_se_ip(src, length, + arg, arg->cinfo_se_ip); + // now write memory to next writer + rtn=ops_stacked_write(ops_memory_get_data(arg->mem_se_ip), + ops_memory_get_length(arg->mem_se_ip), + errors, winfo); + + ops_memory_clear(arg->mem_se_ip); + + return rtn; + } + +static ops_boolean_t stream_encrypt_se_ip_finaliser(ops_error_t **errors, + ops_writer_info_t *winfo) + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + // write trailer + ops_stream_write_se_ip_last(arg, arg->cinfo_se_ip); + // now write memory to next writer + return ops_stacked_write(ops_memory_get_data(arg->mem_se_ip), + ops_memory_get_length(arg->mem_se_ip), + errors, winfo); + } + +static void stream_encrypt_se_ip_destroyer(ops_writer_info_t *winfo) + + { + stream_encrypt_se_ip_arg_t *arg=ops_writer_get_arg(winfo); + + ops_teardown_memory_write(arg->cinfo_se_ip, arg->mem_se_ip); + arg->crypt->decrypt_finish(arg->crypt); + + free(arg->crypt); + free(arg); + } + +// EOF diff --git a/openpgpsdk/src/src.pro b/openpgpsdk/src/src.pro new file mode 100644 index 000000000..ebdee404b --- /dev/null +++ b/openpgpsdk/src/src.pro @@ -0,0 +1,99 @@ +TEMPLATE = lib +CONFIG = staticlib debug + +DEFINES *= OPENSSL_NO_IDEA + +QMAKE_CXXFLAGS *= -Wall -Werror -W + +TARGET = ops +DESTDIR = lib +DEPENDPATH += . +INCLUDEPATH += . + +#################################### Windows ##################################### + +linux-* { + OBJECTS_DIR = temp/linux/obj +} + +win32 { + SSL_DIR = ../../../../OpenSSL + ZLIB_DIR = ../../../zlib-1.2.3 + BZIP_DIR = ../../../bzip2-1.0.6 + + INCLUDEPATH += $${SSL_DIR}/include $${ZLIB_DIR} $${BZIP_DIR} +} + +# Input +HEADERS += openpgpsdk/writer.h \ + openpgpsdk/writer_armoured.h \ + openpgpsdk/version.h \ + openpgpsdk/validate.h \ + openpgpsdk/util.h \ + openpgpsdk/types.h \ + openpgpsdk/streamwriter.h \ + openpgpsdk/std_print.h \ + openpgpsdk/signature.h \ + openpgpsdk/readerwriter.h \ + openpgpsdk/random.h \ + openpgpsdk/partial.h \ + openpgpsdk/packet-show.h \ + openpgpsdk/packet-show-cast.h \ + openpgpsdk/packet-parse.h \ + openpgpsdk/packet.h \ + openpgpsdk/memory.h \ + openpgpsdk/literal.h \ + openpgpsdk/lists.h \ + openpgpsdk/keyring.h \ + openpgpsdk/hash.h \ + openpgpsdk/final.h \ + openpgpsdk/defs.h \ + openpgpsdk/errors.h \ + openpgpsdk/crypto.h \ + openpgpsdk/create.h \ + openpgpsdk/configure.h \ + openpgpsdk/compress.h \ + openpgpsdk/callback.h \ + openpgpsdk/accumulate.h \ + openpgpsdk/armour.h \ + openpgpsdk/parse_local.h \ + openpgpsdk/keyring_local.h + + +SOURCES += openpgpsdk/accumulate.c \ + openpgpsdk/compress.c \ + openpgpsdk/create.c \ + openpgpsdk/crypto.c \ + openpgpsdk/errors.c \ + openpgpsdk/fingerprint.c \ + openpgpsdk/hash.c \ + openpgpsdk/keyring.c \ + openpgpsdk/lists.c \ + openpgpsdk/memory.c \ + openpgpsdk/openssl_crypto.c \ + openpgpsdk/packet-parse.c \ + openpgpsdk/packet-print.c \ + openpgpsdk/packet-show.c \ + openpgpsdk/random.c \ + openpgpsdk/reader.c \ + openpgpsdk/reader_armoured.c \ + openpgpsdk/reader_encrypted_se.c \ + openpgpsdk/reader_encrypted_seip.c \ + openpgpsdk/reader_fd.c \ + openpgpsdk/reader_hashed.c \ + openpgpsdk/reader_mem.c \ + openpgpsdk/readerwriter.c \ + openpgpsdk/signature.c \ + openpgpsdk/symmetric.c \ + openpgpsdk/util.c \ + openpgpsdk/validate.c \ + openpgpsdk/writer.c \ + openpgpsdk/writer_armour.c \ + openpgpsdk/writer_partial.c \ + openpgpsdk/writer_literal.c \ + openpgpsdk/writer_encrypt.c \ + openpgpsdk/writer_encrypt_se_ip.c \ + openpgpsdk/writer_fd.c \ + openpgpsdk/writer_memory.c \ + openpgpsdk/writer_skey_checksum.c \ + openpgpsdk/writer_stream_encrypt_se_ip.c diff --git a/retroshare-gui/src/RetroShare.pro b/retroshare-gui/src/RetroShare.pro index d3a5b7886..5fd886b9e 100644 --- a/retroshare-gui/src/RetroShare.pro +++ b/retroshare-gui/src/RetroShare.pro @@ -26,16 +26,12 @@ linux-* { #CONFIG += version_detail_bash_script QMAKE_CXXFLAGS *= -D_FILE_OFFSET_BITS=64 - system(which gpgme-config >/dev/null 2>&1) { - INCLUDEPATH += $$system(gpgme-config --cflags | sed -e "s/-I//g") - } else { - message(Could not find gpgme-config on your system, assuming gpgme.h is in /usr/include) - } - PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a + PRE_TARGETDEPS *= ../../openpgpsdk/src/lib/libops.a LIBS += ../../libretroshare/src/lib/libretroshare.a - LIBS += -lssl -lgpgme -lupnp -lixml -lXss -lgnome-keyring + LIBS += ../../openpgpsdk/src/lib/libops.a -lbz2 + LIBS += -lssl -lupnp -lixml -lXss -lgnome-keyring LIBS *= -rdynamic DEFINES *= HAVE_XSS # for idle time, libx screensaver extensions DEFINES *= UBUNTU @@ -110,6 +106,7 @@ win32 { PRE_TARGETDEPS += ../../libretroshare/src/lib/libretroshare.a LIBS += ../../libretroshare/src/lib/libretroshare.a + LIBS += ../../openpgpsdk/src/lib/libops.a -lbz2 LIBS += -L"../../../lib" LIBS += -lssl -lcrypto -lgpgme -lpthreadGC2d -lminiupnpc -lz # added after bitdht diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index d143085cb..e92928d27 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "GenCertDialog.h" #include "InfoDialog.h" #include @@ -45,6 +46,8 @@ GenCertDialog::GenCertDialog(QWidget *parent, Qt::WFlags flags) connect(ui.new_gpg_key_checkbox, SIGNAL(clicked()), this, SLOT(newGPGKeyGenUiSetup())); connect(ui.genButton, SIGNAL(clicked()), this, SLOT(genPerson())); + connect(ui.importIdentity_PB, SIGNAL(clicked()), this, SLOT(importIdentity())); + connect(ui.exportIdentity_PB, SIGNAL(clicked()), this, SLOT(exportIdentity())); connect(ui.infopushButton,SIGNAL(clicked()), this, SLOT(infodlg())); //connect(ui.selectButton, SIGNAL(clicked()), this, SLOT(selectFriend())); //connect(ui.friendBox, SIGNAL(stateChanged(int)), this, SLOT(checkChanged(int))); @@ -71,7 +74,7 @@ GenCertDialog::GenCertDialog(QWidget *parent, Qt::WFlags flags) if (RsInit::GetPGPLogins(pgpIds)) { for(it = pgpIds.begin(); it != pgpIds.end(); it++) { - const QVariant & userData = QVariant(QString::fromStdString(*it)); + QVariant userData(QString::fromStdString(*it)); std::string name, email; RsInit::GetPGPLoginDetails(*it, name, email); std::cerr << "Adding PGPUser: " << name << " id: " << *it << std::endl; @@ -94,9 +97,9 @@ GenCertDialog::GenCertDialog(QWidget *parent, Qt::WFlags flags) ui.new_gpg_key_checkbox->setChecked(true); ui.new_gpg_key_checkbox->hide(); ui.progressBar->hide(); - setWindowTitle(tr("Create new Profile")); - ui.genButton->setText(tr("Generate new Profile")); - ui.label_3->setText( titleString.arg( tr("Create a new Profile") ) ) ; + setWindowTitle(tr("Create new Identity")); + ui.genButton->setText(tr("Generate new Identity")); + ui.label_3->setText( titleString.arg( tr("Create a new Identity") ) ) ; genNewGPGKey = true; } newGPGKeyGenUiSetup(); @@ -117,9 +120,11 @@ void GenCertDialog::newGPGKeyGenUiSetup() { ui.password_input->show(); ui.genPGPuserlabel->hide(); ui.genPGPuser->hide(); - setWindowTitle(tr("Create new Profile")); - ui.genButton->setText(tr("Generate new Profile")); - ui.label_3->setText( titleStr.arg( tr("Create a new Profile") ) ) ; + ui.exportIdentity_PB->hide() ; + ui.importIdentity_PB->hide() ; + setWindowTitle(tr("Create new Identity")); + ui.genButton->setText(tr("Generate new Identity")); + ui.label_3->setText( titleStr.arg( tr("Create a new Identity") ) ) ; } else { genNewGPGKey = false; ui.name_label->hide(); @@ -130,12 +135,55 @@ void GenCertDialog::newGPGKeyGenUiSetup() { ui.password_input->hide(); ui.genPGPuserlabel->show(); ui.genPGPuser->show(); + ui.exportIdentity_PB->show() ; + ui.importIdentity_PB->show() ; setWindowTitle(tr("Create new Location")); ui.genButton->setText(tr("Generate new Location")); ui.label_3->setText( titleStr.arg( tr("Create a new Location") ) ) ; } } +void GenCertDialog::exportIdentity() +{ + QString fname = QFileDialog::getSaveFileName(this,tr("Export Identity"), "",tr("Retroshare Identity files (*.asc)")) ; + + if(fname.isNull()) + return ; + + QVariant data = ui.genPGPuser->itemData(ui.genPGPuser->currentIndex()); + std::string gpg_id = data.toString().toStdString() ; + + if(RsInit::exportIdentity(fname.toStdString(),gpg_id)) + QMessageBox::information(this,tr("Identity saved"),tr("Your identity was successfully saved\nIt is encrypted\n\nYou can now copy it to another computer\nand use the import button to load it")) ; + else + QMessageBox::information(this,tr("Identity not saved"),tr("Your identity was not saved. An error occured.")) ; +} +void GenCertDialog::importIdentity() +{ + QString fname = QFileDialog::getOpenFileName(this,tr("Export Identity"), "",tr("Retroshare Identity files (*.asc)")) ; + + if(fname.isNull()) + return ; + + std::string gpg_id ; + std::string err_string ; + + if(!RsInit::importIdentity(fname.toStdString(),gpg_id,err_string)) + { + QMessageBox::information(this,tr("Identity not loaded"),tr("Your identity was not loaded properly:")+" \n "+QString::fromStdString(err_string)) ; + return ; + } + + std::string name,email ; + + RsInit::GetPGPLoginDetails(gpg_id, name, email); + std::cerr << "Adding PGPUser: " << name << " id: " << gpg_id << std::endl; + QVariant userData(QString::fromStdString(gpg_id)); + QString gid = QString::fromStdString(gpg_id).right(8) ; + ui.genPGPuser->addItem(QString::fromUtf8(name.c_str()) + " <" + QString::fromUtf8(email.c_str()) + "> (" + gid + ")", userData); + +} + void GenCertDialog::genPerson() { /* Check the data from the GUI. */ diff --git a/retroshare-gui/src/gui/GenCertDialog.h b/retroshare-gui/src/gui/GenCertDialog.h index 59108ceb8..d8b3915dd 100644 --- a/retroshare-gui/src/gui/GenCertDialog.h +++ b/retroshare-gui/src/gui/GenCertDialog.h @@ -42,6 +42,8 @@ private slots: void genPerson(); //void loadPerson(); void selectFriend(); + void importIdentity(); + void exportIdentity(); void checkChanged(int i); void infodlg(); void newGPGKeyGenUiSetup(); diff --git a/retroshare-gui/src/gui/GenCertDialog.ui b/retroshare-gui/src/gui/GenCertDialog.ui index 946cda23b..ff20e21e5 100644 --- a/retroshare-gui/src/gui/GenCertDialog.ui +++ b/retroshare-gui/src/gui/GenCertDialog.ui @@ -6,8 +6,8 @@ 0 0 - 600 - 480 + 684 + 517 @@ -593,8 +593,8 @@ border: 1px solid #CCCCCC;} - - + + It looks like you don't own any Profile (GPG keys). Please fill in the form below to generate one, or use your favorite gnupg key manager. @@ -604,36 +604,67 @@ border: 1px solid #CCCCCC;} - - - - - 16777215 - 32 - + + + + 0 - - Your profile is associated to a GPG key + + -1 - - Generate a new Profile - - - - :/images/contact_new128.png:/images/contact_new128.png - - - - 32 - 32 - - - + false - + + + + + + + 16777215 + 32 + + + + Your profile is associated to a GPG key + + + Generate a new identity + + + + :/images/contact_new128.png:/images/contact_new128.png + + + + 32 + 32 + + + + false + + + + + + + Import new identity + + + + + + + Export selected identity + + + + + + @@ -646,19 +677,19 @@ border: 1px solid #CCCCCC;} - + - Use Profile + Use identity - Your profile is associated to a GPG key + Your profile is associated to a GPG key. RetroShare currently ignores DSA keys. @@ -737,7 +768,7 @@ anonymous, you can use a fake email. 1 - Put a meaningfull location. ex : home, laptop, etc. This field will be used to differentiate different installations with the same profile (gpg key). + Put a meaningfull location. ex : home, laptop, etc. This field will be used to differentiate different installations with the same identity (gpg key). false @@ -749,32 +780,6 @@ anonymous, you can use a fake email. - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 0 - - - -1 - - - false - - - @@ -789,7 +794,7 @@ anonymous, you can use a fake email. - Generate New Profile + Generate New Identity @@ -850,8 +855,8 @@ anonymous, you can use a fake email. QFrame::Plain - - + + @@ -870,30 +875,37 @@ anonymous, you can use a fake email. - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:16pt; color:#ffffff;">Create a new Profile</span></p></body></html> - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:16pt; color:#ffffff;">Create a new Identity</span></p></body></html> + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">RetroShare uses gpg keys for identity management. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Arial'; font-size:8pt; font-weight:600;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">You can use an existing profile (gpg key), or create a new one with this form.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">You can install retroshare on different locations using the same profile (gpg key).</span></p></body></html> - - +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">You can use an existing identity (i.e. a gpg key pair), from the list below, or create a new </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">one with this form.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;">You can install retroshare on different locations using the same identity. For this, just export the</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Arial'; font-size:8pt; font-weight:600;"> selected identity, and import it on the new computer, then create a new location with it.</span></p></body></html> + + + + diff --git a/retroshare-gui/src/gui/NetworkDialog.cpp b/retroshare-gui/src/gui/NetworkDialog.cpp index 68396e39b..7b4b8e541 100644 --- a/retroshare-gui/src/gui/NetworkDialog.cpp +++ b/retroshare-gui/src/gui/NetworkDialog.cpp @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. ****************************************************************/ -#include #include #include @@ -308,50 +307,6 @@ void NetworkDialog::copyLink() RSLinkClipboard::copyLinks(urls); } -/** Open a QFileDialog to browse for a pem/pqi file. */ -//void NetworkDialog::loadcert() -//{ -// use misc::getOpenFileName -// /* Create a new input dialog, which allows users to create files, too */ -// QFileDialog dialog (this, tr("Select a pem/pqi File")); -// //dialog.setDirectory(QFileInfo(ui.lineTorConfig->text()).absoluteDir()); -// //dialog.selectFile(QFileInfo(ui.lineTorConfig->text()).fileName()); -// dialog.setFileMode(QFileDialog::AnyFile); -// dialog.setReadOnly(false); -// -// /* Prompt the user to select a file or create a new one */ -// if (!dialog.exec() || dialog.selectedFiles().isEmpty()) { -// return; -// } -// QString filename = QDir::convertSeparators(dialog.selectedFiles().at(0)); -// -// /* Check if the file exists */ -// QFile torrcFile(filename); -// if (!QFileInfo(filename).exists()) { -// /* The given file does not exist. Should we create it? */ -// int response = VMessageBox::question(this, -// tr("File Not Found"), -// tr("%1 does not exist. Would you like to create it?") -// .arg(filename), -// VMessageBox::Yes, VMessageBox::No); -// -// if (response == VMessageBox::No) { -// /* Don't create it. Just bail. */ -// return; -// } -// /* Attempt to create the specified file */ -// if (!torrcFile.open(QIODevice::WriteOnly)) { -// VMessageBox::warning(this, -// tr("Failed to Create File"), -// tr("Unable to create %1 [%2]").arg(filename) -// .arg(torrcFile.errorString()), -// VMessageBox::Ok); -// return; -// } -// } -// //ui.lineTorConfig->setText(filename); -//} - void NetworkDialog::updateDisplay() { insertConnect() ; @@ -390,7 +345,7 @@ void NetworkDialog::insertConnect() while (index < connectWidget->topLevelItemCount()) { std::string gpg_widget_id = (connectWidget->topLevelItem(index))->text(COLUMN_PEERID).toStdString(); RsPeerDetails detail; - if (!rsPeers->getGPGDetails(gpg_widget_id, detail) || (detail.validLvl < GPGME_VALIDITY_MARGINAL && !detail.accept_connection)) { + if (!rsPeers->getGPGDetails(gpg_widget_id, detail) || (detail.validLvl < RS_TRUST_LVL_MARGINAL && !detail.accept_connection)) { delete (connectWidget->takeTopLevelItem(index)); } else { index++; @@ -400,7 +355,7 @@ void NetworkDialog::insertConnect() while (index < ui.unvalidGPGkeyWidget->topLevelItemCount()) { std::string gpg_widget_id = (ui.unvalidGPGkeyWidget->topLevelItem(index))->text(COLUMN_PEERID).toStdString(); RsPeerDetails detail; - if (!rsPeers->getGPGDetails(gpg_widget_id, detail) || detail.validLvl >= GPGME_VALIDITY_MARGINAL || detail.accept_connection) { + if (!rsPeers->getGPGDetails(gpg_widget_id, detail) || detail.validLvl >= RS_TRUST_LVL_MARGINAL || detail.accept_connection) { delete (ui.unvalidGPGkeyWidget->takeTopLevelItem(index)); } else { index++; @@ -461,12 +416,12 @@ void NetworkDialog::insertConnect() else switch(detail.trustLvl) { - case GPGME_VALIDITY_MARGINAL: item->setText(2,tr("Marginally trusted peer")) ; break; - case GPGME_VALIDITY_FULL: - case GPGME_VALIDITY_ULTIMATE: item->setText(2,tr("Fully trusted peer")) ; break ; - case GPGME_VALIDITY_UNKNOWN: - case GPGME_VALIDITY_UNDEFINED: - case GPGME_VALIDITY_NEVER: + case RS_TRUST_LVL_MARGINAL: item->setText(2,tr("Marginally trusted peer")) ; break; + case RS_TRUST_LVL_FULL: + case RS_TRUST_LVL_ULTIMATE: item->setText(2,tr("Fully trusted peer")) ; break ; + case RS_TRUST_LVL_UNKNOWN: + case RS_TRUST_LVL_UNDEFINED: + case RS_TRUST_LVL_NEVER: default: item->setText(2,tr("Untrusted peer")) ; break ; } @@ -521,7 +476,7 @@ void NetworkDialog::insertConnect() item -> setBackground(i,QBrush(backgrndcolor)); /* add to the list */ - if (detail.accept_connection || detail.validLvl >= GPGME_VALIDITY_MARGINAL) + if (detail.accept_connection || detail.validLvl >= RS_TRUST_LVL_MARGINAL) { /* add gpg item to the list. If item is already in the list, it won't be duplicated thanks to Qt */ connectWidget->addTopLevelItem(item); diff --git a/retroshare-gui/src/gui/NetworkView.cpp b/retroshare-gui/src/gui/NetworkView.cpp index e4c29c9bb..bffc68758 100644 --- a/retroshare-gui/src/gui/NetworkView.cpp +++ b/retroshare-gui/src/gui/NetworkView.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include #include #include @@ -160,12 +158,12 @@ void NetworkView::updateDisplay() switch(detail.trustLvl) { - case GPGME_VALIDITY_MARGINAL: auth = GraphWidget::ELASTIC_NODE_AUTH_MARGINAL ; break; - case GPGME_VALIDITY_FULL: - case GPGME_VALIDITY_ULTIMATE: auth = GraphWidget::ELASTIC_NODE_AUTH_FULL ; break; - case GPGME_VALIDITY_UNKNOWN: - case GPGME_VALIDITY_UNDEFINED: - case GPGME_VALIDITY_NEVER: + case RS_TRUST_LVL_MARGINAL: auth = GraphWidget::ELASTIC_NODE_AUTH_MARGINAL ; break; + case RS_TRUST_LVL_FULL: + case RS_TRUST_LVL_ULTIMATE: auth = GraphWidget::ELASTIC_NODE_AUTH_FULL ; break; + case RS_TRUST_LVL_UNKNOWN: + case RS_TRUST_LVL_UNDEFINED: + case RS_TRUST_LVL_NEVER: default: auth = GraphWidget::ELASTIC_NODE_AUTH_UNKNOWN ; break ; } diff --git a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp index 0042d5bc2..94aecdaeb 100644 --- a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. ****************************************************************/ -#include #include "ConfCertDialog.h" @@ -135,6 +134,17 @@ void ConfCertDialog::load() return; } + if(detail.isOnlyGPGdetail && !rsPeers->isKeySupported(mId)) + { + ui.make_friend_button->setEnabled(false) ; + ui.make_friend_button->setToolTip(tr("The supplied key algorithm is not supported by RetroShare\n(Only RSA keys are supported at the moment)")) ; + } + else + { + ui.make_friend_button->setEnabled(true) ; + ui.make_friend_button->setToolTip("") ; + } + ui.name->setText(QString::fromUtf8(detail.name.c_str())); ui.peerid->setText(QString::fromStdString(detail.id)); @@ -252,7 +262,7 @@ void ConfCertDialog::load() } //web of trust - if (detail.trustLvl == GPGME_VALIDITY_ULTIMATE) { + if (detail.trustLvl == RS_TRUST_LVL_ULTIMATE) { //trust is ultimate, it means it's one of our own keys ui.web_of_trust_label->setText(tr("Your trust in this peer is ultimate, it's probably a key you own.")); ui.radioButton_trust_fully->hide(); @@ -262,19 +272,19 @@ void ConfCertDialog::load() ui.radioButton_trust_fully->show(); ui.radioButton_trust_marginnaly->show(); ui.radioButton_trust_never->show(); - if (detail.trustLvl == GPGME_VALIDITY_FULL) { + if (detail.trustLvl == RS_TRUST_LVL_FULL) { ui.web_of_trust_label->setText(tr("Your trust in this peer is full.")); ui.radioButton_trust_fully->setChecked(true); ui.radioButton_trust_fully->setIcon(QIcon(":/images/security-high-48.png")); ui.radioButton_trust_marginnaly->setIcon(QIcon(":/images/security-medium-off-48.png")); ui.radioButton_trust_never->setIcon(QIcon(":/images/security-low-off-48.png")); - } else if (detail.trustLvl == GPGME_VALIDITY_MARGINAL) { + } else if (detail.trustLvl == RS_TRUST_LVL_MARGINAL) { ui.web_of_trust_label->setText(tr("Your trust in this peer is marginal.")); ui.radioButton_trust_marginnaly->setChecked(true); ui.radioButton_trust_marginnaly->setIcon(QIcon(":/images/security-medium-48.png")); ui.radioButton_trust_never->setIcon(QIcon(":/images/security-low-off-48.png")); ui.radioButton_trust_fully->setIcon(QIcon(":/images/security-high-off-48.png")); - } else if (detail.trustLvl == GPGME_VALIDITY_NEVER) { + } else if (detail.trustLvl == RS_TRUST_LVL_NEVER) { ui.web_of_trust_label->setText(tr("Your trust in this peer is none.")); ui.radioButton_trust_never->setChecked(true); ui.radioButton_trust_never->setIcon(QIcon(":/images/security-low-48.png")); @@ -363,13 +373,13 @@ void ConfCertDialog::applyDialog() } //check the GPG trustlvl - if (ui.radioButton_trust_fully->isChecked() && detail.trustLvl != GPGME_VALIDITY_FULL) { + if (ui.radioButton_trust_fully->isChecked() && detail.trustLvl != RS_TRUST_LVL_FULL) { //trust has changed to fully - rsPeers->trustGPGCertificate(detail.id, GPGME_VALIDITY_FULL); - } else if (ui.radioButton_trust_marginnaly->isChecked() && detail.trustLvl != GPGME_VALIDITY_MARGINAL) { - rsPeers->trustGPGCertificate(detail.id, GPGME_VALIDITY_MARGINAL); - } else if (ui.radioButton_trust_never->isChecked() && detail.trustLvl != GPGME_VALIDITY_NEVER) { - rsPeers->trustGPGCertificate(detail.id, GPGME_VALIDITY_NEVER); + rsPeers->trustGPGCertificate(detail.id, RS_TRUST_LVL_FULL); + } else if (ui.radioButton_trust_marginnaly->isChecked() && detail.trustLvl != RS_TRUST_LVL_MARGINAL) { + rsPeers->trustGPGCertificate(detail.id, RS_TRUST_LVL_MARGINAL); + } else if (ui.radioButton_trust_never->isChecked() && detail.trustLvl != RS_TRUST_LVL_NEVER) { + rsPeers->trustGPGCertificate(detail.id, RS_TRUST_LVL_NEVER); } if (!detail.isOnlyGPGdetail) { diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp index f2d3fcbae..8cc410d2a 100755 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp @@ -222,7 +222,7 @@ void ConnectFriendWizard::initializePage(int id) case RS_TRUST_LVL_MARGINAL: trustString = tr("Marginal"); break; - case RS_TRUST_LVL_NONE: + case RS_TRUST_LVL_NEVER: trustString = tr("None"); break; default: @@ -595,10 +595,6 @@ void ConnectFriendWizard::updatePeersList(int index) _id_boxes.clear(); -#ifdef FRIEND_WIZARD_DEBUG - std::cerr << "FofPage::updatePeersList() updating peers list with index=" << index << std::endl; -#endif - // We have to use this trick because signers are given by their names instead of their ids. That's a cause // for some confusion when two peers have the same name. // diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index 40b7d32ad..954de1467 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -79,22 +79,49 @@ int main(int argc, char *argv[]) RsInit::InitRsConfig(); int initResult = RsInit::InitRetroShare(argc, argv); + if(initResult == RS_INIT_NO_KEYRING) // happens when we already have accounts, but no pgp key. This is when switching to the openpgp-sdk version. + { + QApplication dummyApp (argc, argv); // needed for QMessageBox + + QMessageBox msgBox; + msgBox.setText(QObject::tr("This version of RetroShare is using OpenPGP-SDK. As a side effect, it's not using the system shared PGP keyring, but has it's own keyring shared by all RetroShare instances.

You do not appear to have such a keyring, although GPG keys are mentionned by existing RetroShare accounts, probably because you just changed to this new version of the software.")); + msgBox.setInformativeText(QObject::tr("Choose between:
  • Ok to copy the existing keyring from gnupg (safest bet), or
  • Close without saving to start fresh with an empty keyring (you will be asked to create a new PGP key to work with RetroShare, or import a previously saved pgp keypair).
  • Cancel to quit and forge a keyring by yourself (needs some PGP skills)
")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setWindowIcon(QIcon(":/images/rstray3.png")); + + int ret = msgBox.exec(); + + if(ret == QMessageBox::Cancel) + return 0 ; + if(ret == QMessageBox::Ok) + { + if(!RsInit::copyGnuPGKeyrings()) + return 0 ; + + initResult = RsInit::InitRetroShare(argc, argv); + } + else + initResult = RS_INIT_OK ; + } + if (initResult < 0) { /* Error occured */ QApplication dummyApp (argc, argv); // needed for QMessageBox QMessageBox mb(QMessageBox::Critical, QObject::tr("RetroShare"), "", QMessageBox::Ok); mb.setWindowIcon(QIcon(":/images/rstray3.png")); - switch (initResult) { - case RS_INIT_AUTH_FAILED: - std::cerr << "RsInit::InitRetroShare AuthGPG::InitAuth failed" << std::endl; - mb.setText(QObject::tr("Inititialize failed. Wrong or missing installation of gpg.")); - break; - default: - /* Unexpected return code */ - std::cerr << "RsInit::InitRetroShare unexpected return code " << initResult << std::endl; - mb.setText(QObject::tr("An unexpected error occured. Please report 'RsInit::InitRetroShare unexpected return code %1'.").arg(initResult)); - break; + switch (initResult) + { + case RS_INIT_AUTH_FAILED: + std::cerr << "RsInit::InitRetroShare AuthGPG::InitAuth failed" << std::endl; + mb.setText(QObject::tr("Inititialize failed. Wrong or missing installation of gpg.")); + break; + default: + /* Unexpected return code */ + std::cerr << "RsInit::InitRetroShare unexpected return code " << initResult << std::endl; + mb.setText(QObject::tr("An unexpected error occured. Please report 'RsInit::InitRetroShare unexpected return code %1'.").arg(initResult)); + break; } mb.exec(); return 1; diff --git a/retroshare-nogui/src/retroshare-nogui.pro b/retroshare-nogui/src/retroshare-nogui.pro index 364f22003..aafdd22ab 100644 --- a/retroshare-nogui/src/retroshare-nogui.pro +++ b/retroshare-nogui/src/retroshare-nogui.pro @@ -15,6 +15,7 @@ linux-* { } LIBS += ../../libretroshare/src/lib/libretroshare.a + LIBS += ../../openpgpsdk/src/lib/libops.a -lbz2 LIBS += -lssl -lgpgme -lupnp -lixml -lgnome-keyring } @@ -55,6 +56,7 @@ win32 { MOC_DIR = temp/moc LIBS += ../../libretroshare/src/lib/libretroshare.a + LIBS += ../../openpgpsdk/src/lib/libops.a -lbz2 LIBS += -L"../../../lib" -lssl -lcrypto -lpthreadGC2d -lminiupnpc -lz LIBS += -lssl -lcrypto -lgpgme -lpthreadGC2d -lminiupnpc -lz # added after bitdht