File: affsign.cpp

package info (click to toggle)
afflib 3.7.22-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,584 kB
  • sloc: cpp: 20,911; ansic: 15,912; makefile: 520; sh: 436; python: 192
file content (314 lines) | stat: -rw-r--r-- 9,793 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
 * affsign.cpp:
 *
 * Sign an existing AFF file.
 * This file is a work of a US government employee and as such is in the Public domain.
 * Simson L. Garfinkel, March 12, 2012
 */

#include "affconfig.h"
#include "afflib.h"
#include "afflib_i.h"

#ifdef USE_AFFSIGS

#include "utils.h"
#include "base64.h"

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <openssl/pem.h>
#include <openssl/x509.h>


#include "aff_bom.h"

int opt_note = 0;
const char *opt_sign_key_file = 0;
const char *opt_sign_cert_file = 0;

using namespace std;
using namespace aff;

const char *progname = "affsign";

void usage()
{
    printf("%s version %s\n",progname,PACKAGE_VERSION);
    printf("usage: %s [options] filename.aff\n",progname);
    printf("This program will:\n");
    printf("  * Sign each segment if there are no segment signatures.\n");
    printf("  * Write signed chain-of-custody Bill of Materials segment.\n");
    printf("\nSignature Options:\n");
    printf("   -k filename.key   = specify private key for signing\n");
    printf("   -c filename.cer   = specify a X.509 certificate that matches the private key\n");
    printf("                       (by default, the file is assumed to be the same one\n");
    printf("                       provided with the -k option.)\n");
    printf("   -Z                = ZAP (remove) all signature segments.\n");
    printf("options:\n");
    printf("    -n      --- ask for a chain-of-custody note.\n");
    printf("    -v      --- Just print the version number and exit.\n");
    exit(0);
}


