shithub: riscv

Download patch

ref: 6118f598e0caff4cba0d5838afa98631b1acf0e1
parent: 56970a285fcc90b4c0418b0653ff7fcae377d7c7
author: Jacob Moody <moody@posixcafe.org>
date: Wed Apr 19 10:57:14 EDT 2023

auth/ssh2rsa: convert Unix ssh private keys to Plan 9 format.

--- a/sys/man/8/rsa
+++ b/sys/man/8/rsa
@@ -43,6 +43,11 @@
 .I file
 ]
 .PP
+.B ssh2rsa
+[
+.I file
+]
+.PP
 .B rsa2ssh
 [
 .B -c
@@ -191,6 +196,17 @@
 extracts the key section from a textual ASN.1/DER/PEM key
 into binary ASN.1/DER format and then
 converts it to a Plan 9 RSA key.
+.PP
+.I Ssh2rsa
+reads an RSA private key stored in the binary OpenSSH key format
+and prints a Plan 9 RSA key. The command:
+.IP
+.EX
+auth/pemdecode 'OPENSSH PRIVATE KEY' id_rsa | auth/ssh2rsa
+.EE
+.LP
+converts an id_rsa, as output by Unix ssh-keygen, to a
+Plan 9 RSA key.
 .PP
 .I Rsa2pub
 reads a Plan 9 RSA public or private key,
--- a/sys/src/cmd/auth/mkfile
+++ b/sys/src/cmd/auth/mkfile
@@ -34,6 +34,7 @@
 	rsa2x509\
 	rsafill\
 	rsagen\
+	ssh2rsa\
 	uniq\
 	userpasswd\
 	warning\
--- /dev/null
+++ b/sys/src/cmd/auth/ssh2rsa.c
@@ -1,0 +1,199 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+#define GET4(p)	(u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24
+
+static char *magic = "openssh-key-v1";
+
+static uchar *pubkey;
+static uchar *pubend;
+
+static uchar *privkey;
+static uchar *privend;
+
+static void*
+emalloc(ulong size, int clr)
+{
+	void *p;
+	if((p = mallocz(size, clr)) == nil)
+		sysfatal("emalloc: %r");
+	return p;
+}
+
+static long
+ereadn(int fd, void *buf, long nbytes)
+{
+	long n;
+	if((n = readn(fd, buf, nbytes)) != nbytes)
+		sysfatal("ereadn: %r");
+	return n;
+}
+
+static uchar*
+slurp(int fd, int alloc, u32int *np)
+{
+	u32int n;
+	uchar buf[4], trash[1024];
+	uchar *p;
+
+	ereadn(fd, buf, sizeof buf);
+	n = GET4(buf);
+	if(alloc){
+		*np = n;
+		p = emalloc(n, 0);
+		ereadn(fd, p, n);
+		return p;
+	}
+	if(n >= sizeof trash)
+		sysfatal("key component too large");
+	ereadn(fd, trash, n);
+	return nil;
+}
+
+static u32int
+decode(int fd)
+{
+	uchar buf[4];
+	u32int ret, n;
+
+	/* ciphername */
+	slurp(fd, 0, nil);
+	/* kdfname */
+	slurp(fd, 0, nil);
+	/* kdfoptions */
+	slurp(fd, 0, nil);
+
+	ereadn(fd, buf, 4);
+	ret = GET4(buf);
+
+	pubkey = slurp(fd, 1, &n);
+	pubend = pubkey + n;
+
+	privkey = slurp(fd, 1, &n);
+	privend = privkey + n;
+
+	return ret;
+}
+
+static u32int
+scan(uchar **p, uchar *e)
+{
+	u32int n;
+
+	if(*p + 4 > e)
+		sysfatal("unexpected end of key");
+	n = GET4(*p); *p += 4;
+	if(*p + n > e)
+		sysfatal("unexpected end of key");
+	return n;
+}
+
+static mpint*
+scanmp(uchar **p, uchar *e)
+{
+	mpint *m;
+	u32int n;
+
+	n = scan(p, e);
+	if(n == 0)
+		sysfatal("required key component has zero length");
+	m = betomp(*p, n, nil);
+	if(m == nil)
+		sysfatal("betomp: %r");
+	*p += n;
+	return m;
+}
+
+static RSApriv*
+fill(void)
+{
+	char *rsa = "ssh-rsa";
+	u32int n, a, b;
+	uchar *p, *e;
+	mpint *ek, *mod, *dk, *p0, *q0;
+
+	p = pubkey;
+	e = pubend;
+	if(scan(&p, e) != 7 || memcmp(rsa, p, 7) != 0)
+		sysfatal("not a RSA key");
+	p += 7;
+
+	ek = scanmp(&p, e);
+	mod = scanmp(&p, e);
+
+	p = privkey;
+	e = privend;
+	if(p + 8 >= e)
+		sysfatal("unexpected end of key");
+	a = GET4(p); p += 4;
+	b = GET4(p); p += 4;
+	if(a != b)
+		sysfatal("private key seems encrypted");
+
+	if(scan(&p, e) != 7 || memcmp(rsa, p, 7) != 0)
+		sysfatal("not a RSA key");
+	p += 7;
+
+	/* public components are repeated */
+	n = scan(&p, e); p += n;
+	n = scan(&p, e); p += n;
+	dk = scanmp(&p, e);
+	/* iq */
+	n = scan(&p, e); p += n;
+	p0 = scanmp(&p, e);
+	q0 = scanmp(&p, e);
+
+	return rsafill(mod, ek, dk, p0, q0);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: auth/ssh2rsa [file]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int fd;
+	RSApriv *p;
+	uchar buf[64];
+
+	fd = 0;
+	fmtinstall('B', mpfmt);
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+	switch(argc){
+	default:
+		usage();
+	case 1:
+		fd = open(argv[0], OREAD);
+		if(fd < 0)
+			sysfatal("open: %r");
+	case 0:
+		break;
+	}
+
+	ereadn(fd, buf, strlen(magic)+1);
+	if(memcmp(buf, magic, strlen(magic)+1) != 0)
+		sysfatal("bad magic");
+
+	if(decode(fd) != 1)
+		sysfatal("invalid key");
+	if((p = fill()) == nil)
+		sysfatal("fill: %r");
+
+	print("%s size=%d ek=%B !dk=%B n=%B !p=%B !q=%B !kp=%B !kq=%B !c2=%B\n",
+		"key service=ssh proto=rsa",
+		mpsignif(p->pub.n), p->pub.ek,
+		p->dk, p->pub.n, p->p, p->q,
+		p->kp, p->kq, p->c2);
+
+	exits(nil);
+}