From 06bf5fef94b962962b3b0895509246e05bb9cab9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Sun, 20 Nov 2005 18:10:36 +0100
Subject: [PATCH] New program.

Rev: src/nettle/tools/pkcs1-conv.c:1.1
---
 tools/pkcs1-conv.c | 574 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 574 insertions(+)
 create mode 100644 tools/pkcs1-conv.c

diff --git a/tools/pkcs1-conv.c b/tools/pkcs1-conv.c
new file mode 100644
index 00000000..0722b6b9
--- /dev/null
+++ b/tools/pkcs1-conv.c
@@ -0,0 +1,574 @@
+/* pkcs1-conv.c
+ *
+ * Converting pkcs#1 keys to sexp format. */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2005 Niels Möller
+ *  
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ * 
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asn1.h"
+#include "base64.h"
+#include "buffer.h"
+#include "rsa.h"
+
+#include "getopt.h"
+#include "misc.h"
+
+enum object_type
+  {
+    RSA_PRIVATE_KEY = 0x200,
+    RSA_PUBLIC_KEY,
+    GENERAL_PUBLIC_KEY,
+  };
+
+static int
+write_file(struct nettle_buffer *buffer, FILE *f)
+{
+  size_t res = fwrite(buffer->contents, 1, buffer->size, f);
+  if (res < buffer->size)
+    {
+      werror("Write failed: %s.\n", strerror(errno));
+      return 0;
+    }
+  else
+    return 1;
+}
+
+/* Return 1 on success, 0 on error, -1 on eof */
+static int
+read_line(struct nettle_buffer *buffer, FILE *f)
+{
+  int c;
+  
+  while ((c = getc(f)) != EOF)
+    {
+      if (!NETTLE_BUFFER_PUTC(buffer, c))
+	return 0;
+
+      if (c == '\n')
+	return 1;
+    }
+  if (ferror(f))
+    {
+      werror("Read failed: %s\n", strerror(errno));
+      return 0;
+    }
+  
+  else 
+    return -1;
+}
+
+static int
+read_file(struct nettle_buffer *buffer, FILE *f)
+{
+  int c;
+  
+  while ((c = getc(f)) != EOF)
+    if (!NETTLE_BUFFER_PUTC(buffer, c))
+      return 0;
+
+  if (ferror(f))
+    {
+      werror("Read failed: %s\n", strerror(errno));
+      return 0;
+    }
+  else
+    return 1;
+}
+
+static const uint8_t
+pem_start_pattern[11] = "-----BEGIN ";
+
+static const uint8_t
+pem_end_pattern[9] = "-----END ";
+
+static const uint8_t
+pem_trailer_pattern[5] = "-----";
+
+static const char
+pem_ws[33] = {
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 1, 1, 1, 1, 1, 0, 0, /* \t, \n, \v, \f, \r */
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  1 /* SPC */
+};
+
+#define PEM_IS_SPACE(c) ((c) < sizeof(pem_ws) && pem_ws[(c)]) 
+
+/* Returns 1 on match, otherwise 0. */ 
+static int
+match_pem_start(unsigned length, const uint8_t *line,
+		unsigned *marker_start,
+		unsigned *marker_length)
+{
+  while (length > 0 && PEM_IS_SPACE(line[length - 1]))
+    length--;
+
+  if (length > (sizeof(pem_start_pattern) + sizeof(pem_trailer_pattern))
+      && memcmp(line, pem_start_pattern, sizeof(pem_start_pattern)) == 0
+      && memcmp(line + length - sizeof(pem_trailer_pattern),
+		pem_trailer_pattern, sizeof(pem_trailer_pattern)) == 0)
+    {
+      *marker_start = 11;
+      *marker_length = length - (sizeof(pem_start_pattern) + sizeof(pem_trailer_pattern));
+
+      return 1;
+    }
+  else
+    return 0;
+}
+
+/* Returns 1 on match, -1 if the line is of the right form except for
+   the marker, otherwise 0. */ 
+static int
+match_pem_end(unsigned length, const uint8_t *line,
+	      unsigned marker_length,
+	      const uint8_t *marker)
+{
+  while (length > 0 && PEM_IS_SPACE(line[length - 1]))
+    length--;
+
+  if (length > (sizeof(pem_end_pattern) + sizeof(pem_trailer_pattern))
+      && memcmp(line, pem_end_pattern, sizeof(pem_end_pattern)) == 0
+      && memcmp(line + length - sizeof(pem_trailer_pattern),
+		pem_trailer_pattern, sizeof(pem_trailer_pattern)) == 0)
+    {
+      /* Right form. Check marker */
+      if (length == marker_length + (sizeof(pem_end_pattern) + sizeof(pem_trailer_pattern))
+	  && memcmp(line + sizeof(pem_end_pattern), marker, marker_length) == 0)
+	return 1;
+      else
+	return -1;
+    }
+  else
+    return 0;  
+}
+
+struct pem_info
+{
+  /* The FOO part in "-----BEGIN FOO-----" */
+  unsigned marker_start;
+  unsigned marker_length;
+  unsigned data_start;
+  unsigned data_length;
+};
+
+static int
+read_pem(struct nettle_buffer *buffer, FILE *f,
+	 struct pem_info *info)
+{  
+  /* Find start line */
+  for (;;)
+    {
+      nettle_buffer_reset(buffer);
+
+      int res = read_line(buffer, f);
+      if (res != 1)
+	return res;
+
+      if (match_pem_start(buffer->size, buffer->contents,
+			  &info->marker_start, &info->marker_length))
+	break;
+    }
+
+  /* NUL-terminate the marker. Don't care to check for embedded NULs. */
+  buffer->contents[info->marker_start + info->marker_length] = 0;
+
+  info->data_start = buffer->size;
+
+  for (;;)
+    {
+      unsigned line_start = buffer->size;
+
+      if (read_line(buffer, f) != 1)
+	return 0;
+
+      switch (match_pem_end(buffer->size - line_start,
+			    buffer->contents + line_start,
+			    info->marker_length,
+			    buffer->contents + info->marker_start))
+	{
+	case 0:
+	  break;
+	case -1:
+	  werror("PEM END line doesn't match BEGIN.\n");
+	  return 0;
+	case 1:
+	  /* Return base 64 data; let caller do the decoding */ 
+	  info->data_length = line_start - info->data_start;
+	  return 1;
+	}
+    }
+}
+
+static int
+decode_base64(struct nettle_buffer *buffer,
+	      unsigned start, unsigned *length)
+{
+  struct base64_decode_ctx ctx;
+  
+  base64_decode_init(&ctx);
+
+  /* Decode in place */
+  if (base64_decode_update(&ctx,
+			   length, buffer->contents + start,
+			   *length, buffer->contents + start)
+      && base64_decode_final(&ctx))
+    return 1;
+  
+  else
+    {
+      werror("Invalid base64 date.\n");
+      return 0;
+    }
+}
+
+static int
+convert_rsa_public_key(struct nettle_buffer *buffer, unsigned length, const uint8_t *data)
+{
+  struct rsa_public_key pub;
+  int res;
+  
+  rsa_public_key_init(&pub);
+
+  if (rsa_keypair_from_der(&pub, NULL, 0,
+			   length, data))
+    {
+      /* Reuses the buffer */
+      nettle_buffer_reset(buffer);
+      res = rsa_keypair_to_sexp(buffer, NULL, &pub, NULL);
+    }
+  else
+    {
+      werror("Invalid PKCS#1 public key.\n");
+      res = 0;
+    }
+  rsa_public_key_clear(&pub);
+  return res;
+}
+
+static int
+convert_rsa_private_key(struct nettle_buffer *buffer, unsigned length, const uint8_t *data)
+{
+  struct rsa_public_key pub;
+  struct rsa_private_key priv;
+  int res;
+  
+  rsa_public_key_init(&pub);
+  rsa_private_key_init(&priv);
+
+  if (rsa_keypair_from_der(&pub, &priv, 0,
+			   length, data))
+    {
+      /* Reuses the buffer */
+      nettle_buffer_reset(buffer);
+      res = rsa_keypair_to_sexp(buffer, NULL, &pub, &priv);
+    }
+  else
+    {
+      werror("Invalid PKCS#1 private key.\n");
+      res = 0;
+    }
+  rsa_public_key_clear(&pub);
+  rsa_private_key_clear(&priv);
+
+  return res;
+}
+
+/* Returns 1 on success, 0 on error, and -1 for unsupported algorithms. */
+static int
+convert_public_key(struct nettle_buffer *buffer, unsigned length, const uint8_t *data)
+{
+  /* SubjectPublicKeyInfo ::= SEQUENCE {
+         algorithm		AlgorithmIdentifier,
+	 subjectPublicKey 	BIT STRING
+     }
+
+     AlgorithmIdentifier ::= SEQUENCE {
+         algorithm  	OBJECT IDENTIFIER,
+	 parameters 	OPTIONAL
+     }
+  */
+  struct asn1_der_iterator i;
+  struct asn1_der_iterator j;
+  int res = 0;
+
+  if (asn1_der_iterator_first(&i, length, data) == ASN1_ITERATOR_CONSTRUCTED
+      && i.type == ASN1_SEQUENCE
+      && asn1_der_decode_constructed_last(&i) == ASN1_ITERATOR_CONSTRUCTED
+      && i.type == ASN1_SEQUENCE
+
+      /* Use the j iterator to parse the algorithm identifier */
+      && asn1_der_decode_constructed(&i, &j) == ASN1_ITERATOR_PRIMITIVE
+      && j.type == ASN1_IDENTIFIER
+      && asn1_der_iterator_next(&i) == ASN1_ITERATOR_PRIMITIVE
+      && i.type == ASN1_BITSTRING
+
+      /* Use i to parse the object wrapped in the bit string. For all
+	 currently supported key types, it is a sequence. */
+      && asn1_der_decode_bitstring_last(&i) == ASN1_ITERATOR_CONSTRUCTED)
+    {
+      /* pkcs-1 {
+	     iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)
+	     modules(0) pkcs-1(1)
+	 }
+
+	 --
+	 -- When rsaEncryption is used in an AlgorithmIdentifier the
+	 -- parameters MUST be present and MUST be NULL.
+	 --
+	 rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }
+      */
+      static const uint8_t id_rsaEncryption[9] =
+	{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
+      
+      switch (j.length)
+	{
+	unknown:
+	default:
+	  werror("SubjectPublicKeyInfo: Unsupported algorithm.\n");
+	  res = -1;
+	  break;
+	  
+	case 9:
+	  if (memcmp(j.data, id_rsaEncryption, 9) == 0)
+	    {
+	      if (asn1_der_iterator_next(&j) == ASN1_ITERATOR_PRIMITIVE
+		  && j.type == ASN1_NULL
+		  && j.length == 0
+		  && asn1_der_iterator_next(&j) == ASN1_ITERATOR_END)
+		{
+		  struct rsa_public_key pub;
+
+		  rsa_public_key_init(&pub);
+
+		  if (rsa_public_key_from_der_iterator(&pub, 0, &i))
+		    {
+		      nettle_buffer_reset(buffer);
+		      res = rsa_keypair_to_sexp(buffer, NULL, &pub, NULL) > 0;
+		    }
+		}
+	      if (!res)
+		werror("SubjectPublicKeyInfo: Invalid RSA key.\n");
+	      break;
+	    }
+	  else goto unknown;
+	}
+    }
+  else
+    werror("SubjectPublicKeyInfo: Invalid object.\n");
+  
+  return res;
+}
+
+/* NOTE: Destroys contents of buffer */
+/* Returns 1 on success, 0 on error, and -1 for unsupported algorithms. */
+static int
+convert_type(struct nettle_buffer *buffer,
+	     enum object_type type,
+	     unsigned length, const uint8_t *data)
+{
+  int res;
+  
+  switch(type)
+    {
+    default:
+      abort();
+
+    case GENERAL_PUBLIC_KEY:
+      res = convert_public_key(buffer, length, data);
+      break;
+
+    case RSA_PUBLIC_KEY:
+      res = convert_rsa_public_key(buffer, length, data);
+      break;
+
+    case RSA_PRIVATE_KEY:
+      res = convert_rsa_private_key(buffer, length, data);
+      break;
+    }
+
+  if (res > 0)
+    res = write_file(buffer, stdout);
+
+  return res;
+}
+
+static int
+convert_file(struct nettle_buffer *buffer,
+	     FILE *f,
+	     enum object_type type,
+	     int base64)
+{
+  if (type)
+    {
+      read_file(buffer, f);
+      if (base64 && !decode_base64(buffer, 0, &buffer->size))
+	return 0;
+      
+      if (convert_type(buffer, type,
+		       buffer->size, buffer->contents) != 1)
+	return 0;
+
+      return 1;
+    }
+  else
+    {
+      /* PEM processing */
+      for (;;)
+	{
+	  struct pem_info info;
+	  const uint8_t *marker;
+	  
+	  nettle_buffer_reset(buffer);
+	  switch (read_pem(buffer, f, &info))
+	    {
+	    default:
+	      return 0;
+	    case 1:
+	      break;
+	    case -1:
+	      /* EOF */
+	      return 1;
+	    }
+
+	  if (!decode_base64(buffer, info.data_start, &info.data_length))
+	    return 0;
+
+	  marker = buffer->contents + info.marker_start;
+
+	  type = 0;
+	  switch (info.marker_length)
+	    {
+	    case 10:
+	      if (memcmp(marker, "PUBLIC KEY", 10) == 0)
+		{
+		  type = GENERAL_PUBLIC_KEY;
+		  break;
+		}
+	    case 14:
+	      if (memcmp(marker, "RSA PUBLIC KEY", 14) == 0)
+		{
+		  type = RSA_PUBLIC_KEY;
+		  break;
+		}
+
+	    case 15:
+	      if (memcmp(marker, "RSA PRIVATE KEY", 15) == 0)
+		{
+		  type = RSA_PRIVATE_KEY;
+		  break;
+		}
+	    }
+	  
+	  if (!type)
+	    werror("Ignoring unsupported object type `%s'.\n", marker);
+
+	  else if (convert_type(buffer, type,
+				info.data_length,
+				buffer->contents + info.data_start) != 1)
+	    return 0;
+	}
+    }
+}
+
+
+int
+main(int argc, char **argv)
+{
+  struct nettle_buffer buffer;
+  enum object_type type = 0;
+  int base64 = 0;
+  int c;
+  
+  static const struct option options[] =
+    {
+      /* Name, args, flag, val */
+      { "help", no_argument, NULL, '?' },
+      { "version", no_argument, NULL, 'V' },
+      { "private-rsa-key", no_argument, NULL, RSA_PRIVATE_KEY },
+      { "public-rsa-key", no_argument, NULL, RSA_PUBLIC_KEY },
+      { "public-key-info", no_argument, NULL, GENERAL_PUBLIC_KEY },
+      { "base-64", no_argument, NULL, 'b' },
+      { NULL, 0, NULL, 0 }
+    };
+
+  while ( (c = getopt_long(argc, argv, "V?b", options, NULL)) != -1)
+    {
+      switch (c)
+	{
+	default:
+	  abort();
+
+	case 'b':
+	  base64 = 1;
+	  break;
+
+	case RSA_PRIVATE_KEY:
+	case RSA_PUBLIC_KEY:
+	case GENERAL_PUBLIC_KEY:
+	  type = c;
+	  break;
+
+	case '?':
+	  printf("FIXME: Usage information.\n");
+	  return EXIT_SUCCESS;
+
+	case 'V':
+	  printf("pkcs1-conv (" PACKAGE_STRING ")\n");
+	  exit (EXIT_SUCCESS);
+	}
+    }
+
+  nettle_buffer_init_realloc(&buffer, NULL, nettle_xrealloc);  
+
+  if (optind == argc)
+    {
+      if (!convert_file(&buffer, stdin, type, base64))
+	return EXIT_FAILURE;
+    }
+  else
+    {
+      int i;
+      const char *mode = (type || base64) ? "r" : "rb";
+      
+      for (i = optind; i < argc; i++)
+	{
+	  FILE *f = fopen(argv[i], mode);
+	  if (!f)
+	    die("Failed to open `%s': %s.\n", argv[i], strerror(errno));
+
+	  if (!convert_file(&buffer, f, type, base64))
+	    return EXIT_FAILURE;
+
+	  fclose(f);
+	}
+    }
+  return EXIT_SUCCESS;
+}
-- 
GitLab