2012-03-24 15:58:18 +00:00
|
|
|
/*
|
|
|
|
* 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 <openpgpsdk/packet-parse.h>
|
|
|
|
#include <openpgpsdk/packet-show.h>
|
|
|
|
#include <openpgpsdk/keyring.h>
|
|
|
|
#include "keyring_local.h"
|
|
|
|
#include "parse_local.h"
|
|
|
|
#include <openpgpsdk/util.h>
|
|
|
|
#include <openpgpsdk/armour.h>
|
|
|
|
#include <openpgpsdk/signature.h>
|
|
|
|
#include <openpgpsdk/memory.h>
|
|
|
|
#include <openpgpsdk/validate.h>
|
|
|
|
#include <openpgpsdk/readerwriter.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <openpgpsdk/final.h>
|
|
|
|
|
|
|
|
static int debug=0;
|
|
|
|
|
|
|
|
static ops_boolean_t check_binary_signature(const unsigned len,
|
2012-04-26 19:37:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static int keydata_reader(void *dest,size_t length,ops_error_t **errors,
|
2012-04-26 19:37:15 +00:00
|
|
|
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)
|
2012-03-24 15:58:18 +00:00
|
|
|
{
|
2012-04-26 19:37:15 +00:00
|
|
|
++arg->packet;
|
|
|
|
arg->offset=0;
|
2012-03-24 15:58:18 +00:00
|
|
|
}
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
if(arg->packet == arg->key->npackets)
|
|
|
|
return 0;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// we should never be asked to cross a packet boundary in a single read
|
|
|
|
assert(arg->key->packets[arg->packet].length >= arg->offset+length);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
memcpy(dest,&arg->key->packets[arg->packet].raw[arg->offset],length);
|
|
|
|
arg->offset+=length;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
return length;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-05-14 20:01:00 +00:00
|
|
|
static void free_signature_info(ops_signature_info_t *sig,int n)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
2012-05-14 20:01:00 +00:00
|
|
|
int i ;
|
|
|
|
for(i=0;i<n;++i)
|
|
|
|
free (sig[i].v4_hashed_data);
|
2012-04-26 19:37:15 +00:00
|
|
|
free (sig);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static void copy_signature_info(ops_signature_info_t* dst, const ops_signature_info_t* src)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
memcpy(dst,src,sizeof *src);
|
|
|
|
dst->v4_hashed_data=ops_mallocz(src->v4_hashed_data_length);
|
|
|
|
memcpy(dst->v4_hashed_data,src->v4_hashed_data,src->v4_hashed_data_length);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static void add_sig_to_valid_list(ops_validate_result_t * result, const ops_signature_info_t* sig)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
size_t newsize;
|
|
|
|
size_t start;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// increment count
|
|
|
|
++result->valid_count;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// 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);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// copy key ptr to array
|
2012-05-13 19:04:13 +00:00
|
|
|
copy_signature_info(&result->valid_sigs[result->valid_count-1],sig);
|
2012-04-26 19:37:15 +00:00
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static void add_sig_to_invalid_list(ops_validate_result_t * result, const ops_signature_info_t *sig)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
size_t newsize;
|
|
|
|
size_t start;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// increment count
|
|
|
|
++result->invalid_count;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// 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);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// copy key ptr to array
|
2012-05-13 19:04:13 +00:00
|
|
|
copy_signature_info(&result->invalid_sigs[result->invalid_count-1],sig);
|
2012-04-26 19:37:15 +00:00
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static void add_sig_to_unknown_list(ops_validate_result_t * result, const ops_signature_info_t *sig)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
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
|
2012-05-13 19:04:13 +00:00
|
|
|
copy_signature_info(&result->unknown_sigs[result->unknown_signer_count-1],sig);
|
2012-04-26 19:37:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ops_parse_cb_return_t
|
2012-03-24 15:58:18 +00:00
|
|
|
ops_validate_key_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
|
/*
|
|
|
|
printf(" type=%02x signer_id=",content->signature.type);
|
|
|
|
hexdump(content->signature.signer_id,
|
|
|
|
sizeof content->signature.signer_id);
|
|
|
|
*/
|
|
|
|
|
|
|
|
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,
|
2012-03-24 15:58:18 +00:00
|
|
|
&arg->user_id,
|
|
|
|
&content->signature,
|
|
|
|
ops_get_public_key_from_data(signer),
|
|
|
|
arg->rarg->key->packets[arg->rarg->packet].raw);
|
2012-04-26 19:37:15 +00:00
|
|
|
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;
|
2012-03-24 15:58:18 +00:00
|
|
|
}
|
2012-04-26 19:37:15 +00:00
|
|
|
return OPS_RELEASE_MEMORY;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_parse_cb_return_t
|
2012-03-24 15:58:18 +00:00
|
|
|
validate_data_cb(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
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)
|
2012-03-24 15:58:18 +00:00
|
|
|
{
|
2012-04-26 19:37:15 +00:00
|
|
|
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:
|
2012-04-27 12:07:29 +00:00
|
|
|
arg->literal_data_body=content->literal_data_body;
|
2012-04-26 19:37:15 +00:00
|
|
|
arg->use=LITERAL_DATA;
|
|
|
|
return OPS_KEEP_MEMORY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
|
2012-04-27 12:07:29 +00:00
|
|
|
arg->signed_cleartext_body=content->signed_cleartext_body;
|
2012-04-26 19:37:15 +00:00
|
|
|
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; zzz<content->signature.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,
|
2012-04-27 12:07:29 +00:00
|
|
|
arg->literal_data_body.data,
|
|
|
|
arg->literal_data_body.length);
|
2012-04-26 19:37:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SIGNED_CLEARTEXT:
|
|
|
|
ops_memory_add(mem,
|
2012-04-27 12:07:29 +00:00
|
|
|
arg->signed_cleartext_body.data,
|
|
|
|
arg->signed_cleartext_body.length);
|
2012-04-26 19:37:15 +00:00
|
|
|
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;
|
2012-03-24 15:58:18 +00:00
|
|
|
}
|
2012-04-26 19:37:15 +00:00
|
|
|
return OPS_RELEASE_MEMORY;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
static void keydata_destroyer(ops_reader_info_t *rinfo)
|
2012-04-26 19:37:15 +00:00
|
|
|
{ free(ops_reader_get_arg(rinfo)); }
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
void ops_keydata_reader_set(ops_parse_info_t *pinfo,const ops_keydata_t *key)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
validate_reader_arg_t *arg=malloc(sizeof *arg);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
memset(arg,'\0',sizeof *arg);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
arg->key=key;
|
|
|
|
arg->packet=0;
|
|
|
|
arg->offset=0;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_reader_set(pinfo,keydata_reader,keydata_destroyer,arg);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \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)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
if (result->invalid_count || result->unknown_signer_count || !result->valid_count)
|
|
|
|
return ops_false;
|
|
|
|
else
|
|
|
|
return ops_true;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* \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()
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
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
|
2012-03-24 15:58:18 +00:00
|
|
|
*/
|
|
|
|
ops_boolean_t ops_validate_key_signatures(ops_validate_result_t *result,const ops_keydata_t *key,
|
2012-04-26 19:37:15 +00:00
|
|
|
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;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
memset(&carg,'\0',sizeof carg);
|
|
|
|
carg.result=result;
|
|
|
|
carg.cb_get_passphrase=cb_get_passphrase;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
pinfo=ops_parse_info_new();
|
|
|
|
// ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
carg.keyring=keyring;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_parse_cb_set(pinfo,ops_validate_key_cb,&carg);
|
|
|
|
pinfo->rinfo.accumulate=ops_true;
|
|
|
|
ops_keydata_reader_set(pinfo,key);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// Note: Coverity incorrectly reports an error that carg.rarg
|
|
|
|
// is never used.
|
|
|
|
carg.rarg=ops_reader_get_arg_from_pinfo(pinfo);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_parse(pinfo);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
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);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_parse_info_delete(pinfo);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
/* if(carg.literal_data_body.data != NULL)
|
|
|
|
free(carg.literal_data_body.data) ; */
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
if (result->invalid_count || result->unknown_signer_count || !result->valid_count)
|
|
|
|
return ops_false;
|
|
|
|
else
|
|
|
|
return ops_true;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
2012-04-26 19:37:15 +00:00
|
|
|
\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()
|
|
|
|
*/
|
2012-03-24 15:58:18 +00:00
|
|
|
ops_boolean_t ops_validate_all_signatures(ops_validate_result_t *result,
|
2012-04-26 19:37:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
2012-04-26 19:37:15 +00:00
|
|
|
\ingroup HighLevel_Verify
|
|
|
|
\brief Frees validation result and associated memory
|
|
|
|
\param result Struct to be freed
|
|
|
|
\note Must be called after validation functions
|
|
|
|
*/
|
2012-03-24 15:58:18 +00:00
|
|
|
void ops_validate_result_free(ops_validate_result_t *result)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
if (!result)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (result->valid_sigs)
|
2012-05-14 20:01:00 +00:00
|
|
|
free_signature_info(result->valid_sigs,result->valid_count);
|
2012-04-26 19:37:15 +00:00
|
|
|
if (result->invalid_sigs)
|
2012-05-14 20:01:00 +00:00
|
|
|
free_signature_info(result->invalid_sigs,result->invalid_count);
|
2012-04-26 19:37:15 +00:00
|
|
|
if (result->unknown_sigs)
|
2012-05-14 20:01:00 +00:00
|
|
|
free_signature_info(result->unknown_sigs,result->unknown_signer_count);
|
2012-04-26 19:37:15 +00:00
|
|
|
|
|
|
|
free(result);
|
|
|
|
result=NULL;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
2012-04-26 19:37:15 +00:00
|
|
|
\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)
|
|
|
|
{
|
2012-03-24 15:58:18 +00:00
|
|
|
ops_validate_result_t* result=ops_mallocz(sizeof *result);
|
2012-04-26 19:37:15 +00:00
|
|
|
|
2012-03-24 15:58:18 +00:00
|
|
|
if (ops_validate_file(result, filename, armoured, keyring)==ops_true)
|
|
|
|
{
|
2012-04-26 19:37:15 +00:00
|
|
|
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
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_validate_result_free(result);
|
2012-03-24 15:58:18 +00:00
|
|
|
}
|
|
|
|
\endcode
|
2012-04-26 19:37:15 +00:00
|
|
|
*/
|
2012-03-24 15:58:18 +00:00
|
|
|
ops_boolean_t ops_validate_file(ops_validate_result_t *result, const char* filename, const int armoured, const ops_keyring_t* keyring)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
ops_parse_info_t *pinfo=NULL;
|
|
|
|
validate_data_cb_arg_t validate_arg;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
int fd=0;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
//
|
|
|
|
fd=ops_setup_file_read(&pinfo, filename, &validate_arg, validate_data_cb, ops_true);
|
|
|
|
if (fd < 0)
|
|
|
|
return ops_false;
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// Set verification reader and handling options
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
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);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
if (armoured)
|
|
|
|
ops_reader_push_dearmour(pinfo);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
// Do the verification
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
ops_parse(pinfo);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
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);
|
2012-03-24 15:58:18 +00:00
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
if(validate_arg.literal_data_body.data != NULL) free(validate_arg.literal_data_body.data) ;
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
return validate_result_status(result);
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
/**
|
2012-04-26 19:37:15 +00:00
|
|
|
\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.
|
|
|
|
*/
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
ops_boolean_t ops_validate_mem(ops_validate_result_t *result, ops_memory_t* mem, const int armoured, const ops_keyring_t* keyring)
|
2012-04-26 19:37:15 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
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) ;
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
return validate_result_status(result);
|
|
|
|
}
|
|
|
|
|
2012-05-13 19:04:13 +00:00
|
|
|
/**
|
|
|
|
\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
|
|
|
|
*/
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
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 ;
|
2012-05-13 19:04:13 +00:00
|
|
|
tmp_keyring.keys = (ops_keydata_t *)signers_key ; // this is a const_cast, somehow
|
2012-04-26 19:37:15 +00:00
|
|
|
|
|
|
|
memset(&validate_arg,'\0',sizeof validate_arg);
|
|
|
|
|
|
|
|
validate_arg.result=result;
|
|
|
|
validate_arg.keyring=&tmp_keyring;
|
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
int length = literal_data_length ;
|
2012-04-26 19:37:15 +00:00
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
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 ;
|
2012-04-26 19:37:15 +00:00
|
|
|
|
|
|
|
// 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) ;
|
|
|
|
|
2012-04-27 12:07:29 +00:00
|
|
|
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) ;
|
|
|
|
|
2012-04-26 19:37:15 +00:00
|
|
|
return res ;
|
|
|
|
}
|
2012-03-24 15:58:18 +00:00
|
|
|
|
|
|
|
// eof
|