~sircmpwn/hare-ssh

fbcba2b410a894eb70744c21954c2fce39b54866 — Armin Preiml 9 months ago c4d30bd
format::ssh: add rsa key support
M format/ssh/+test/key.ha => format/ssh/+test/key.ha +14 -0
@@ 16,3 16,17 @@ use strings;
};

const testkey_pub: str = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJhFq0avPAyp4BZrFbnSIj7YUMeiha1CaRfMUuCJYaOG sircmpwn@taiga";

fn copykey(sink: *key, src: *key) void = {
	let pubbuf = bufio::dynamic(io::mode::RDWR);
	defer io::close(&pubbuf)!;
	key_encoderawpub(src, &pubbuf)!;
	io::seek(&pubbuf, 0, io::whence::SET)!;
	key_decoderawpub(sink, &pubbuf)!;

	let privbuf = bufio::dynamic(io::mode::RDWR);
	defer io::close(&privbuf)!;
	key_encoderawpriv(src, &privbuf)!;
	io::seek(&privbuf, 0, io::whence::SET)!;
	key_decoderawpriv(sink, &privbuf)!;
};

M format/ssh/+test/rawkey.ha => format/ssh/+test/rawkey.ha +1 -13
@@ 13,19 13,7 @@ use io;
	let copy = newed25519key();
	defer key_free(copy);

	let pubbuf = bufio::dynamic(io::mode::RDWR);
	defer io::close(&pubbuf)!;
	key_encoderawpub(private, &pubbuf)!;
	io::seek(&pubbuf, 0, io::whence::SET)!;
	key_decoderawpub(copy, &pubbuf)!;

	copykey(copy, private);
	assert(bytes::equal(private.pubkey, copy.pubkey));

	let privbuf = bufio::dynamic(io::mode::RDWR);
	defer io::close(&privbuf)!;
	key_encoderawpriv(private, &privbuf)!;
	io::seek(&privbuf, 0, io::whence::SET)!;
	key_decoderawpriv(copy, &privbuf)!;

	assert(bytes::equal(private.privkey, copy.privkey));
};

A format/ssh/+test/rsakeys.ha => format/ssh/+test/rsakeys.ha +144 -0
@@ 0,0 1,144 @@
use bufio;
use bytes;
use encoding::hex;
use io;
use os;
use strings;

@test fn decodersaprivate() void = {
	const reader = bufio::fixed(strings::toutf8(rsatestkey), io::mode::READ);
	const sshkey = decodesshprivate(&reader)!;
	defer sshprivkey_finish(&sshkey);
	assert(sshkey.cipher == "none");
	assert(sshkey.kdfname == "none");
	assert(len(sshkey.kdf) == 0);
	assert(!isencrypted(&sshkey));

	let priv = decodeprivate(&sshkey)!;

	let pubbuf = bufio::dynamic(io::mode::WRITE);
	defer io::close(&pubbuf)!;
	encode_pubkeystr(&pubbuf, priv)!;

	assert(bytes::equal(strings::toutf8(rsapukeystr), bufio::buffer(&pubbuf)));
};

const rsatestkey: str = `-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----`;

