Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F7712838
affverify.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
14 KB
Referenced Files
None
Subscribers
None
affverify.cpp
View Options
/*
* afverify.cpp:
*
* Verify the digital signature on a signed file
*/
#include "affconfig.h"
#include "afflib.h"
#include "afflib_i.h"
#include "utils.h"
#include "base64.h"
#include "aff_bom.h"
#include "aftimer.h"
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <openssl/pem.h>
#include <openssl/x509.h>
using namespace std;
using namespace aff;
const char *progname = "afcrypto";
int opt_change = 0;
int opt_verbose = 0;
int opt_all = 0;
void usage()
{
printf("afverify version %s\n",PACKAGE_VERSION);
printf("usage: afverify [options] filename.aff\n");
printf("Verifies the digital signatures on a file\n");
printf("options:\n");
printf(" -a --- print all segments\n");
printf(" -V --- Just print the version number and exit.\n");
printf(" -v --- verbose\n");
OpenSSL_add_all_digests();
const EVP_MD *sha256 = EVP_get_digestbyname("sha256");
if(sha256){
printf(" SHA256 is operational\n");
} else {
printf("Warning: EVP_get_digestbyname(\"sha256\") fails\n");
}
exit(0);
}
void print_x509_info(X509 *cert)
{
printf("SIGNING CERTIFICATE :\n");
printf(" Subject: "); X509_NAME_print_ex_fp(stdout,X509_get_subject_name(cert),0,XN_FLAG_SEP_CPLUS_SPC);
printf("\n");
printf(" Issuer: "); X509_NAME_print_ex_fp(stdout,X509_get_issuer_name(cert),0,XN_FLAG_SEP_CPLUS_SPC);
printf("\n");
ASN1_INTEGER *sn = X509_get_serialNumber(cert);
if(sn){
long num = ASN1_INTEGER_get(sn);
if(num>0) printf(" Certificate serial number: %ld\n",num);
}
printf("\n");
}
#ifdef USE_AFFSIGS
#include "expat.h"
void startElement(void *userData, const char *name, const char **atts);
void endElement(void *userData, const char *name);
void cHandler(void *userData,const XML_Char *s,int len);
class segmenthash {
public:
segmenthash():total_validated(0),total_invalid(0),sigmode(0),in_cert(false),
in_seghash(false),get_cdata(false),arg(0),seglen(0),
get_cdata_segment(0),af(0),cert(0),pubkey(0) {
parser = XML_ParserCreate(NULL);
XML_SetUserData(parser, this);
XML_SetElementHandler(parser, ::startElement, ::endElement);
XML_SetCharacterDataHandler(parser,cHandler);
};
int parse(const char *buf,int len) { return XML_Parse(parser, buf, len, 1);}
XML_Parser parser;
int total_validated;
int total_invalid;
int sigmode;
bool in_cert;
bool in_seghash;
bool get_cdata;
string segname;
string alg;
string cdata;
int arg;
int seglen;
const char *get_cdata_segment; // just get this segment
AFFILE *af; // if set, we are parsing crypto
X509 *cert; // public key used to sign
EVP_PKEY *pubkey;
void clear(){
segname="";
cdata="";
sigmode=0;
alg="";
seglen=0;
}
~segmenthash(){
if(cert) X509_free(cert);
if(parser) XML_ParserFree(parser);
}
void startElement(const char *name,const char **atts);
void endElement(const char *name);
};
int count=0;
void startElement(void *userData, const char *name, const char **atts)
{
segmenthash *sh = (segmenthash *)userData;
sh->startElement(name,atts);
}
void segmenthash::startElement(const char *name,const char **atts)
{
clear();
if(strcmp(name,AF_XML_SEGMENT_HASH)==0){
for(int i=0;atts[i];i+=2){
const char *name = atts[i];
const char *value = atts[i+1];
if(!strcmp(name,"segname")) segname = value;
if(!strcmp(name,"sigmode")) sigmode = atoi(value);
if(!strcmp(name,"alg")) alg = value;
if(!strcmp(name,"seglen")) seglen = atoi(value);
}
in_seghash = true;
get_cdata = true;
return;
}
if(strcmp(name,"signingcertificate")==0){
in_cert = true;
get_cdata = true;
return;
}
if(get_cdata_segment && strcmp(name,get_cdata_segment)==0){
get_cdata = true;
return;
}
}
void cHandler(void *userData,const XML_Char *s,int len)
{
segmenthash *sh = (segmenthash *)userData;
if(sh->get_cdata==false) return; // don't want cdata
sh->cdata.append(s,len);
}
void endElement(void *userData, const char *name)
{
segmenthash *sh = (segmenthash *)userData;
sh->endElement(name);
}
void segmenthash::endElement(const char *name)
{
if(get_cdata_segment && strcmp(name,get_cdata_segment)==0){
get_cdata = false;
XML_StopParser(parser,0);
return;
}
if(in_seghash && af){
if(segname.size()==0) return; // don't have a segment name
/* Try to validate this one */
size_t hashbuf_len = cdata.size() + 2;
u_char *hashbuf = (u_char *)malloc(hashbuf_len);
hashbuf_len = b64_pton_slg((char *)cdata.c_str(),cdata.size(),hashbuf,hashbuf_len);
if(alg=="sha256"){
/* TODO: Don't re-validate something that's already validated */
int r = af_hash_verify_seg2(af,segname.c_str(),hashbuf,hashbuf_len,sigmode);
if(r==AF_HASH_VERIFIES){
total_validated++;
}
else total_invalid++;
}
free(hashbuf);
in_seghash = false;
}
if(in_cert && af){
BIO *cert_bio = BIO_new_mem_buf((char *)cdata.c_str(),cdata.size());
PEM_read_bio_X509(cert_bio,&cert,0,0);
BIO_free(cert_bio);
pubkey = X509_get_pubkey(cert);
in_cert = false;
}
cdata = ""; // erase it
}
string get_xml_field(const char *buf,const char *field)
{
segmenthash sh;
sh.get_cdata_segment = field;
sh.parse(buf,strlen(buf));
return sh.cdata;
}
/* verify the chain signature; return 0 if successful, -1 if failed.
* The signature is a block of XML with a base64 encoded at the end.
*/
int verify_bom_signature(AFFILE *af,const char *buf)
{
OpenSSL_add_all_digests();
const EVP_MD *sha256 = EVP_get_digestbyname("sha256");
if(!sha256){
fprintf(stderr,"OpenSSL does not have SHA256; signatures cannot be verified.\n");
return -1;
}
const char *cce = "</" AF_XML_AFFBOM ">\n";
const char *chain_end = strstr(buf,cce);
if(!chain_end){
warn("end of chain XML can't be found\n");
return -1; // can't find it
}
const char *sig_start = chain_end + strlen(cce);
BIO *seg = BIO_new_mem_buf((void *)buf,strlen(buf));
if(BIO_seek(seg,0)!=0){
printf("Cannot seek to beginning of BIO mem?");
return -1;
}
X509 *cert = 0;
PEM_read_bio_X509(seg,&cert,0,0); // get the contained x509 cert
BIO_free(seg);
/* Now get the binary signature */
u_char sigbuf[1024];
int sigbuf_len = b64_pton_slg(sig_start,strlen(sig_start),sigbuf,sizeof(sigbuf));
if(sigbuf_len<80){
warn("BOM is not signed");
return -1;
}
/* Try to verify it */
EVP_MD_CTX md;
EVP_VerifyInit(&md,sha256);
EVP_VerifyUpdate(&md,buf,sig_start-buf);
int r = EVP_VerifyFinal(&md,sigbuf,sigbuf_len,X509_get_pubkey(cert));
if(r!=1){
printf("BAD SIGNATURE ON BOM\n");
return -1;
}
print_x509_info(cert);
printf("Date: %s\n",get_xml_field(buf,"date").c_str());
printf("Notes: \n%s\n",get_xml_field(buf,"notes").c_str());
/* Now extract the XML block, terminating at the beginning of the XML signature */
char *buffer_without_signature = strdup(buf);
char *sigend = strstr(buffer_without_signature,cce);
if(sigend){
sigend[strlen(cce)] = 0;/* terminate the XML to remove the signature */
}
segmenthash sh;
sh.af = af;
if (!sh.parse(buffer_without_signature, strlen(buffer_without_signature))){
fprintf(stderr, "expat error: %s at line %d\n",
XML_ErrorString(XML_GetErrorCode(sh.parser)),
(int)XML_GetCurrentLineNumber(sh.parser));
fprintf(stderr,"buffer without signature:\n%s\n",buffer_without_signature);
return 1;
}
free(buffer_without_signature);
return 0;
}
#endif
int crypto_verify(AFFILE *af,u_char *certbuf,size_t certbuf_len)
{
seglist segments(af);
seglist no_sigs;
seglist bad_sigs;
seglist good_sigs;
seglist unknown_errors;
for(seglist::const_iterator seg = segments.begin();
seg != segments.end();
seg++){
if(parse_chain(seg->name)>=0) continue; // chain of custody segments don't need signatures
const char *segname = seg->name.c_str();
int i =af_sig_verify_seg(af,segname);
if(opt_verbose){
printf("af_sig_verify_seg(af,%s)=%d\n",segname,i);
}
switch(i){
case AF_ERROR_SIG_NO_CERT:
err(1,"%s: no public key in AFF file\n",af_filename(af));
case AF_ERROR_SIG_BAD:
bad_sigs.push_back(*seg);
break;
case AF_ERROR_SIG_READ_ERROR:
no_sigs.push_back(*seg);
break;
case AF_SIG_GOOD:
good_sigs.push_back(*seg);
break;
case AF_ERROR_SIG_SIG_SEG:
break; // can't verify the sig on a sig seg
case AF_ERROR_SIG_NOT_COMPILED:
errx(1,"AFFLIB was compiled without signature support. Cannot continue.\n");
default:
unknown_errors.push_back(*seg);
break;
}
}
const char *prn = "";
/* Tell us something about the certificate */
BIO *cert_bio = BIO_new_mem_buf(certbuf,certbuf_len);
X509 *cert = 0;
PEM_read_bio_X509(cert_bio,&cert,0,0);
if(!cert) errx(1,"Cannot decode certificate");
printf("\n");
printf("Filename: %s\n",af_filename(af));
printf("# Segments signed and Verified: %d\n",(int)good_sigs.size());
printf("# Segments unsigned: %d\n",(int)no_sigs.size());
printf("# Segments with corrupted signatures: %d\n",(int)bad_sigs.size());
printf("\n");
print_x509_info(cert);
int compromised = 0;
for(seglist::const_iterator seg = good_sigs.begin(); seg != good_sigs.end() && opt_all;
seg++){
if(*seg==good_sigs.front()) printf("%sSegments with valid signatures:\n",prn);
printf("\t%s\n",seg->name.c_str());
prn = "\n";
}
for(seglist::const_iterator seg = no_sigs.begin();
seg != no_sigs.end();
seg++){
if(*seg==no_sigs.front()) printf("%sUnsigned segments:\n",prn);
printf("\t%s\n",seg->name.c_str());
prn = "\n";
/* Only unsigned data segments are a problem */
if(af_segname_page_number(seg->name.c_str())>=0){
compromised++;
}
}
for(seglist::const_iterator seg = bad_sigs.begin();
seg != bad_sigs.end();
seg++){
if(*seg==bad_sigs.front()) printf("%sBad signature segments:\n",prn);
printf("\t%s\n",seg->name.c_str());
prn = "\n";
compromised++;
}
for(seglist::const_iterator seg = unknown_errors.begin();
seg != unknown_errors.end();
seg++){
if(*seg==unknown_errors.front()) printf("%sUnknown error segments:\n",prn);
printf("\t%s\n",seg->name.c_str());
prn = "\n";
compromised++;
}
int highest = highest_chain(segments);
printf("\nNumber of custody chains: %d\n",highest+1);
for(int i=0;i<=highest;i++){
/* Now print each one */
printf("---------------------\n");
printf("Signed Bill of Material #%d:\n\n",i+1);
/* Get the segment and verify */
size_t chainbuf_len = 0;
char segname[AF_MAX_NAME_LEN];
snprintf(segname,sizeof(segname),AF_BOM_SEG,i);
if(af_get_seg(af,segname,0,0,&chainbuf_len)){
printf("*** BOM MISSING ***\n");
compromised++;
}
char *chainbuf = (char *)malloc(chainbuf_len+1);
if(af_get_seg(af,segname,0,(u_char *)chainbuf,&chainbuf_len)){
printf("*** CANNOT READ BOM ***\n");
compromised++;
}
chainbuf[chainbuf_len]=0; // terminate
#ifdef USE_AFFSIGS
if(verify_bom_signature(af,chainbuf)){
printf("*** BOM SIGNATURE INVALID ***\n");
compromised++;
}
#else
printf("BOM signature cannot be verified beause libxpat is not available.\n");
#endif
}
printf("---------------------\n");
af_close(af);
#ifdef USE_AFFSIGS
if(compromised){
printf("\nEVIDENCE FILE DOES NOT VERIFY.\n");
printf("ERRORS DETECTED: %d\n",compromised);
printf("EVIDENTUARY VALUE MAY BE COMPROMISED.\n");
return -1;
}
printf("\nEVIDENCE FILE VERIFIES.\n");
return 0;
#endif
printf("\n");
return -1;
}
int hash_verify(AFFILE *af)
{
/* See if there is a SHA1 segment */
unsigned char sha1_buf[20];
unsigned char md5_buf[16];
char hexbuf[256];
size_t sha1_len = sizeof(sha1_buf);
size_t md5_len =sizeof(md5_buf);
const EVP_MD *md5_evp = 0;
const EVP_MD *sha1_evp = 0;
EVP_MD_CTX md5,sha1;
if(af_get_seg(af,AF_SHA1,0,sha1_buf,&sha1_len)==0){
printf("SHA1 stored in file: %s\n",af_hexbuf(hexbuf,sizeof(hexbuf),sha1_buf,sha1_len,0));
sha1_evp = EVP_get_digestbyname("sha1");
EVP_DigestInit(&sha1,sha1_evp);
}
if(af_get_seg(af,AF_MD5,0,md5_buf,&md5_len)==0){
printf("MD5 stored in file: %s\n",af_hexbuf(hexbuf,sizeof(hexbuf),md5_buf,md5_len,0));
md5_evp = EVP_get_digestbyname("md5");
EVP_DigestInit(&md5,md5_evp);
}
/* Might as well read this puppy */
u_char *buf = (u_char *)malloc(af_get_pagesize(af));
ssize_t readsize = 0;
ssize_t total_read = 0;
af_seek(af,0L,0);
aftimer t;
t.start();
printf("\n");
do {
char tbuf[64];
double frac = (double)total_read / af_get_imagesize(af);
printf(" Read %14zd/%14"PRId64" bytes; done in %s\n",
total_read,af_get_imagesize(af),t.eta_text(tbuf,frac));
readsize = af_read(af,buf,af_get_pagesize(af));
if(readsize<1) break;
if(md5_evp) EVP_DigestUpdate(&md5,buf,readsize);
if(sha1_evp) EVP_DigestUpdate(&sha1,buf,readsize);
total_read += readsize;
} while(total_read < af_get_imagesize(af));
printf("\n");
if(sha1_evp){
unsigned char sha1_calc[32];
unsigned int sha1_calc_len = sizeof(sha1_calc);
EVP_DigestFinal(&sha1,sha1_calc,(unsigned int *)&sha1_calc_len);
printf("Calculated SHA1: %s ",af_hexbuf(hexbuf,sizeof(hexbuf),sha1_calc,sha1_calc_len,0));
if(memcmp(sha1_buf,sha1_calc,sha1_len)==0){
printf("VERIFIES\n");
} else {
printf("INVALID\n");
}
}
if(md5_evp){
unsigned char md5_calc[32];
unsigned int md5_calc_len = sizeof(md5_calc);
EVP_DigestFinal(&md5,md5_calc,(unsigned int *)&md5_calc_len);
printf("Calculated MD5: %s ",af_hexbuf(hexbuf,sizeof(hexbuf),md5_calc,md5_calc_len,0));
if(memcmp(md5_buf,md5_calc,md5_len)==0){
printf("VERIFIES\n");
} else {
printf("INVALID\n");
}
}
af_close(af);
return 0;
}
int process(const char *fn)
{
AFFILE *af = af_open(fn,O_RDONLY,0666);
if(!af) af_err(1,fn);
/* Get the public key */
unsigned char certbuf[65536];
size_t certbuf_len = sizeof(certbuf);
if(af_get_seg(af,AF_SIGN256_CERT,0,certbuf,&certbuf_len)){
/* See if it is present, but encrypted */
if(af_get_seg(af,AF_SIGN256_CERT AF_AES256_SUFFIX,0,0,0)==0){
errx(1,"%s: signed file is encrypted; present decryption key to verify signature",fn);
}
printf("%s: no signing certificate present. \n\n",fn);
return hash_verify(af);
}
return crypto_verify(af,certbuf,certbuf_len);
}
int main(int argc,char **argv)
{
int bflag, ch;
bflag = 0;
while ((ch = getopt(argc, argv, "ach?vV")) != -1) {
switch (ch) {
case 'a': opt_all = 1; break;
case 'c': opt_change = 1; break;
case 'v': opt_verbose++; break;
case 'h':
case '?':
default:
usage();
break;
case 'V':
printf("%s version %s\n",progname,PACKAGE_VERSION);
exit(0);
}
}
argc -= optind;
argv += optind;
if(argc!=1){
usage();
}
OpenSSL_add_all_digests();
return process(argv[0]);
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Fri, Nov 21, 9:54 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1361124
Default Alt Text
affverify.cpp (14 KB)
Attached To
Mode
rXMOUNT xmount
Attached
Detach File
Event Timeline
Log In to Comment