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);
+}