int affsign(const char *fn)
{
    AFFILE *af = af_open(fn,O_RDWR,0);
    if(!af) af_err(1,"%s",fn);

    struct af_vnode_info vni;
    if(af_vstat(af,&vni)) err(1,"af_vstat");

    if(vni.supports_metadata==0){
	/* If it is a raw file, we can create an AFM file to sign */
	if(vni.is_raw==0) errx(1,"%s: file does not support metadata. Cannot sign\n",fn);
	af_close(af);			// afm will open it
	char afmfile[MAXPATHLEN+1];
	char file000[MAXPATHLEN+1];
	char extension[MAXPATHLEN+1];
	strcpy(afmfile,fn);
	char *period = strrchr(afmfile,'.');
	if(!period) errx(1,"%s: file does not support metadata and lacks a file extension,\n"
			 "which is needed to create an AFM file '%s\n",afmfile,fn);
	strcpy(extension,period+1);	// get the extension

	/* If the file being opened is not a .000 file, and a .000 file exists, do not proceed */
	strcpy(period,".000");
	strcpy(file000,afmfile);	// make the 000 file
	strcpy(period,".afm");
	
	if(strcmp(extension,"000")!=0){
	    if(access(file000,F_OK)==0){
		errx(1,"Can't create .afm file because %s exists.\n",file000);
	    }
	}
	
	strcpy(period,".afm");		// we are now going to make an afm file
	af = af_open(afmfile,O_RDWR|O_CREAT,0600);
	if(!af) af_err(1,"%s: file does not support metadata and cannot create AFM file '%s\n",fn,afmfile);
	if(strcmp(extension,"000")!=0){
	    af_update_seg(af,AF_RAW_IMAGE_FILE_EXTENSION,0,(const u_char *)extension,strlen(extension));
	    af_close(af);
	    unlink(file000);		// get rid of that .000 file
	    af = af_open(afmfile,O_RDWR,0600);
	    if(!af) af_err(1,"%s: Created AFM file but cannot re-open it\n",fn);
	    /* Read the first byte to force a call to afm_split_raw_setup().
	     * The results of the read don't matter, but we better be able to read.
	     */
	    u_char buf[1];
	    if(af_read(af,buf,1)!=1){
		err(1,"Cannot read first byte of %s",fn);
	    }
	    af_seek(af,0L,0);
	}
    }

    seglist segments(af);

    if(isatty(fileno(stdout))){
	printf("Signing segments...\n");
	fflush(stdout);
    }
    
    bool signed_unsigned_segments = false;
    if(segments.has_signed_segments()==false){
	if(af_set_sign_files(af,opt_sign_key_file,opt_sign_cert_file)){
	    errx(1,"key file '%s' or certificate file '%s' is invalid",
		 opt_sign_key_file,opt_sign_cert_file);
	}
	int r = af_sign_all_unsigned_segments(af);
	if(r<0) af_err(1,"%s: all unsigned segments cannot be signed.",fn);
	if(r>0) signed_unsigned_segments = true;
    }

    aff_bom bom(opt_note);
    if(bom.read_files(opt_sign_cert_file,opt_sign_key_file)) err(1,"Can't read signature files???");

    u_char *pagebuf = (unsigned char *)calloc(af_page_size(af),1);
    u_char *parity_buf = (unsigned char *)calloc(af_page_size(af),1);
    bool compute_parity = true;		// do we need to compute the parity?

    /* Create the parity buffer if it doesn't exist. If the parity buffer exists, we'll just trust it.
     * We could do a two-pass here, one for creating the parity buffer, another for creating the BOM.
     * But that would require reading the data twice; hence this extra layer of complexity.
     */
    size_t parity_buf_len = af_page_size(af);
    if(af_get_seg(af,AF_PARITY0,0,parity_buf,&parity_buf_len)==0){
	compute_parity = false;		// no need to compute it; we read it
    }

    for(seglist::const_iterator seg = segments.begin(); seg!= segments.end();seg++){
	const char *segname = seg->name.c_str();

	if(isatty(fileno(stdout))){
	    printf("\rCalculating BOM for segment %s...   ",segname);
	    printf("\n");
	    fflush(stdout);
	}

	u_char seghash[32]; /* resultant message digest; could be any size */
	unsigned int seghash_len = sizeof(seghash); /* big enough to hold SHA256 */
	int sigmode = 0;
	int64_t pagenumber = af_segname_page_number(segname);
	if(pagenumber>=0){
	    /* Page segments must run in SIGNATURE_MODE1 - the actual data in the page */
	    size_t this_pagesize = af_page_size(af);
	    if(af_get_page(af,pagenumber,pagebuf,&this_pagesize)){ 
		free(pagebuf);
		return -1;
	    }
	    /* Add to parity buf if we are making a parity page*/
	    if(compute_parity){
		for(u_int i=0;i<this_pagesize;i++){
		    parity_buf[i] ^= pagebuf[i];
		}
	    }
	    aff_bom::make_hash(seghash,0,segname,pagebuf,this_pagesize);
	    sigmode = AF_SIGNATURE_MODE1;
	}
	else{
	    /* Non-Page segments can be run in SIGNATURE_MODE0 - the actual data in the file */
	    size_t seglen=0;
	    if(af_get_seg(af,segname,0,0,&seglen)){
		err(1,"Cannot read length of segment '%s' on input file %s", segname,af_filename(af));
	    }
	    unsigned char *segbuf = (unsigned char *)malloc(seglen);
	    if(!segbuf){
		err(1,"Cannot allocated %d bytes for segment '%s' in %s",
		    (int)seglen,segname,af_filename(af));
	    }
	    /* Now get the raw source segment */
	    uint32_t arg=0;
	    if(af_get_seg(af,segname,&arg,segbuf,&seglen)){
		err(1,"Cannot read segment '%s' in %s. Deleting output file", segname,af_filename(af));
	    }
	    aff_bom::make_hash(seghash,arg,segname,segbuf,seglen);
	    sigmode = AF_SIGNATURE_MODE0;
	    free(segbuf);
	}
	bom.add(segname,sigmode,seghash,seghash_len); // add to the BOM
    }

    /* If we have been making the parity buf:
     * 1 - Write it out; add it to the BOM
     * 2 - Write out the signature segment for the parity buf; add it to the bom
     */
    if(compute_parity){
	if(af_update_seg(af,AF_PARITY0,0,parity_buf,af_page_size(af))) err(1,"Can't write %s",AF_PARITY0);

	/* Add the parity page that we made to the BOM */
	u_char seghash[32]; /* resultant message digest; could be any size */
	unsigned int seghash_len = sizeof(seghash); /* big enough to hold SHA256 */

	aff_bom::make_hash(seghash,0,AF_PARITY0,parity_buf,af_page_size(af));
	bom.add(AF_PARITY0,AF_SIGNATURE_MODE0,seghash,seghash_len);


	/* If we are signing segments for the first time, we need to sign the parity page
	 * and then add the parity page's signature segment to the BOM as well.
	 */
	if(signed_unsigned_segments){
	    af_sign_seg(af,AF_PARITY0);	// sign the parity segment if we signed the other segments
	    bom.add(af,AF_PARITY0_SIG);

	    u_char buf[1024];
	    size_t buflen = sizeof(buf);

	    const char *segname = AF_PARITY0_SIG;
	    if(af_get_seg(af,segname,0,buf,&buflen)==0){ // Get the signature 
		aff_bom::make_hash(seghash,0,segname,buf,buflen); // and add it to the BOM
		bom.add(segname,AF_SIGNATURE_MODE0,seghash,seghash_len);
	    }
	}
    }

    if(isatty(fileno(stdout))){
	printf("                                                    \r\n");
	fflush(stdout);
    }
    
    bom.close();
    bom.write(af,segments);
    af_close(af);
    return 0;
}

