+unsigned char *rfc4716_loadpub(FILE *fp, char **algorithm,
+ int *pub_blob_len, char **commentptr,
+ const char **errorstr)
+{
+ const char *error;
+ char *line, *colon, *value;
+ char *comment = NULL;
+ unsigned char *pubblob = NULL;
+ int pubbloblen, pubblobsize;
+ char base64in[4];
+ unsigned char base64out[3];
+ int base64bytes;
+ int alglen;
+
+ line = chomp(fgetline(fp));
+ if (!line || 0 != strcmp(line, "---- BEGIN SSH2 PUBLIC KEY ----")) {
+ error = "invalid begin line in SSH-2 public key file";
+ goto error;
+ }
+ sfree(line); line = NULL;
+
+ while (1) {
+ line = chomp(fgetline(fp));
+ if (!line) {
+ error = "truncated SSH-2 public key file";
+ goto error;
+ }
+ colon = strstr(line, ": ");
+ if (!colon)
+ break;
+ *colon = '\0';
+ value = colon + 2;
+
+ if (!strcmp(line, "Comment")) {
+ char *p, *q;
+
+ /* Remove containing double quotes, if present */
+ p = value;
+ if (*p == '"' && p[strlen(p)-1] == '"') {
+ p[strlen(p)-1] = '\0';
+ p++;
+ }
+
+ /* Remove \-escaping, not in RFC4716 but seen in the wild
+ * in practice. */
+ for (q = line; *p; p++) {
+ if (*p == '\\' && p[1])
+ p++;
+ *q++ = *p;
+ }
+
+ *q = '\0';
+ sfree(comment); /* *just* in case of multiple Comment headers */
+ comment = dupstr(line);
+ } else if (!strcmp(line, "Subject") ||
+ !strncmp(line, "x-", 2)) {
+ /* Headers we recognise and ignore. Do nothing. */
+ } else {
+ error = "unrecognised header in SSH-2 public key file";
+ goto error;
+ }
+
+ sfree(line); line = NULL;
+ }
+
+ /*
+ * Now line contains the initial line of base64 data. Loop round
+ * while it still does contain base64.
+ */
+ pubblobsize = 4096;
+ pubblob = snewn(pubblobsize, unsigned char);
+ pubbloblen = 0;
+ base64bytes = 0;
+ while (line && line[0] != '-') {
+ char *p;
+ for (p = line; *p; p++) {
+ base64in[base64bytes++] = *p;
+ if (base64bytes == 4) {
+ int n = base64_decode_atom(base64in, base64out);
+ if (pubbloblen + n > pubblobsize) {
+ pubblobsize = (pubbloblen + n) * 5 / 4 + 1024;
+ pubblob = sresize(pubblob, pubblobsize, unsigned char);
+ }
+ memcpy(pubblob + pubbloblen, base64out, n);
+ pubbloblen += n;
+ base64bytes = 0;
+ }
+ }
+ sfree(line); line = NULL;
+ line = chomp(fgetline(fp));
+ }
+
+ /*
+ * Finally, check the END line makes sense.
+ */
+ if (!line || 0 != strcmp(line, "---- END SSH2 PUBLIC KEY ----")) {
+ error = "invalid end line in SSH-2 public key file";
+ goto error;
+ }
+ sfree(line); line = NULL;
+
+ /*
+ * OK, we now have a public blob and optionally a comment. We must
+ * return the key algorithm string too, so look for that at the
+ * start of the public blob.
+ */
+ if (pubbloblen < 4) {
+ error = "not enough data in SSH-2 public key file";
+ goto error;
+ }
+ alglen = toint(GET_32BIT(pubblob));
+ if (alglen < 0 || alglen > pubbloblen-4) {
+ error = "invalid algorithm prefix in SSH-2 public key file";
+ goto error;
+ }
+ if (algorithm)
+ *algorithm = dupprintf("%.*s", alglen, pubblob+4);
+ if (pub_blob_len)
+ *pub_blob_len = pubbloblen;
+ if (commentptr)
+ *commentptr = comment;
+ else
+ sfree(comment);
+ return pubblob;
+
+ error:
+ sfree(line);
+ sfree(comment);
+ sfree(pubblob);
+ if (errorstr)
+ *errorstr = error;
+ return NULL;
+}
+
+unsigned char *openssh_loadpub(FILE *fp, char **algorithm,
+ int *pub_blob_len, char **commentptr,
+ const char **errorstr)
+{
+ const char *error;
+ char *line, *base64;
+ char *comment = NULL;
+ unsigned char *pubblob = NULL;
+ int pubbloblen, pubblobsize;
+ int alglen;
+
+ line = chomp(fgetline(fp));
+
+ base64 = strchr(line, ' ');
+ if (!base64) {
+ error = "no key blob in OpenSSH public key file";
+ goto error;
+ }
+ *base64++ = '\0';
+
+ comment = strchr(base64, ' ');
+ if (comment) {
+ *comment++ = '\0';
+ comment = dupstr(comment);
+ }
+
+ pubblobsize = strlen(base64) / 4 * 3;
+ pubblob = snewn(pubblobsize, unsigned char);
+ pubbloblen = 0;
+
+ while (!memchr(base64, '\0', 4)) {
+ assert(pubbloblen + 3 <= pubblobsize);
+ pubbloblen += base64_decode_atom(base64, pubblob + pubbloblen);
+ base64 += 4;
+ }
+ if (*base64) {
+ error = "invalid length for base64 data in OpenSSH public key file";
+ goto error;
+ }
+
+ /*
+ * Sanity check: the first word on the line should be the key
+ * algorithm, and should match the encoded string at the start of
+ * the public blob.
+ */
+ alglen = strlen(line);
+ if (pubbloblen < alglen + 4 ||
+ GET_32BIT(pubblob) != alglen ||
+ 0 != memcmp(pubblob + 4, line, alglen)) {
+ error = "key algorithms do not match in OpenSSH public key file";
+ goto error;
+ }
+
+ /*
+ * Done.
+ */
+ if (algorithm)
+ *algorithm = dupstr(line);
+ if (pub_blob_len)
+ *pub_blob_len = pubbloblen;
+ if (commentptr)
+ *commentptr = comment;
+ else
+ sfree(comment);
+ sfree(line);
+ return pubblob;
+
+ error:
+ sfree(line);
+ sfree(comment);
+ sfree(pubblob);
+ if (errorstr)
+ *errorstr = error;
+ return NULL;
+}
+