const rsapukeystr: str = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDhgxydoRnldIYHhVCWIWIR3YDK4ivwIhGD8DVlxhxUGMZY2C4y7aCEZSTllnnaBUNlg1bnXO0vglYEL0dL33181fIHu0RDeBgUwQBEFspsCFMcGXZRz0olM8oe8w0xKmpvdx/kVekB6nwdQXC3OS65BXlH6a4U6zj+yFeN/htoZjSxgPb4ioLoYBs698xR39O0Z7+usm2gf98I38Qd+RWtaOhxzvJaDR2Sh3eIBZy2fQgzepTPU4AEFuYDFFRTfbLljIBk5xTfvfkHvqaxHk1l+goYEEWDOQ6boIanC7dYWIJSF1N8/s9VDmHY1LzphyFldOgKq0OYOoNyr9B/k9Q5pgnIHfEGJZWeE1UGKAOyPPI6sIy6TgUZu5gCyo1N0v+KpTSKBhXAw/YbuF7XuSg5jJbbhVYhuv97XyVHtS5scHOrE00kqLNz13/Ys+TflJxI1oALi9HUKbmi76sejad0osfjibUBz/DCKE2OYEI3vhhhKY0B5vm1p8W3zqiFYRs= ape@prateotu`;

const rsamsg: [_]u8 = [
	0x53, 0x53, 0x48, 0x53, 0x49, 0x47, 0x00, 0x00, 0x00, 0x03,
	0x6a, 0x6f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x00, 0x00, 0x00,
	0x20, 0x27, 0x53, 0x91, 0x0d, 0xb8, 0x7e, 0x73, 0x24, 0x39,
	0x77, 0x7f, 0x23, 0xd6, 0x1f, 0xcd, 0x5f, 0x5d, 0x56, 0x4c,
	0x3e, 0x51, 0x42, 0xf0, 0x1d, 0xbd, 0xb4, 0x30, 0x5a, 0xfd,
	0xba, 0xea, 0x85,
];

const rsasig: [_]u8 = [
	0x00, 0x00, 0x00, 0x0c, 0x72, 0x73, 0x61, 0x2d, 0x73, 0x68, 0x61, 0x32,
	0x2d, 0x35, 0x31, 0x32, 0x00, 0x00, 0x01, 0x80, 0x28, 0xe0, 0xd0, 0xf1,
	0x30, 0x3a, 0x76, 0x08, 0xd8, 0x24, 0x6f, 0xb2, 0x8b, 0xdb, 0x79, 0xd4,
	0xb9, 0x9f, 0x60, 0x68, 0x0d, 0x2d, 0x11, 0x48, 0xf8, 0x27, 0x07, 0x2c,
	0x06, 0x8a, 0x32, 0x77, 0x4d, 0xe2, 0x42, 0x01, 0x3f, 0x38, 0x3d, 0xbc,
	0xab, 0xff, 0x38, 0x20, 0x88, 0xbd, 0xdc, 0x31, 0x60, 0x39, 0xc8, 0x7b,
	0x9f, 0xba, 0x78, 0xdb, 0xc6, 0x69, 0x71, 0xb1, 0x4f, 0x8b, 0xf4, 0x24,
	0x62, 0x5b, 0x62, 0x50, 0xde, 0x5c, 0x37, 0x7b, 0x68, 0xb2, 0xff, 0xb7,
	0x5e, 0xd4, 0xe8, 0x75, 0x6b, 0x47, 0xf2, 0x4a, 0xb6, 0x7d, 0x03, 0x0d,
	0x84, 0x04, 0x5f, 0x67, 0x8e, 0xd2, 0xb5, 0xe5, 0xf3, 0xe1, 0x3b, 0x2e,
	0x45, 0x3e, 0x42, 0x0f, 0x64, 0xf9, 0x26, 0x3a, 0xf6, 0x54, 0x1e, 0x1c,
	0x14, 0xf9, 0x0f, 0x90, 0x70, 0x90, 0x40, 0x60, 0x48, 0x84, 0x1c, 0x6e,
	0x7d, 0x5e, 0x15, 0xc5, 0x74, 0xcf, 0xa8, 0x51, 0xcd, 0xc7, 0x39, 0x77,
	0x5f, 0xfc, 0xe0, 0x89, 0x52, 0xda, 0xf4, 0xcd, 0xe4, 0x5a, 0x52, 0x66,
	0x86, 0x3b, 0xf4, 0x8b, 0xf2, 0x26, 0xac, 0x2e, 0x2d, 0xc8, 0x05, 0xba,
	0x99, 0x8d, 0xac, 0x0b, 0xb3, 0x3a, 0xdc, 0x7a, 0x07, 0x86, 0x51, 0x59,
	0x7c, 0x1c, 0xaf, 0xa0, 0xb6, 0x03, 0x43, 0x9e, 0x59, 0x7b, 0xe3, 0xf4,
	0x45, 0xd4, 0x5e, 0x16, 0x56, 0xf5, 0x12, 0xdd, 0x72, 0xc6, 0x9d, 0x7d,
	0x60, 0x0e, 0xcb, 0xad, 0x3c, 0x7e, 0x91, 0x11, 0x45, 0x6b, 0x03, 0xed,
	0x03, 0x96, 0x0d, 0x7d, 0xdd, 0x2a, 0x77, 0x9a, 0xe8, 0x07, 0x15, 0x98,
	0x53, 0x69, 0x65, 0x9d, 0x49, 0x25, 0x1b, 0xbc, 0xa6, 0x73, 0x86, 0xde,
	0xa6, 0xd8, 0x01, 0xcb, 0xf4, 0xe2, 0x37, 0xcd, 0x03, 0xcf, 0xf8, 0xbb,
	0x89, 0x93, 0xc3, 0x1d, 0x8a, 0x7d, 0x58, 0xac, 0xed, 0x93, 0xbf, 0x92,
	0xbe, 0x5a, 0xaf, 0xf1, 0x3e, 0x47, 0x57, 0xce, 0xc4, 0x6b, 0x4c, 0x4c,
	0x33, 0x84, 0xa8, 0xaf, 0xb1, 0xa4, 0xca, 0x8a, 0x58, 0xcf, 0xd4, 0x05,
	0x64, 0x24, 0xf0, 0x28, 0x5a, 0x7e, 0x77, 0x87, 0x9d, 0xa7, 0xc1, 0x7c,
	0x80, 0x03, 0xc6, 0x7c, 0x53, 0x53, 0x9b, 0x5c, 0x9e, 0xa6, 0xed, 0x0c,
	0xe7, 0xca, 0xb9, 0xfb, 0xda, 0x37, 0x7c, 0x62, 0x3c, 0x6a, 0x3b, 0xd1,
	0x82, 0x32, 0x0b, 0x03, 0x95, 0x64, 0x68, 0xa1, 0x8f, 0x23, 0x8d, 0x7a,
	0xea, 0x59, 0x2d, 0x0f, 0xed, 0x85, 0x2e, 0x4b, 0xc8, 0x08, 0x8c, 0x7d,
	0x3b, 0xeb, 0xf4, 0x62, 0xef, 0x0a, 0x35, 0x90, 0xf1, 0x4d, 0x2d, 0x82,
	0xa1, 0x99, 0x32, 0xce, 0xc4, 0x3c, 0x6d, 0x48, 0x33, 0x50, 0xa0, 0x1a,
	0xde, 0xde, 0x57, 0x8c, 0xe6, 0x88, 0xd0, 0x7f, 0xf3, 0xc2, 0xf4, 0x75,
	0xc4, 0xd1, 0x03, 0xe6, 0x26, 0x8a, 0x92, 0xe6,
];

@test fn rsasign() void = {
	const reader = bufio::fixed(strings::toutf8(rsatestkey), io::mode::READ);
	const sshkey = decodesshprivate(&reader)!;
	defer sshprivkey_finish(&sshkey);

	let sigbuf = bufio::fixed(rsasig, io::mode::READ);
	let key = decodeprivate(&sshkey)!;
	verify(&sigbuf, key, rsamsg)!;

	let newsig = bufio::dynamic(io::mode::WRITE);
	defer io::close(&newsig)!;
	sign(&newsig, key, rsamsg)!;

	assert(bytes::equal(rsasig, bufio::buffer(&newsig)));
};

@test fn rsa_rawkeys() void = {
	const reader = bufio::fixed(strings::toutf8(rsatestkey), io::mode::READ);
	const sshkey = decodesshprivate(&reader)!;
	defer sshprivkey_finish(&sshkey);
	const private = decodeprivate(&sshkey)!;
	defer key_free(private);

	let private = private: *rsakey;
	let copy = newrsakey();
	defer key_free(copy);

	copykey(copy, private);
	assert(bytes::equal(private.pubkey, copy.pubkey));
	assert(bytes::equal(private.privkey, copy.privkey));
};

M format/ssh/key.ha => format/ssh/key.ha +2 -0
@@ 72,6 72,8 @@ fn newkey(keytype: const str) (*key | error) = {
	switch (keytype) {
	case "ssh-ed25519" =>
		return newed25519key();
	case "ssh-rsa" =>
		return newrsakey();
	case =>
		return badcipher;
	};

A format/ssh/rsa.ha => format/ssh/rsa.ha +247 -0
@@ 0,0 1,247 @@
use bytes;
use crypto::bigint;
use crypto::sha512;
use crypto::rsa;
use crypto::math::*;
use hash;
use io;
use fmt;
use encoding::hex;
use endian;
use errors;
use os;

// A RSA key pair.
export type rsakey = struct {
	key,
	pubkey: []u8,
	privkey: []u8,

	// crypto::rsa can not yet extract 'd' back from its internal
	// representation. Therefore we need to store it extra.
	d: []u8,
};

const rsa_vtable: key_vtable = key_vtable {
	keytype = "ssh-rsa",
	decodepub = &rsa_decodepub,
	decodepriv = &rsa_decodepriv,
	encodepub = &rsa_encodepub,
	encoderawpub = &rsa_encoderawpub,
	decoderawpub = &rsa_decoderawpub,
	encoderawpriv = &rsa_encoderawpriv,
	decoderawpriv = &rsa_decoderawpriv,
	sign = &rsa_sign,
	verify = &rsa_verify,
	finish = &rsa_finish,
	...
};

fn newrsakey() *rsakey = {
	return alloc(rsakey {
		vtable = &rsa_vtable,
		...
	});
};

fn rsa_decodepub(key: *key, src: io::handle) (void | error) = {
	let key = key: *rsakey;
	let pub = rsa::pubparams {
		...
	};

	pub.e = readumpint(src)?;
	defer free(pub.e);
	pub.n = readumpint(src)?;
	defer free(pub.n);

	assert(len(key.pubkey) == 0, "can not reassign keys");
	key.pubkey = alloc([0...], rsa::PUBKEYSIZE);
	match (rsa::pubkey_init(key.pubkey, pub)) {
	case size =>
		yield;
	case rsa::error =>
		free(key.pubkey);
		return invalid;
	};
};

fn rsa_decodepriv(key: *key, src: io::handle) (void | error) = {
	let key = key: *rsakey;
	rsa_decoderawpub(key, src)?;
	rsa_decoderawpriv(key, src)?;
	key.comment = readstr(src)?;
};

fn alen(x: []u8) size = {
	let l = len(x);
	for (x[0] == 0) {
		x = x[1..];
		l -= 1;
	};
	return l;
};

fn rsa_encodepub(key: *const key, sink: io::handle) (void | io::error) = {
	let key = key: *rsakey;
	let pubkey = rsa::pubkey_params(key.pubkey);

	writeumpint(sink, pubkey.e)?;
	writeumpint(sink, pubkey.n)?;
};

fn rsa_encoderawpub(
	key: *const key,
	sink: io::handle
) (void | io::error) = {
	let key = key: *rsakey;
	let pub = rsa::pubkey_params(key.pubkey);
	writeumpint(sink, pub.n)?;
	writeumpint(sink, pub.e)?;
};

fn rsa_decoderawpub(key: *key, src: io::handle) (void | error) = {
	let key = key: *rsakey;

	let pub = rsa::pubparams {
		...
	};

	pub.n = readumpint(src)?;
	defer free(pub.n);
	pub.e = readumpint(src)?;
	defer free(pub.e);

	assert(len(key.pubkey) == 0, "can not reassign keys");
	key.pubkey = alloc([0...], rsa::PUBKEYSIZE);
	match (rsa::pubkey_init(key.pubkey, pub)) {
	case size =>
		yield;
	case rsa::error =>
		free(key.pubkey);
		return invalid;
	};
};

fn rsa_encoderawpriv(
	key: *const key,
	sink: io::handle
) (void | io::error) = {
	let key = key: *rsakey;
	let params = rsa::privkey_params(key.privkey);
	writeumpint(sink, key.d)?;
	writeumpint(sink, params.iq)?;
	writeumpint(sink, params.p)?;
	writeumpint(sink, params.q)?;
};

fn rsa_decoderawpriv(key: *key, src: io::handle) (void | error) = {
	let key = key: *rsakey;

	let priv = rsa::privparams {
		...
	};

	let d = readumpint(src)?;
	defer bytes::zero(d);
	defer free(d);

	priv.iq = readumpint(src)?;
	defer bytes::zero(priv.iq);
	defer free(priv.iq);

	priv.p = readumpint(src)?;
	defer bytes::zero(priv.p);
	defer free(priv.p);

	priv.q = readumpint(src)?;
	defer bytes::zero(priv.q);
	defer free(priv.q);

	assert(len(key.pubkey) > 0, "pubkey required when decoding privkey");
	let pubp = rsa::pubkey_params(key.pubkey);

	key.privkey = alloc([0...], rsa::PRIVKEYSIZE);
	match (rsa::privkey_initd(key.privkey, priv, d, pubp.n)) {
	case size =>
		yield;
	case rsa::error =>
		free(key.privkey);
		return invalid;
	};

	// copy d to keep preceeding code simple. XXX: better if errdefer
	key.d = alloc(d...);
};

fn rsa_sign(key: *const key, sink: io::handle, msg: []u8) (void | io::error) = {
	let key = key: *rsakey;

	const nsize = rsa::privkey_nsize(key.privkey);
	let sig: []u8 = alloc([0...], nsize);
	defer free(sig);

	let msghash: [sha512::SIZE]u8 = [0...];
	let h = sha512::sha512();
	io::writeall(&h, msg)!;
	hash::sum(&h, msghash);
	hash::close(&h);

	let buf: [rsa::PKCS1_SIGNBUFSIZE]u8 = [0...];
	match (rsa::pkcs1_sign(key.privkey, msghash,
			sig, rsa::pkcs1_hashalgo::SHA512, buf)) {
	case rsa::error =>
		return errors::invalid;
	case =>
		yield;
	};

	writestr(sink, "rsa-sha2-512")?;
	writeslice(sink, sig)?;
};

fn rsa_verify(key: *const key, src: io::handle, msg: []u8) (void | error) = {
	let key = key: *rsakey;

	const sigtype = readstr(src)?;
	defer free(sigtype);

	let algo = rsa_sigalgo(sigtype)?;

	const sig = readslice(src)?;
	defer free(sig);

	let msghash: [sha512::SIZE]u8 = [0...];
	let h = sha512::sha512();
	io::writeall(&h, msg)!;
	hash::sum(&h, msghash);
	hash::close(&h);

	let buf: [rsa::PKCS1_VERIFYBUFSIZE]u8 = [0...];
	match (rsa::pkcs1_verify(key.pubkey, msghash,
			sig, rsa::pkcs1_hashalgo::SHA512, buf)) {
	case rsa::error =>
		return badsig;
	case =>
		return;
	};
};

fn rsa_sigalgo(sigtype: str) (rsa::pkcs1_hashalgo | badcipher) = {
	switch (sigtype) {
	case "rsa-sha2-256" =>
		return rsa::pkcs1_hashalgo::SHA256;
	case "rsa-sha2-512" =>
		return rsa::pkcs1_hashalgo::SHA512;
	case =>
		return badcipher;
	};
};

fn rsa_finish(key: *key) void = {
	let key = key: *rsakey;
	free(key.pubkey);
	bytes::zero(key.privkey);
	free(key.privkey);
};


M format/ssh/util.ha => format/ssh/util.ha +27 -0
@@ 57,3 57,30 @@ fn writeslice(sink: io::handle, sl: []u8) (void | io::error) = {
fn writestr(sink: io::handle, s: str) (void | io::error) = {
	return writeslice(sink, strings::toutf8(s));
};

// writes an unsigned big integer as mpint
fn writeumpint(sink: io::handle, i: []u8) (void | io::error) = {
	assert(len(i) <= types::U32_MAX, "parameter exceeds maximum length");
	let l = len(i);
	if (l > 0 && (i[0] & 0x80) != 0) {
		// need to prepend a 0 otherwise mpint is parsed as negative
		l += 1;
	};
	writeu32(sink, l: u32)?;
	if (l > len(i)) {
		io::writeall(sink, [0])?;
	};
	io::writeall(sink, i)?;
};

// reads a mpint an expects it to be unsigned
fn readumpint(sink: io::handle) ([]u8 | error) = {
	let i = readslice(sink)?;
	if (len(i) == 0) {
		return i;
	};
	if ((i[0] & 0x80) != 0) {
		return invalid;
	};
	return i;
};