int remove_signatures(const char *fn)
{
    AFFILE *af = af_open(fn,O_RDWR,0);
    if(!af) af_err(1,"%s",fn);
    
    aff::seglist sl(af);
    for(aff::seglist::const_iterator i = sl.begin();
	i!= sl.end();
	i++){
	if(af_is_signature_segment(i->name.c_str()) || i->name==AF_SIGN256_CERT){
	    cout << "Deleting " << i->name << "\n";
	    af_del_seg(af,i->name.c_str());
	}
    }
    af_close(af);
    return 0;
}

int main(int argc,char **argv)
{
    int ch;
    int opt_zap = 0;

    while ((ch = getopt(argc, argv, "nk:c:h?vZ")) != -1) {
	switch (ch) {
	case 'n': opt_note = 1;break;
	case 'k':
	    if(access(optarg,R_OK)) err(1,"%s",optarg);
	    opt_sign_key_file = optarg;
	    break;
	case 'c':
	    if(access(optarg,R_OK)) err(1,"%s",optarg);
	    opt_sign_cert_file = optarg;
	    break;
	case 'v':
	    printf("%s version %s\n",progname,PACKAGE_VERSION);
	    exit(0);
	case 'Z':
	    opt_zap = 1;
	    break;
	case 'h':
	case '?':
	default:
	    usage();
	    break;
	}
    }
    argc -= optind;
    argv += optind;

    if(opt_sign_cert_file==0) opt_sign_cert_file=opt_sign_key_file; // if not set, make same as key file


    if(argc!=1){
	usage();
    }

    if(opt_zap) return remove_signatures(argv[0]);

    /* We either need both a key file and a cert file, or neither */
    if((opt_sign_key_file==0) || (opt_sign_cert_file==0)){
	errx(1,"Both a private key and a certificate must be specified.");
    }


    return affsign(argv[0]);
}
#else
int main(int argc,char **argv)
{
    fprintf(stderr,"afflib compiled without USE_AFFSIGS.  affsign cannot run.\n");
    exit(-1);
}

#endif