mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-31 03:45:10 -04:00

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@7014 b45a01b8-16f6-495d-af2f-9b41ad6348cc
1087 lines
26 KiB
C
1087 lines
26 KiB
C
/*
|
|
* 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 <openpgpsdk/armour.h>
|
|
#include <openpgpsdk/std_print.h>
|
|
#include <openpgpsdk/keyring.h>
|
|
#include <openpgpsdk/packet-parse.h>
|
|
#include <openpgpsdk/util.h>
|
|
#include <openpgpsdk/accumulate.h>
|
|
#include <openpgpsdk/validate.h>
|
|
#include <openpgpsdk/signature.h>
|
|
#include <openpgpsdk/readerwriter.h>
|
|
#include <openpgpsdk/defs.h>
|
|
|
|
#include "keyring_local.h"
|
|
#include "parse_local.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
|
|
#include <openpgpsdk/final.h>
|
|
#include <openpgpsdk/opsdir.h>
|
|
|
|
/**
|
|
\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;n<keydata->nsigs;++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;
|
|
|
|
keydata_internal_free(dst) ;
|
|
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_false);
|
|
|
|
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=ops_open(filename,O_RDONLY | O_BINARY, 0);
|
|
|
|
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;
|
|
}
|
|
|
|
void ops_keyring_remove_key(ops_keyring_t *keyring,int index)
|
|
{
|
|
if(index > keyring->nkeys-1)
|
|
{
|
|
fprintf(stderr,"ops_keyring_remove_key: ERROR: cannot remove key with index %d > %d.",index,keyring->nkeys-1) ;
|
|
return ;
|
|
}
|
|
|
|
if(index < keyring->nkeys-1)
|
|
ops_keydata_copy(&keyring->keys[index],&keyring->keys[keyring->nkeys-1]) ;
|
|
|
|
keydata_internal_free(&keyring->keys[keyring->nkeys-1]) ;
|
|
keyring->nkeys-- ;
|
|
|
|
// keyring->nkeys_allocated is left untouched intentionnaly.
|
|
}
|
|
|
|
/**
|
|
\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; i<keyring->keys[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; i<key->nuids; 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;i<keyring->nkeys;++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_teardown_file_write(info, fd);
|
|
|
|
return ops_true ;
|
|
}
|
|
|
|
/*\@}*/
|
|
|
|
// eof
|