~apreiml/hare-openpgp

2ac6876703435725a63444c52ba1a1404d6cf0ac — Armin Preiml 3 months ago b2de9be
hacking towards acceptible pubkey reader support
4 files changed, 123 insertions(+), 30 deletions(-)

M format/openpgp/errors.ha
M format/openpgp/reader.ha
M format/openpgp/rsa.ha
M format/openpgp/sig.ha
M format/openpgp/errors.ha => format/openpgp/errors.ha +17 -4
@@ 1,4 1,5 @@
use io;
use fmt;


export type unsupported = !str;


@@ 7,12 8,19 @@ export type invalid = !void;
export type unexpected = !void;
export type weakhash = !void;

export type error = !(io::error | unsupported | badsig | invalid | unexpected | weakhash);
export type critsigsub = !u8;

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

// 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...];

	match (e) {
	case unsupported =>
		return "Unsupported";
	case let e: unsupported =>
		return e;
	case badsig =>
		return "Signature verification failed";
	case invalid =>


@@ 20,7 28,11 @@ export fn strerror(e: error) str = {
	case unexpected =>
		return "Unexpected data";
	case weakhash =>
		return "Signature on key or data is done using a weak hash function and can not be trusted.";
		return "Signature on key or data is done using a weak hash "
			"function and can not be trusted.";
	case let e: critsigsub =>
		return fmt::bsprint(errbuf, "Unsupported critical signature "
			"subtype:", strsigsubtype(e));
	case let e: io::error =>
		return io::strerror(e);
	};


@@ 29,5 41,6 @@ export fn strerror(e: error) str = {
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/reader.ha => format/openpgp/reader.ha +65 -22
@@ 118,14 118,18 @@ 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,


@@ 136,11 140,14 @@ 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 | io::error) = {
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;


@@ 159,13 166,15 @@ fn parsesz(src: io::handle, p: *packet, t: u8) (void | io::error) = {
		};

		if (io::readall(src, buf[..max])? is io::EOF) {
			return errors::invalid;
			print("wtf");
			return invalid;
		};
		p.hsz += max;
		for (let i = 0z; i < max; i += 1) {
			p.sz <<= 8;
			p.sz += buf[i];
		};
		print("size", p.sz);
		return;
	};



@@ 255,14 264,16 @@ fn pkt_parse_pubkey(p: *packet, mh: *multihash) (*keypair | error) = {
	};

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

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

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

	let key = alloc(newrsakey(d)): *keypair;


@@ 340,8 351,30 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
		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;
	};

	let k = key {
		primary = pkt_parse_key(&p, &mh, priv)?,
		primary = primary,
		sub = [],
		userids = [],
		...


@@ 360,17 393,24 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
		return invalid;
	};

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

		let mh = multihash_clone(&mh);
		p = next(g)?;
		let id = pkt_parseuserid(&p, &mh)?;
		append(k.userids, userid { id = id });

		if (p.tag == tag::USER_ID) {
			print("USER_ID");
			let id = pkt_parseuserid(&p, &mh)?;
			append(k.userids, userid { id = id });
		} else {
			print("USER_ATTRIBUTE (skip)");
			io::copy(&mh, &p)?;
		};

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


