~apreiml/hare-openpgp

e25323a49d2940d295e8c5e39eb7bdbbebcfad92 — Armin Preiml 8 months ago 2ac6876 master
improve key parsing and error messages, lots of debug prints
M format/openpgp/errors.ha => format/openpgp/errors.ha +18 -5
@@ 8,15 8,21 @@ export type invalid = !void;
export type unexpected = !void;
export type weakhash = !void;

export type unknowntag = !u8;
export type critsigsub = !u8;
export type unsupversion = !u8;
export type unsupsenc = !u8;
export type unsupkey = !u8;

export type error = !(io::error | unsupported | badsig | invalid | unexpected |
	weakhash | critsigsub);
export type error = !(io::error | unsupported | badsig | invalid | unexpected
	| weakhash | unknowntag | critsigsub | unsupversion | unsupsenc
	| unsupkey);

// Returns the string representation of error 'e'. The string can depend on a
// statically allocated buffer.
export fn strerror(e: error) str = {
	static let errbuf: [256]u8 = [0...];
	errbuf = [0...];

	match (e) {
	case let e: unsupported =>


@@ 30,17 36,24 @@ export fn strerror(e: error) str = {
	case weakhash =>
		return "Signature on key or data is done using a weak hash "
			"function and can not be trusted.";
	case let e: unknowntag =>
		// XXX: could be unsupported, if implementation matures
		return fmt::bsprintf(errbuf, "Unknown tag id: {}", e: u8);
	case let e: unsupversion =>
		return fmt::bsprintf(errbuf, "Unsupported format version: {}", e: u8);
	case let e: critsigsub =>
		return fmt::bsprint(errbuf, "Unsupported critical signature "
			"subtype:", strsigsubtype(e));
	case let e: unsupsenc =>
		return fmt::bsprint(errbuf, "Unsupported symmetric encryption "
			"algo:", strsenclgo(e));
	case let e: unsupkey =>
		return fmt::bsprint(errbuf, "Unsupported key:", strpubkeyalgo(e));
	case let e: io::error =>
		return io::strerror(e);
	};
};

let unsupported_version: unsupported = "Unsupported file version";
let unsupported_encalgo: unsupported = "Unsupported encryption algorithm";
let unsupported_hashalgo: unsupported = "Unsupported hash algorithm";
let unsupported_key: unsupported = "Unsupported public key crypto algorithm";
let unsupported_fp: unsupported = "Unsupported fingerprint method";
let unsupported_critsub: unsupported = "Unsupported critical signature sub-packet";

M format/openpgp/key.ha => format/openpgp/key.ha +1 -1
@@ 127,7 127,7 @@ fn ivsz(algo: u8) (size | error) = {
	case sencalgo::AES256 =>
		return 32z;
	case =>
		return unsupported_encalgo;
		return (algo: unsupsenc): error;
	};
};


M format/openpgp/reader.ha => format/openpgp/reader.ha +55 -62
@@ 80,9 80,7 @@ fn next(d: *reader) (packet | error) = {
};

