xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/validate.c (revision 9ee9e0d7de4c59c936a17df52be682915dc66f43)
1 /*
2  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
3  * All rights reserved.
4  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
5  * their moral rights under the UK Copyright Design and Patents Act 1988 to
6  * be recorded as the authors of this copyright work.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9  * use this file except in compliance with the License.
10  *
11  * You may obtain a copy of the License at
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 #include "config.h"
22 
23 #include "packet-parse.h"
24 #include "packet-show.h"
25 #include "keyring.h"
26 #include "signature.h"
27 #include "netpgpsdk.h"
28 
29 #include "readerwriter.h"
30 #include "netpgpdefs.h"
31 #include "memory.h"
32 #include "keyring_local.h"
33 #include "parse_local.h"
34 #include "validate.h"
35 
36 #ifdef HAVE_ASSERT_H
37 #include <assert.h>
38 #endif
39 
40 #include <string.h>
41 
42 
43 /* Does the signed hash match the given hash? */
44 static          bool
45 check_binary_signature(const unsigned len,
46 		       const unsigned char *data,
47 		       const __ops_signature_t * sig,
48 		    const __ops_public_key_t * signer __attribute__((unused)))
49 {
50 	unsigned char   hashout[OPS_MAX_HASH_SIZE];
51 	unsigned char   trailer[6];
52 	unsigned int    hashedlen;
53 	__ops_hash_t	hash;
54 	unsigned	n = 0;
55 
56 	__ops_hash_any(&hash, sig->info.hash_algorithm);
57 	hash.init(&hash);
58 	hash.add(&hash, data, len);
59 	switch (sig->info.version) {
60 	case OPS_V3:
61 		trailer[0] = sig->info.type;
62 		trailer[1] = (unsigned)(sig->info.creation_time) >> 24;
63 		trailer[2] = (unsigned)(sig->info.creation_time) >> 16;
64 		trailer[3] = (unsigned)(sig->info.creation_time) >> 8;
65 		trailer[4] = (unsigned char)(sig->info.creation_time);
66 		hash.add(&hash, &trailer[0], 5);
67 		break;
68 
69 	case OPS_V4:
70 		hash.add(&hash, sig->info.v4_hashed_data,
71 				sig->info.v4_hashed_data_length);
72 		trailer[0] = 0x04;	/* version */
73 		trailer[1] = 0xFF;
74 		hashedlen = sig->info.v4_hashed_data_length;
75 		trailer[2] = hashedlen >> 24;
76 		trailer[3] = hashedlen >> 16;
77 		trailer[4] = hashedlen >> 8;
78 		trailer[5] = hashedlen;
79 		hash.add(&hash, &trailer[0], 6);
80 		break;
81 
82 	default:
83 		fprintf(stderr, "Invalid signature version %d\n",
84 				sig->info.version);
85 		return false;
86 	}
87 
88 	n = hash.finish(&hash, hashout);
89 	if (__ops_get_debug_level(__FILE__)) {
90 		printf("check_binary_signature: hash length %" PRIsize "u\n",
91 			hash.size);
92 	}
93 	return __ops_check_signature(hashout, n, sig, signer);
94 }
95 
96 static int
97 keydata_reader(void *dest, size_t length, __ops_error_t ** errors,
98 	       __ops_reader_info_t * rinfo,
99 	       __ops_parse_cb_info_t * cbinfo)
100 {
101 	validate_reader_t *reader = __ops_reader_get_arg(rinfo);
102 
103 	OPS_USED(errors);
104 	OPS_USED(cbinfo);
105 	if (reader->offset == reader->key->packets[reader->packet].length) {
106 		++reader->packet;
107 		reader->offset = 0;
108 	}
109 	if (reader->packet == reader->key->npackets)
110 		return 0;
111 
112 	/*
113 	 * we should never be asked to cross a packet boundary in a single
114 	 * read
115 	 */
116 	assert(reader->key->packets[reader->packet].length >=
117 			reader->offset + length);
118 
119 	(void) memcpy(dest,
120 		&reader->key->packets[reader->packet].raw[reader->offset],
121 		length);
122 	reader->offset += length;
123 
124 	return length;
125 }
126 
127 static void
128 free_signature_info(__ops_signature_info_t * sig)
129 {
130 	free(sig->v4_hashed_data);
131 	free(sig);
132 }
133 
134 static void
135 copy_signature_info(__ops_signature_info_t * dst,
136 			const __ops_signature_info_t * src)
137 {
138 	(void) memcpy(dst, src, sizeof(*src));
139 	dst->v4_hashed_data = calloc(1, src->v4_hashed_data_length);
140 	(void) memcpy(dst->v4_hashed_data, src->v4_hashed_data,
141 		src->v4_hashed_data_length);
142 }
143 
144 static void
145 add_sig_to_list(const __ops_signature_info_t *sig,
146 			__ops_signature_info_t **sigs,
147 			unsigned *count)
148 {
149 	if (*count == 0) {
150 		*sigs = calloc(*count + 1, sizeof(__ops_signature_info_t));
151 	} else {
152 		*sigs = realloc(*sigs,
153 				(*count + 1) * sizeof(__ops_signature_info_t));
154 	}
155 	copy_signature_info(&(*sigs)[*count], sig);
156 	*count += 1;
157 }
158 
159 
160 __ops_parse_cb_return_t
161 __ops_validate_key_cb(const __ops_parser_content_t * contents,
162 			__ops_parse_cb_info_t * cbinfo)
163 {
164 	const __ops_parser_content_union_t *content = &contents->u;
165 	const __ops_keydata_t	*signer;
166 	validate_key_cb_t	*key = __ops_parse_cb_get_arg(cbinfo);
167 	__ops_error_t		**errors = __ops_parse_cb_get_errors(cbinfo);
168 	bool			 valid = false;
169 
170 	if (__ops_get_debug_level(__FILE__)) {
171 		printf("%s\n", __ops_show_packet_tag(contents->tag));
172 	}
173 
174 	switch (contents->tag) {
175 	case OPS_PTAG_CT_PUBLIC_KEY:
176 		assert(key->pkey.version == 0);
177 		key->pkey = content->public_key;
178 		return OPS_KEEP_MEMORY;
179 
180 	case OPS_PTAG_CT_PUBLIC_SUBKEY:
181 		if (key->subkey.version)
182 			__ops_public_key_free(&key->subkey);
183 		key->subkey = content->public_key;
184 		return OPS_KEEP_MEMORY;
185 
186 	case OPS_PTAG_CT_SECRET_KEY:
187 		key->skey = content->secret_key;
188 		key->pkey = key->skey.public_key;
189 		return OPS_KEEP_MEMORY;
190 
191 	case OPS_PTAG_CT_USER_ID:
192 		if (key->user_id.user_id)
193 			__ops_user_id_free(&key->user_id);
194 		key->user_id = content->user_id;
195 		key->last_seen = ID;
196 		return OPS_KEEP_MEMORY;
197 
198 	case OPS_PTAG_CT_USER_ATTRIBUTE:
199 		assert(content->user_attribute.data.len);
200 		printf("user attribute, length=%d\n",
201 			(int) content->user_attribute.data.len);
202 		if (key->user_attribute.data.len)
203 			__ops_user_attribute_free(&key->user_attribute);
204 		key->user_attribute = content->user_attribute;
205 		key->last_seen = ATTRIBUTE;
206 		return OPS_KEEP_MEMORY;
207 
208 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
209 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
210 
211 		signer = __ops_keyring_find_key_by_id(key->keyring,
212 					 content->signature.info.signer_id);
213 		if (!signer) {
214 			add_sig_to_list(&content->signature.info,
215 					&key->result->unknown_sigs,
216 					&key->result->unknownc);
217 			break;
218 		}
219 		switch (content->signature.info.type) {
220 		case OPS_CERT_GENERIC:
221 		case OPS_CERT_PERSONA:
222 		case OPS_CERT_CASUAL:
223 		case OPS_CERT_POSITIVE:
224 		case OPS_SIG_REV_CERT:
225 			if (key->last_seen == ID)
226 				valid = __ops_check_user_id_certification_signature(&key->pkey,
227 							      &key->user_id,
228 							&content->signature,
229 				       __ops_get_public_key_from_data(signer),
230 					key->rarg->key->packets[key->rarg->packet].raw);
231 			else
232 				valid = __ops_check_user_attribute_certification_signature(&key->pkey,
233 						       &key->user_attribute,
234 							&content->signature,
235 				       __ops_get_public_key_from_data(signer),
236 					key->rarg->key->packets[key->rarg->packet].raw);
237 
238 			break;
239 
240 		case OPS_SIG_SUBKEY:
241 			/*
242 			 * XXX: we should also check that the signer is the
243 			 * key we are validating, I think.
244 			 */
245 			valid = __ops_check_subkey_signature(&key->pkey, &key->subkey,
246 							&content->signature,
247 				       __ops_get_public_key_from_data(signer),
248 			    key->rarg->key->packets[key->rarg->packet].raw);
249 			break;
250 
251 		case OPS_SIG_DIRECT:
252 			valid = __ops_check_direct_signature(&key->pkey, &content->signature,
253 				       __ops_get_public_key_from_data(signer),
254 			    key->rarg->key->packets[key->rarg->packet].raw);
255 			break;
256 
257 		case OPS_SIG_STANDALONE:
258 		case OPS_SIG_PRIMARY:
259 		case OPS_SIG_REV_KEY:
260 		case OPS_SIG_REV_SUBKEY:
261 		case OPS_SIG_TIMESTAMP:
262 		case OPS_SIG_3RD_PARTY:
263 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
264 				    "Verification of signature type 0x%02x not yet implemented\n", content->signature.info.type);
265 			break;
266 
267 		default:
268 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
269 				    "Unexpected signature type 0x%02x\n",
270 				    	content->signature.info.type);
271 		}
272 
273 		if (valid) {
274 			add_sig_to_list(&content->signature.info,
275 				&key->result->valid_sigs,
276 				&key->result->validc);
277 		} else {
278 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE, "Bad Sig");
279 			add_sig_to_list(&content->signature.info,
280 					&key->result->invalid_sigs,
281 					&key->result->invalidc);
282 		}
283 		break;
284 
285 		/* ignore these */
286 	case OPS_PARSER_PTAG:
287 	case OPS_PTAG_CT_SIGNATURE_HEADER:
288 	case OPS_PARSER_PACKET_END:
289 		break;
290 
291 	case OPS_PARSER_CMD_GET_SK_PASSPHRASE:
292 		if (key->cb_get_passphrase) {
293 			return key->cb_get_passphrase(contents, cbinfo);
294 		}
295 		break;
296 
297 	default:
298 		fprintf(stderr, "unexpected tag=0x%x\n", contents->tag);
299 		assert(/* CONSTCOND */0);
300 		break;
301 	}
302 	return OPS_RELEASE_MEMORY;
303 }
304 
305 __ops_parse_cb_return_t
306 validate_data_cb(const __ops_parser_content_t * contents,
307 			__ops_parse_cb_info_t * cbinfo)
308 {
309 	const __ops_parser_content_union_t *content = &contents->u;
310 	const __ops_keydata_t	*signer;
311 	validate_data_cb_t	*data = __ops_parse_cb_get_arg(cbinfo);
312 	__ops_error_t		**errors = __ops_parse_cb_get_errors(cbinfo);
313 	bool			 valid = false;
314 
315 	if (__ops_get_debug_level(__FILE__)) {
316 		printf("validate_data_cb: %s\n",
317 			__ops_show_packet_tag(contents->tag));
318 	}
319 	switch (contents->tag) {
320 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
321 		/*
322 		 * ignore - this gives us the "Armor Header" line "Hash:
323 		 * SHA1" or similar
324 		 */
325 		break;
326 
327 	case OPS_PTAG_CT_LITERAL_DATA_HEADER:
328 		/* ignore */
329 		break;
330 
331 	case OPS_PTAG_CT_LITERAL_DATA_BODY:
332 		data->data.literal_data_body = content->literal_data_body;
333 		data->use = LITERAL_DATA;
334 		__ops_memory_add(data->mem, data->data.literal_data_body.data,
335 			       data->data.literal_data_body.length);
336 		return OPS_KEEP_MEMORY;
337 
338 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY:
339 		data->data.signed_cleartext_body = content->signed_cleartext_body;
340 		data->use = SIGNED_CLEARTEXT;
341 		__ops_memory_add(data->mem, data->data.literal_data_body.data,
342 			       data->data.literal_data_body.length);
343 		return OPS_KEEP_MEMORY;
344 
345 	case OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
346 		/* this gives us an __ops_hash_t struct */
347 		break;
348 
349 	case OPS_PTAG_CT_SIGNATURE:	/* V3 sigs */
350 	case OPS_PTAG_CT_SIGNATURE_FOOTER:	/* V4 sigs */
351 		if (__ops_get_debug_level(__FILE__)) {
352 			unsigned i = 0;
353 
354 			printf("\n*** hashed data:\n");
355 			for (i = 0;
356 			     i < content->signature.info.v4_hashed_data_length;
357 			     i++) {
358 				printf("0x%02x ",
359 				content->signature.info.v4_hashed_data[i]);
360 			}
361 			printf("\n");
362 			printf("  type=%02x signer_id=",
363 				content->signature.info.type);
364 			hexdump(content->signature.info.signer_id,
365 				sizeof(content->signature.info.signer_id), "");
366 			printf("\n");
367 		}
368 		signer = __ops_keyring_find_key_by_id(data->keyring,
369 					 content->signature.info.signer_id);
370 		if (!signer) {
371 			OPS_ERROR(errors, OPS_E_V_UNKNOWN_SIGNER,
372 					"Unknown Signer");
373 			add_sig_to_list(&content->signature.info,
374 					&data->result->unknown_sigs,
375 					&data->result->unknownc);
376 			break;
377 		}
378 		switch (content->signature.info.type) {
379 		case OPS_SIG_BINARY:
380 		case OPS_SIG_TEXT:
381 			valid = check_binary_signature(
382 					__ops_memory_get_length(data->mem),
383 					__ops_memory_get_data(data->mem),
384 					&content->signature,
385 					__ops_get_public_key_from_data(signer));
386 			break;
387 
388 		default:
389 			OPS_ERROR_1(errors, OPS_E_UNIMPLEMENTED,
390 				    "No Sig Verification type 0x%02x yet\n",
391 				    content->signature.info.type);
392 			break;
393 
394 		}
395 
396 		__ops_memory_free(data->mem);
397 
398 		if (valid) {
399 			add_sig_to_list(&content->signature.info,
400 					&data->result->valid_sigs,
401 					&data->result->validc);
402 		} else {
403 			OPS_ERROR(errors, OPS_E_V_BAD_SIGNATURE,
404 					"Bad Signature");
405 			add_sig_to_list(&content->signature.info,
406 					&data->result->invalid_sigs,
407 					&data->result->invalidc);
408 		}
409 		break;
410 
411 		/* ignore these */
412 	case OPS_PARSER_PTAG:
413 	case OPS_PTAG_CT_SIGNATURE_HEADER:
414 	case OPS_PTAG_CT_ARMOUR_HEADER:
415 	case OPS_PTAG_CT_ARMOUR_TRAILER:
416 	case OPS_PTAG_CT_ONE_PASS_SIGNATURE:
417 	case OPS_PARSER_PACKET_END:
418 		break;
419 
420 	default:
421 		OPS_ERROR(errors, OPS_E_V_NO_SIGNATURE, "No signature");
422 		break;
423 	}
424 	return OPS_RELEASE_MEMORY;
425 }
426 
427 static void
428 keydata_destroyer(__ops_reader_info_t * rinfo)
429 {
430 	free(__ops_reader_get_arg(rinfo));
431 }
432 
433 void
434 __ops_keydata_reader_set(__ops_parse_info_t *pinfo,
435 			const __ops_keydata_t *key)
436 {
437 	validate_reader_t *data = calloc(1, sizeof(*data));
438 
439 	data->key = key;
440 	data->packet = 0;
441 	data->offset = 0;
442 
443 	__ops_reader_set(pinfo, keydata_reader, keydata_destroyer, data);
444 }
445 
446 /**
447  * \ingroup HighLevel_Verify
448  * \brief Indicicates whether any errors were found
449  * \param result Validation result to check
450  * \return false if any invalid signatures or unknown signers
451  	or no valid signatures; else true
452  */
453 static bool
454 validate_result_status(__ops_validation_t *val)
455 {
456 	return val->validc && !val->invalidc && !val->unknownc;
457 }
458 
459 /**
460  * \ingroup HighLevel_Verify
461  * \brief Validate all signatures on a single key against the given keyring
462  * \param result Where to put the result
463  * \param key Key to validate
464  * \param keyring Keyring to use for validation
465  * \param cb_get_passphrase Callback to use to get passphrase
466  * \return true if all signatures OK; else false
467  * \note It is the caller's responsiblity to free result after use.
468  * \sa __ops_validate_result_free()
469  */
470 bool
471 __ops_validate_key_signatures(__ops_validation_t * result,
472 				const __ops_keydata_t * key,
473 			    const __ops_keyring_t * keyring,
474 			    __ops_parse_cb_return_t cb_get_passphrase(const __ops_parser_content_t *, __ops_parse_cb_info_t *)
475 )
476 {
477 	__ops_parse_info_t *pinfo;
478 	validate_key_cb_t carg;
479 
480 	(void) memset(&carg, 0x0, sizeof(carg));
481 	carg.result = result;
482 	carg.cb_get_passphrase = cb_get_passphrase;
483 
484 	pinfo = __ops_parse_info_new();
485 	/* __ops_parse_options(&opt,OPS_PTAG_CT_SIGNATURE,OPS_PARSE_PARSED); */
486 
487 	carg.keyring = keyring;
488 
489 	__ops_parse_cb_set(pinfo, __ops_validate_key_cb, &carg);
490 	pinfo->rinfo.accumulate = true;
491 	__ops_keydata_reader_set(pinfo, key);
492 
493 	/* Note: Coverity incorrectly reports an error that carg.rarg */
494 	/* is never used. */
495 	carg.rarg = pinfo->rinfo.arg;
496 
497 	__ops_parse(pinfo);
498 
499 	__ops_public_key_free(&carg.pkey);
500 	if (carg.subkey.version)
501 		__ops_public_key_free(&carg.subkey);
502 	__ops_user_id_free(&carg.user_id);
503 	__ops_user_attribute_free(&carg.user_attribute);
504 
505 	__ops_parse_info_delete(pinfo);
506 
507 	if (result->invalidc || result->unknownc || !result->validc)
508 		return false;
509 	else
510 		return true;
511 }
512 
513 /**
514    \ingroup HighLevel_Verify
515    \param result Where to put the result
516    \param ring Keyring to use
517    \param cb_get_passphrase Callback to use to get passphrase
518    \note It is the caller's responsibility to free result after use.
519    \sa __ops_validate_result_free()
520 */
521 bool
522 __ops_validate_all_signatures(__ops_validation_t * result,
523 			    const __ops_keyring_t * ring,
524 			    __ops_parse_cb_return_t cb_get_passphrase(const __ops_parser_content_t *, __ops_parse_cb_info_t *)
525 )
526 {
527 	int             n;
528 
529 	(void) memset(result, 0x0, sizeof(*result));
530 	for (n = 0; n < ring->nkeys; ++n) {
531 		__ops_validate_key_signatures(result, &ring->keys[n], ring,
532 				cb_get_passphrase);
533 	}
534 	return validate_result_status(result);
535 }
536 
537 /**
538    \ingroup HighLevel_Verify
539    \brief Frees validation result and associated memory
540    \param result Struct to be freed
541    \note Must be called after validation functions
542 */
543 void
544 __ops_validate_result_free(__ops_validation_t *result)
545 {
546 	if (result != NULL) {
547 		if (result->valid_sigs) {
548 			free_signature_info(result->valid_sigs);
549 		}
550 		if (result->invalid_sigs) {
551 			free_signature_info(result->invalid_sigs);
552 		}
553 		if (result->unknown_sigs) {
554 			free_signature_info(result->unknown_sigs);
555 		}
556 		(void) free(result);
557 		result = NULL;
558 	}
559 }
560 
561 /**
562    \ingroup HighLevel_Verify
563    \brief Verifies the signatures in a signed file
564    \param result Where to put the result
565    \param filename Name of file to be validated
566    \param armoured Treat file as armoured, if set
567    \param keyring Keyring to use
568    \return true if signatures validate successfully;
569    	false if signatures fail or there are no signatures
570    \note After verification, result holds the details of all keys which
571    have passed, failed and not been recognised.
572    \note It is the caller's responsiblity to call
573    	__ops_validate_result_free(result) after use.
574 */
575 bool
576 __ops_validate_file(__ops_validation_t *result,
577 			const char *filename,
578 			const int armoured,
579 			const __ops_keyring_t *keyring)
580 {
581 	__ops_parse_info_t *pinfo = NULL;
582 	validate_data_cb_t validation;
583 	int             fd = 0;
584 
585 	fd = __ops_setup_file_read(&pinfo, filename, &validation,
586 				validate_data_cb, true);
587 	if (fd < 0) {
588 		return false;
589 	}
590 
591 	/* Set verification reader and handling options */
592 	(void) memset(&validation, 0x0, sizeof(validation));
593 	validation.result = result;
594 	validation.keyring = keyring;
595 	validation.mem = __ops_memory_new();
596 	__ops_memory_init(validation.mem, 128);
597 	/* Note: Coverity incorrectly reports an error that carg.rarg */
598 	/* is never used. */
599 	validation.rarg = pinfo->rinfo.arg;
600 
601 	if (armoured) {
602 		__ops_reader_push_dearmour(pinfo);
603 	}
604 
605 	/* Do the verification */
606 	__ops_parse(pinfo);
607 	if (__ops_get_debug_level(__FILE__)) {
608 		printf("valid=%d, invalid=%d, unknown=%d\n",
609 		       result->validc,
610 		       result->invalidc,
611 		       result->unknownc);
612 	}
613 
614 	/* Tidy up */
615 	if (armoured) {
616 		__ops_reader_pop_dearmour(pinfo);
617 	}
618 	__ops_teardown_file_read(pinfo, fd);
619 
620 	return validate_result_status(result);
621 }
622 
623 /**
624    \ingroup HighLevel_Verify
625    \brief Verifies the signatures in a __ops_memory_t struct
626    \param result Where to put the result
627    \param mem Memory to be validated
628    \param armoured Treat data as armoured, if set
629    \param keyring Keyring to use
630    \return true if signature validates successfully; false if not
631    \note After verification, result holds the details of all keys which
632    have passed, failed and not been recognised.
633    \note It is the caller's responsiblity to call
634    	__ops_validate_result_free(result) after use.
635 */
636 
637 bool
638 __ops_validate_mem(__ops_validation_t *result, __ops_memory_t *mem,
639 			const int armoured, const __ops_keyring_t *keyring)
640 {
641 	__ops_parse_info_t *pinfo = NULL;
642 	validate_data_cb_t validation;
643 
644 	__ops_setup_memory_read(&pinfo, mem, &validation, validate_data_cb,
645 			true);
646 
647 	/* Set verification reader and handling options */
648 	(void) memset(&validation, 0x0, sizeof(validation));
649 	validation.result = result;
650 	validation.keyring = keyring;
651 	validation.mem = __ops_memory_new();
652 	__ops_memory_init(validation.mem, 128);
653 	/* Note: Coverity incorrectly reports an error that carg.rarg */
654 	/* is never used. */
655 	validation.rarg = pinfo->rinfo.arg;
656 
657 	if (armoured) {
658 		__ops_reader_push_dearmour(pinfo);
659 	}
660 
661 	/* Do the verification */
662 	__ops_parse(pinfo);
663 	if (__ops_get_debug_level(__FILE__)) {
664 		printf("valid=%d, invalid=%d, unknown=%d\n",
665 		       result->validc,
666 		       result->invalidc,
667 		       result->unknownc);
668 	}
669 
670 	/* Tidy up */
671 	if (armoured) {
672 		__ops_reader_pop_dearmour(pinfo);
673 	}
674 	__ops_teardown_memory_read(pinfo, mem);
675 
676 	return validate_result_status(result);
677 }
678