@@ 415,15 455,6 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
	//   contain an issuer subpacket for each key, as a way of explicitly
	//   tying those keys to the signature.

	print("parse user attributes");
	for (hasnext(g)? && peek(g) == tag::USER_ATTRIBUTE) {
		print(" has attr");
		p = next(g)?;

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


	// TODO


@@ 438,7 469,7 @@ export fn keys_next(iter: *keyiter) (key | void | error) = {
	// used only for certifying subkeys that are used for encryption and
	// signatures.

	print("parse sub keys");
	print("# parse sub keys");
	const subkeytag = if (priv) tag::SECRET_SUBKEY else tag::PUBLIC_SUBKEY;
	for (hasnext(g)? && peek(g) == subkeytag) {
		print(" - subkey");


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

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


@@ 475,3 506,15 @@ fn pkt_dump(p: *packet) void = {
	io::copy(&buf, p)!;
	hex::dump(os::stdout, memio::buffer(&buf))!;
};

fn pkt_skip(p: *packet) (void | io::error) = {
	return match (io::copy(io::empty, p)) {
	case size =>
		void;
	case let e: io::error =>
		if (e is io::underread) {
			return;
		};
		return e;
	};
};

M format/openpgp/rsa.ha => format/openpgp/rsa.ha +16 -0
@@ 9,6 9,8 @@ use errors;
use hash;
use io;

use debug::*;

export type rsakey = struct {
	key: keypair,
	pub: []u8,


@@ 116,10 118,24 @@ fn rsa_verify(
	};

	let sig = sig[2..];

	let pp = rsa::pubkey_params(k.pub);

	let sigbuf: []u8 = [];
	defer free(sigbuf);

	if (len(sig) < len(pp.n)) {
		// sig must be the same length as n.
		sigbuf = alloc([0...], len(pp.n));
		sigbuf[len(pp.n) - len(sig)..] = sig[..];
		sig = sigbuf;
	};

	const algo = rsa_hashalgo(hashalgo)?;

	let r = rsa::pkcs1_verify(k.pub, hashsum, sig, algo, buf);
	if (r is rsa::error) {
		print("!!2");
		return badsig;
	};
};

M format/openpgp/sig.ha => format/openpgp/sig.ha +25 -4
@@ 74,6 74,8 @@ export type sigtype = enum u8 {
};

export fn strsigtype(st: u8) str = {
	static let idbuf: [16]u8 = [0...];

	switch (st) {
	case sigtype::BINARY =>
		return "BINARY";


@@ 106,7 108,7 @@ export fn strsigtype(st: u8) str = {
	case sigtype::THIRD_PARTY_CONFIRM_SIG =>
		return "THIRD_PARTY_CONFIRM_SIG";
	case =>
		return "UNKNOWN";
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", st);
	};
};



@@ 136,7 138,8 @@ export type subtype = enum u8 {
	ISSUER_FINGERPRINT = 33,
};

export fn strsubtype(st: u8) str = {
export fn strsigsubtype(st: u8) str = {
	let idbuf: [16]u8 = [0...];
	switch (st) {
	case subtype::SIG_CREATION_TIME =>
		return "SIG_CREATION_TIME";


@@ 185,7 188,8 @@ export fn strsubtype(st: u8) str = {
	case subtype::ISSUER_FINGERPRINT =>
		return "ISSUER_FINGERPRINT";
	case =>
		return "UNKNOWN";
		idbuf = [0...];
		return fmt::bsprintf(idbuf, "[UNKNOWN {}]", st);
	};
};



@@ 231,6 235,7 @@ export type sigopts = struct {
	keyfp: []u8,
	keyflags: u8,
	createdat: (void | date::date),
	expiresat: (void | date::date),
};

export fn sig_free(s: *sig) void = {


@@ 243,6 248,7 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
	let s = sig {
		opts = sigopts {
			createdat = void,
			expiresat = void,
			...
		},
		...


@@ 261,6 267,7 @@ fn pkg_parsesig(p: *packet) (sig | error) = {

	s.hsub = io::drain(&io::limitreader(p, hsubsz))!;

	print("parse hsub");
	let hs = memio::fixed(s.hsub);
	for (true) {
		let sz = match (parsesubsz(&hs)?) {


@@ 285,6 292,13 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
			n += io::read(&sub, tbuf) as size;
			let in = time::from_unix(endian::begetu32(tbuf): i64);
			s.opts.createdat = date::from_instant(chrono::UTC, in);
		case subtype::SIG_EXPIRATION_TIME =>
			// TODO duplicate code
			if (subsz != 4) return invalid;
			let tbuf: [4]u8 = [0...];
			n += io::read(&sub, tbuf) as size;
			let in = time::from_unix(endian::begetu32(tbuf): i64);
			s.opts.expiresat = date::from_instant(chrono::UTC, in);
		case subtype::KEYFLAGS =>
			s.opts.keyflags = readu8(&sub)?;
			n += 1;


@@ 294,11 308,16 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
			s.opts.keyfp = io::drain(&sub)?;
			n += 1 + len(s.opts.keyfp);
		case =>
			if (crit) return unsupported_critsub;
			print("CRIT", crit, stype);
			if (crit) {
				hexdump(&sub);
				return stype: critsigsub;
			};
			n += io::copy(io::empty, &sub)?;
		};

		if (n != subsz) {
			print("WTF");
			return invalid;
		};
		//fmt::printfln("  SUB [{}], crit: {}", stype & 0x7f, stype & 0x80 == 0x80)!;


@@ 312,6 331,7 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
	readall(p, s.hprefix)?;

	s.s = io::drain(p)!;
	print("SIGOK");
	return s;
};



@@ 385,6 405,7 @@ export fn verify(s: *sig, h: *hash::hash, k: *key) (void | error) = {
	};

	let sr = memio::fixed(s.s);
	print("Keypair_verify");
	// TODO figure out signing key, if not primary?
	return keypair_verify(k.primary, &sr, sum, s.hashalgo);
};