fn hasnext(d: *reader) (bool | error) = {
	print("hasnext");
	if (d.next is packet) {
		print("cached");
		return true;
	};



@@ 118,18 116,14 @@ fn trynext(d: *reader) (packet | error | io::EOF) = {
	if (io::readall(d.h, buf[..1])? is io::EOF) {
		return io::EOF;
	};
	print("HEAD");
	hexdump(buf);

	let hsz = 1z;
	let t = buf[0];
	if (t & 0x80 != 0x80) {
		print("inv1");
		return invalid;
	};

	const old = t & 0x40 == 0;
	print("ha", old);
	let p = packet {
		stream = &packet_vtable,
		h = d.h,


@@ 140,14 134,11 @@ fn trynext(d: *reader) (packet | error | io::EOF) = {

	parsesz(d.h, &p, t)?;

	print("TAG", p.tag: u8);
	print("SZ", p.sz);
	return p;
};

fn parsesz(src: io::handle, p: *packet, t: u8) (void | error) = {
	const old = t & 0x40 == 0;
	print("old", old);

	p.szt = sizetype::FIXED;
	p.sz = 0z;


@@ 162,11 153,11 @@ fn parsesz(src: io::handle, p: *packet, t: u8) (void | error) = {
			p.szt = sizetype::INDETERMINATE;
			return;
		case =>
			// unreachable
			abort();
		};

		if (io::readall(src, buf[..max])? is io::EOF) {
			print("wtf");
			return invalid;
		};
		p.hsz += max;


@@ 174,7 165,6 @@ fn parsesz(src: io::handle, p: *packet, t: u8) (void | error) = {
			p.sz <<= 8;
			p.sz += buf[i];
		};
		print("size", p.sz);
		return;
	};



@@ 260,23 250,23 @@ fn pkt_parse_pubkey(p: *packet, mh: *multihash) (*keypair | error) = {

	let buf: [6]u8 = [0...];
	if (io::readall(p, buf)? is io::EOF) {
		return errors::invalid;
		return invalid;
	};

	if (buf[0] != 4) {
		print("unsup1");
		return unsupported_key: unsupported;
	const version = buf[0];
	if (version != 4) {
		return version: unsupversion;
	};

	let i = time::from_unix(endian::begetu32(buf[1..5]): i64);
	let d = date::from_instant(chrono::UTC, i);
	let created = date::from_instant(chrono::UTC, i);

	if (buf[5] != 1) { // RSA sign or encrypt
		print("unsup2");
		return unsupported_key: unsupported;
	const keyalgo = buf[5];
	if (keyalgo != pubkeyalgo::RSA) { // RSA sign or encrypt
		return keyalgo: unsupkey;
	};

	let key = alloc(newrsakey(d)): *keypair;
	let key = alloc(newrsakey(created)): *keypair;
	key.vtable.read_pub(key, p)?;

	let rawbuf = memio::buffer(&raw);


@@ 324,6 314,7 @@ export type keyiter = struct {

export fn load_keys(h: io::handle) keyiter = keyiter {
	r = newreader(h),
	...
};

export fn parse_key(h: io::handle) (key | void | error) = {


@@ 331,16 322,36 @@ export fn parse_key(h: io::handle) (key | void | error) = {
	return keys_next(&iter);
};

fn pkt_skip_until(r: *reader, tags: tag...) (void | io::EOF | error) = {
	for (true) {
		if (!hasnext(r)?) {
			return io::EOF;
		};

		const t = peek(r);
		for (let i = 0z; i < len(tags); i += 1) {
			if (t == tags[i]) {
				return;
			};
		};

		let p = next(r)?;
		pkt_skip(&p)?;
	};
};

export fn keys_next(iter: *keyiter) (key | void | error) = {
	print("Next");
	let mh = newmultihash();
	let g = &iter.r;
	let r = &iter.r;

	if (!hasnext(g)?) {
		return void;
	if (pkt_skip_until(r, tag::SECRET_KEY, tag::PUBLIC_KEY)? is io::EOF) {
		return;
	};
	let p = next(g)?;
	print(p.tag: u8);

	let p = next(r)?;
	// TODO: instead of defer, would be neat to safe p in iter or reader
	//       and skip it if set on pkt_skip_until. Requires ptr to compound
	defer pkt_skip(&p)!;

	const priv = switch (p.tag) {
	case tag::SECRET_KEY =>


@@ 348,31 359,11 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
	case tag::PUBLIC_KEY =>
		yield false;
	case =>
		return unexpected;
	};

	let primary = match (pkt_parse_key(&p, &mh, priv)) {
	case let k: *keypair =>
		yield k;
	case let e: unsupported =>
		print("unsupported");
		for (true) {
			print("skip");
			pkt_skip(&p)?;
			if (hasnext(g)?) {
				print("peek", peek(g): u8);
			};
			if (!hasnext(g)? || peek(g) == tag::SECRET_KEY
				|| peek(g) == tag::PUBLIC_KEY) {
				return e;
			};
			p = next(g)?;
		};
	case let e: error =>
		// TODO fail iterator?
		return e;
		return invalid;
	};

	let primary = pkt_parse_key(&p, &mh, priv)?;

	let k = key {
		primary = primary,
		sub = [],


@@ 382,22 373,22 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
	let valid = false;
	defer if (!valid) key_finish(&k);

	for (hasnext(g)? && peek(g) == tag::SIGNATURE) {
		p = next(g)?;
	for (hasnext(r)? && peek(r) == tag::SIGNATURE) {
		p = next(r)?;
		// TODO Zero or more revocation signatures
		//println("TODO: REVOCATION SIGNATURE")!;
		pkt_dump(&p);
	};

	if (!hasnext(g)? || peek(g) != tag::USER_ID) {
	if (!hasnext(r)? || peek(r) != tag::USER_ID) {
		return invalid;
	};

	print("# parse user ids and attributes");
	for (hasnext(g)? && (peek(g) == tag::USER_ID || peek(g) == tag::USER_ATTRIBUTE)) {
	for (hasnext(r)? && (peek(r) == tag::USER_ID || peek(r) == tag::USER_ATTRIBUTE)) {

		let mh = multihash_clone(&mh);
		p = next(g)?;
		p = next(r)?;

		if (p.tag == tag::USER_ID) {
			print("USER_ID");


@@ 408,10 399,10 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
			io::copy(&mh, &p)?;
		};

		for (hasnext(g)? && peek(g) == tag::SIGNATURE) {
		for (hasnext(r)? && peek(r) == tag::SIGNATURE) {
			let mh = multihash_clone(&mh);
			print(" parse sig");
			p = next(g)?;
			p = next(r)?;
			let sig = pkg_parsesig(&p)?;
			defer sig_free(&sig);
			multihash_select(&mh, sig.hashalgo)?;


@@ 471,15 462,15 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {

	print("# parse sub keys");
	const subkeytag = if (priv) tag::SECRET_SUBKEY else tag::PUBLIC_SUBKEY;
	for (hasnext(g)? && peek(g) == subkeytag) {
	for (hasnext(r)? && peek(r) == subkeytag) {
		print(" - subkey");
		let mh = multihash_clone(&mh);
		p = next(g)?;
		p = next(r)?;
		let sk = pkt_parse_key(&p, &mh, priv)?;
		append(k.sub, subkey { key = sk });


		p = next(g)?;
		p = next(r)?;

		let sig = pkg_parsesig(&p)?;
		multihash_select(&mh, sig.hashalgo)?;


@@ 489,13 480,15 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
		};
		verify(&sig, &mh, &k)?;

		for (hasnext(g)? && peek(g) == tag::SIGNATURE) {
			p = next(g)?;
		for (hasnext(r)? && peek(r) == tag::SIGNATURE) {
			p = next(r)?;
			pkt_dump(&p);
		};
	};

	print("done, next is", (g.next: packet).tag: u8);
	if (r.next is packet) {
		print("next is", strtag((r.next: packet).tag));
	};
	valid = true;
	return k;
};

M format/openpgp/sig.ha => format/openpgp/sig.ha +1 -1
@@ 255,7 255,7 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
	};
	s.v = readu8(p)?;
	if (s.v != 4) {
		return unsupported_version;
		return s.v: unsupversion;
	};

	s.sigtype = readu8(p)?;

M format/openpgp/types.ha => format/openpgp/types.ha +42 -27
@@ 1,6 1,7 @@
use crypto::sha256;
use crypto::sha512;
use errors;
use fmt;
use hash;
use io;
use time::date;


@@ 14,11 15,11 @@ export type sizetype = enum {
fn strsizetype(s: sizetype) str = {
	switch (s) {
	case sizetype::FIXED =>
		return "fixed";
		return "FIXED";
	case sizetype::INDETERMINATE =>
		return "indeterminate";
		return "INDETERMINATE";
	case sizetype::PARTIAL =>
		return "partial";
		return "PARTIAL";
	case =>
		abort();
	};


@@ 55,9 56,9 @@ export type tag = enum u8 {
	MODIFICATION_DETECTION_CODE = 19,
};

fn u8totag(t: u8) (tag | errors::invalid) = {
fn u8totag(t: u8) (tag | unknowntag) = {
	if (t == 0 || t == 15 || t == 16 || t > 19) {
		return errors::invalid;
		return t: unknowntag;
	};

	return t: tag;


@@ 66,41 67,41 @@ fn u8totag(t: u8) (tag | errors::invalid) = {
fn strtag(t: tag) str = {
	switch (t) {
	case tag::RESERVED =>
		return "reserved";
		return "RESERVED";
	case tag::PUBLIC_KEY_ENCRYPTED_SESSION_KEY =>
		return "public_key_encrypted_session_key";
		return "PUBLIC_KEY_ENCRYPTED_SESSION_KEY";
	case tag::SIGNATURE =>
		return "signature";
		return "SIGNATURE";
	case tag::ENCRYPTED_SESSION_KEY =>
		return "encrypted_session_key";
		return "ENCRYPTED_SESSION_KEY";
	case tag::ONE_PASS_SIGNATURE =>
		return "one_pass_signature";
		return "ONE_PASS_SIGNATURE";
	case tag::SECRET_KEY =>
		return "secret_key";
		return "SECRET_KEY";
	case tag::PUBLIC_KEY =>
		return "public_key";
		return "PUBLIC_KEY";
	case tag::SECRET_SUBKEY =>
		return "secret_subkey";
		return "SECRET_SUBKEY";
	case tag::COMPRESSED_DATA =>
		return "compressed_data";
		return "COMPRESSED_DATA";
	case tag::ENCRYPTED_DATA =>
		return "encrypted_data";
		return "ENCRYPTED_DATA";
	case tag::MARKER =>
		return "marker";
		return "MARKER";
	case tag::LITERAL_DATA =>
		return "literal_data";
		return "LITERAL_DATA";
	case tag::TRUST =>
		return "trust";
		return "TRUST";
	case tag::USER_ID =>
		return "user_id";
		return "USER_ID";
	case tag::PUBLIC_SUBKEY =>
		return "public_subkey";
		return "PUBLIC_SUBKEY";
	case tag::USER_ATTRIBUTE =>
		return "user_attribute";
		return "USER_ATTRIBUTE";
	case tag::ENCRYPTED_AND_INTEGRITY_PROTECTED_DATA =>
		return "encrypted_and_integrity_protected_data";
		return "ENCRYPTED_AND_INTEGRITY_PROTECTED_DATA";
	case tag::MODIFICATION_DETECTION_CODE =>
		return "modification_detection_code";
		return "MODIFICATION_DETECTION_CODE";
	};
};



@@ 166,7 167,9 @@ fn strhashalgo(hashalgo: u8) str = {
	case hashalgo::SHA224 =>
		return "SHA224";
	case =>
		return "unsupported";
		static let idbuf: [16]u8 = [0...];
		idbuf = [0...];
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", hashalgo);
	};
};



@@ 174,6 177,8 @@ type pubkeyalgo = enum u8 {
	RSA = 1,
	RSA_ENCRYPT = 2,
	RSA_SIGN = 3,
	ELGAMAL = 16,
	DSA = 17,
};

fn strpubkeyalgo(pubkeyalgo: u8) str = {


@@ 184,8 189,14 @@ fn strpubkeyalgo(pubkeyalgo: u8) str = {
		return "RSA_ENCRYPT";
	case pubkeyalgo::RSA_SIGN =>
		return "RSA_SIGN";
	case pubkeyalgo::ELGAMAL =>
		return "ELGAMAL";
	case pubkeyalgo::DSA =>
		return "DSA";
	case =>
		return "unsupported";
		static let idbuf: [16]u8 = [0...];
		idbuf = [0...];
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", pubkeyalgo);
	};
};



@@ 222,7 233,9 @@ fn strsenclgo(a: u8) str = {
	case sencalgo::TWOFISH =>
		return "TWOFISH";
	case =>
		return "unsupported";
		static let idbuf: [16]u8 = [0...];
		idbuf = [0...];
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", a);
	};
};



@@ 241,6 254,8 @@ fn strs2ktype(t: u8) str = {
	case s2ktype::ITERATED_AND_SALTED =>
		return "ITERATED_AND_SALTED";
	case =>
		return "unsupported";
		static let idbuf: [16]u8 = [0...];
		idbuf = [0...];
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", t);
	};
};