From 970a4919ee6c0d62be9204f68259e68d583488df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Sun, 6 Oct 2002 20:38:54 +0200
Subject: [PATCH] * pgp-encode.c: New file. Functions for writing openpgp data
 packets.

* pgp.h: New file, with pgp related declarations.

Rev: src/nettle/pgp-encode.c:1.1
Rev: src/nettle/pgp.h:1.1
---
 pgp-encode.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++++++
 pgp.h        | 173 +++++++++++++++++++++++
 2 files changed, 566 insertions(+)
 create mode 100644 pgp-encode.c
 create mode 100644 pgp.h

diff --git a/pgp-encode.c b/pgp-encode.c
new file mode 100644
index 00000000..9471d07e
--- /dev/null
+++ b/pgp-encode.c
@@ -0,0 +1,393 @@
+/* pgp.c
+ *
+ * PGP related functions.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2001, 2002 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.
+ */
+
+#include "pgp.h"
+
+#include "base64.h"
+#include "buffer.h"
+#include "macros.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+pgp_put_uint32(struct nettle_buffer *buffer, uint32_t i)
+{
+  uint8_t *p = nettle_buffer_space(buffer, 4);
+  if (!p)
+    return 0;
+  
+  WRITE_UINT32(p, i);
+  return 1;
+}
+
+int
+pgp_put_uint16(struct nettle_buffer *buffer, unsigned i)
+{
+  uint8_t *p = nettle_buffer_space(buffer, 2);
+  if (!p)
+    return 0;
+  
+  WRITE_UINT16(p, i);
+  return 1;
+}
+
+int
+pgp_put_mpi(struct nettle_buffer *buffer, mpz_t x)
+{
+  unsigned bits = mpz_sizeinbase(x, 2);
+  unsigned octets = (bits + 7) / 8;
+
+  uint8_t *p;
+
+  /* FIXME: What's the correct representation of zero? */
+  if (!pgp_put_uint16(buffer, bits))
+    return 0;
+  
+  p = nettle_buffer_space(buffer, octets);
+
+  if (!p)
+    return 0;
+  
+  nettle_mpz_set_str_256(x, octets, p);
+
+  return 1;
+}
+
+int
+pgp_put_string(struct nettle_buffer *buffer,
+	       unsigned length,
+	       const uint8_t *s)
+{
+  return nettle_buffer_write(buffer, length, s);
+}
+
+#if 0
+static unsigned
+length_field(unsigned length)
+{
+  if (length < PGP_LENGTH_TWO_OCTET)
+    return 1;
+  else if (length < PGP_LENGTH_FOUR_OCTETS)
+    return 2;
+  else return 4;
+}
+#endif
+
+/*   bodyLen = ((1st_octet - 192) << 8) + (2nd_octet) + 192
+ *   ==> bodyLen - 192 + 192 << 8 = (1st_octet << 8) + (2nd_octet) 
+ */
+
+#define LENGTH_TWO_OFFSET (192 * 255)
+
+int
+pgp_put_length(struct nettle_buffer *buffer,
+	       unsigned length)
+{
+  if (length < PGP_LENGTH_TWO_OCTETS)
+    return NETTLE_BUFFER_PUTC(buffer, length);
+
+  else if (length < PGP_LENGTH_FOUR_OCTETS)
+    return pgp_put_uint16(buffer, length + LENGTH_TWO_OFFSET);
+  else
+    return NETTLE_BUFFER_PUTC(buffer, 0xff) && pgp_put_uint32(buffer, length);
+}
+
+/* Uses the "new" packet format */
+int
+pgp_put_header(struct nettle_buffer *buffer,
+	       unsigned tag, unsigned length)
+{
+  assert(tag < 0x40);
+
+  return (NETTLE_BUFFER_PUTC(buffer, 0xC0 | tag)
+	  && pgp_put_length(buffer, length));  
+}
+
+/* FIXME: Should we abort or return error if the length and the field
+ * size don't match? */
+void
+pgp_put_header_length(struct nettle_buffer *buffer,
+		      /* start of the header */
+		      unsigned start,
+		      unsigned field_size)
+{
+  unsigned length;
+  switch (field_size)
+    {
+    case 1:
+      length = buffer->size - (start + 2);
+      assert(length < PGP_LENGTH_TWO_OCTETS);
+      buffer->contents[start + 1] = length;
+      break;
+    case 2:
+      length = buffer->size - (start + 3);
+      assert(length < PGP_LENGTH_FOUR_OCTETS
+	     && length >= PGP_LENGTH_TWO_OCTETS);
+      WRITE_UINT16(buffer->contents + start + 1, length + LENGTH_TWO_OFFSET);
+      break;
+    case 4:
+      length = buffer->size - (start + 5);
+      WRITE_UINT32(buffer->contents + start + 2, length);
+      break;
+    default:
+      abort();
+    }
+}
+
+int
+pgp_put_userid(struct nettle_buffer *buffer,
+	       unsigned length,
+	       const uint8_t *name)
+{
+  return (pgp_put_header(buffer, PGP_TAG_USERID, length)
+	  && pgp_put_string(buffer, length, name));
+}
+
+unsigned
+pgp_sub_packet_start(struct nettle_buffer *buffer)
+{
+  return nettle_buffer_space(buffer, 2) ? buffer->size : 0;
+}
+
+int
+pgp_put_sub_packet(struct nettle_buffer *buffer,
+		   unsigned type,
+		   unsigned length,
+		   const uint8_t *data)
+{
+  return (pgp_put_length(buffer, length + 1)
+	  && NETTLE_BUFFER_PUTC(buffer, type)
+	  && pgp_put_string(buffer, length, data));
+}
+
+void
+pgp_sub_packet_end(struct nettle_buffer *buffer, unsigned start)
+{
+  unsigned length;
+  
+  assert(start >= 2);
+  assert(start <= buffer->size);
+
+  length = buffer->size - start;
+  WRITE_UINT32(buffer->contents + start - 2, length);
+}
+
+#if WITH_PUBLIC_KEY
+int
+pgp_put_public_rsa_key(struct nettle_buffer *,
+		       struct rsa_public_key *key,
+		       time_t timestamp)
+{
+  /* Public key packet, version 4 */
+  unsigned start;
+  unsigned length;
+
+  /* Size of packet is 16 + the size of e and n */
+  length = (4 * 4
+	  + nettle_mpz_sizeinbase_256(pub->n)
+	  + nettle_mpz_sizeinbase_256(pub->e));
+
+  if (!pgp_put_header(buffer, PGP_TAG_PUBLIC_KEY, size))
+    return 0;
+
+  start = buffer->size;
+  
+  if (! (pgp_put_header(buffer, PGP_TAG_PUBLIC_KEY,
+			/* Assume that we need two octets */
+			PGP_LENGTH_TWO_OCTETS)
+	  && pgp_put_uint32(buffer, 4)        /* Version */  
+	  && pgp_put_uint32(buffer, now)      /* Time stamp */
+	  && pgp_put_uint32(buffer, PGP_RSA)  /* Algorithm */
+	  && pgp_put_mpi(buffer, pub->n)
+	  && pgp_put_mpi(buffer, pub->e)) )
+    return 0;
+
+  assert(buffer->size == start + length);
+
+  return 1;
+}
+
+int
+pgp_put_rsa_sha1_signature(struct nettle_buffer *buffer,
+			   struct rsa_private_key *key,
+			   const uint8_t *keyid,
+			   unsigned type,
+			   struct sha1_ctx *hash)
+{
+  unsigned signature_start = buffer->size;
+  unsigned hash_end;
+  uint8_t trailer[6];
+  uint8_t digest16[2];
+  mpz_t s;
+  
+  /* Signature packet. The packet could reasonably be both smaller and
+   * larger than 192, so for simplicity we use the 4 octet header
+   * form. */
+
+  if (! (pgp_put_header(buffer, PGP_TAG_SIGNATURE, PGP_LENGTH_FOUR_OCTETS)
+	 && NETTLE_BUFFER_PUTC(buffer, 4)  /* Version */
+	 && NETTLE_BUFFER_PUTC(buffer, type)
+	 /* Could also be PGP_RSA_SIGN */
+	 && NETTLE_BUFFER_PUTC(buffer, PGP_RSA)
+	 && NETTLE_BUFFER_PUTC(buffer, PGP_SHA1)
+	 && pgp_put_uint16(0)))  /* Hashed subpacket length */
+    return 0;
+
+  hash_end = buffer->size;
+
+  sha1_update(hash,
+	      hash_end - signature_start,
+	      buffer->contents + signature_start);
+
+  trailer[0] = 4; trailer[1] = 0xff;
+  WRITE_UINT32(trailer + 2, buffer->size - signature_start);
+
+  sha1_update(hash, sizeof(trailer), trailer);
+
+  {
+    sha1_ctx hcopy = *hash;
+    uint8_t *p = nettle_buffer_space(2);
+    if (!p)
+      return 0;
+    
+    sha1_digest(&hcopy, 2, p);
+  }
+
+  /* One "sub-packet" field with the issuer keyid */
+  sub_packet_start = pgp_sub_packet_start(buffer);
+  if (!sub_packet_start)
+    return 0;
+
+  if (pgp_put_sub_packet(buffer, PGP_SUBPACKET_ISSUER, 8, keyid)
+      && pgp_sub_packet_end(buffer, sub_packet_start))
+    return 0;
+    
+  mpz_init(s);
+  rsa_sha1_sign(key, hash, s);
+
+  if (!pgp_put_mpi(buffer, s))
+    {
+      mpz_clear(s);
+      return 0;
+    }
+
+  mpz_clear(s);
+  pgp_put_header_length(buffer, signature_start, 4);
+
+  return 1;
+}
+#endif /* WITH_PUBLIC_KEY */
+
+#define CRC24_INIT 0x0b704ceL
+#define CRC24_POLY 0x1864cfbL
+
+uint32_t
+pgp_crc24(unsigned length, const uint8_t *data)
+{
+  uint32_t crc = CRC24_INIT;
+
+  unsigned i;
+  for (i = 0; i<length; i++)
+    {
+      unsigned j;
+      crc ^= ((unsigned) (data[i]) << 16);
+      for (j = 0; j<8; j++)
+	{
+	  crc <<= 1;
+	  if (crc & 0x1000000)
+	    crc ^= CRC24_POLY;
+	}
+    }
+  assert(crc < 0x1000000);
+  return crc;
+}
+
+
+#define WRITE(buffer, s) (nettle_buffer_write(buffer, strlen((s)), (s)))
+
+/* Base 64 groups data per line */
+#define GROUPS_PER_LINE 15
+#define BINARY_PER_LINE (GROUPS_PER_LINE * BASE64_BINARY_BLOCK_SIZE)
+#define TEXT_PER_LINE (GROUPS_PER_LINE * BASE64_BINARY_BLOCK_SIZE)
+
+int
+pgp_armor(struct nettle_buffer *buffer,
+	  const char *tag,
+	  unsigned length,
+	  const uint8_t *data)
+{
+  unsigned crc = pgp_crc24(length, data);
+  
+  if (! (WRITE(buffer, "BEGIN PGP ")
+	 && WRITE(buffer, tag)
+	 && WRITE(buffer, "\nComment: Nettle\n\n")))
+    return 0;
+
+  for (;
+       length >= BINARY_PER_LINE;
+       length -= BINARY_PER_LINE, data += BINARY_PER_LINE)
+    {
+      uint8_t *p
+	= nettle_buffer_space(buffer, TEXT_PER_LINE);
+      
+      if (!p)
+	return 0;
+
+      base64_encode(p, BINARY_PER_LINE, data);
+
+      if (!NETTLE_BUFFER_PUTC(buffer, '\n'))
+	return 0;
+    }
+
+  if (length)
+    {
+      unsigned text_size = BASE64_ENCODE_LENGTH(length);
+
+      uint8_t *p
+	= nettle_buffer_space(buffer, text_size);
+      if (!p)
+	return 0;
+
+      base64_encode(p, length, data);
+      if (!NETTLE_BUFFER_PUTC(buffer, '\n'))
+	return 0;
+    }
+  /* Checksum */
+  if (!NETTLE_BUFFER_PUTC(buffer, '='))
+    return 0;
+
+  {
+    uint8_t *p = nettle_buffer_space(buffer, 4);
+    if (!p)
+      return 0;
+    base64_encode_group(p, crc);
+  }
+  
+  return (WRITE(buffer, "\nBEGIN PGP ")
+	  && WRITE(buffer, tag)
+	  && NETTLE_BUFFER_PUTC(buffer, '\n'));
+}
diff --git a/pgp.h b/pgp.h
new file mode 100644
index 00000000..b13bafe4
--- /dev/null
+++ b/pgp.h
@@ -0,0 +1,173 @@
+/* pgp.h
+ *
+ * PGP related functions.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2001, 2002 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.
+ */
+
+#ifndef NETTLE_PGP_H_INCLUDED
+#define NETTLE_PGP_H_INCLUDED
+
+#include "bignum.h"
+
+struct nettle_buffer;
+
+int
+pgp_put_uint32(struct nettle_buffer *buffer, uint32_t i);
+
+int
+pgp_put_uint16(struct nettle_buffer *buffer, unsigned i);
+
+int
+pgp_put_mpi(struct nettle_buffer *buffer, mpz_t x);
+
+int
+pgp_put_string(struct nettle_buffer *buffer,
+	       unsigned length,
+	       const uint8_t *s);
+
+int
+pgp_put_length(struct nettle_buffer *buffer,
+	       unsigned length);
+
+int
+pgp_put_header(struct nettle_buffer *buffer,
+	       unsigned tag, unsigned length);
+
+void
+pgp_put_header_length(struct nettle_buffer *buffer,
+		      /* start of the header */
+		      unsigned start,
+		      unsigned field_size);
+
+unsigned
+pgp_sub_packet_start(struct nettle_buffer *buffer);
+
+int
+pgp_put_sub_packet(struct nettle_buffer *buffer,
+		   unsigned type,
+		   unsigned length,
+		   const uint8_t *data);
+
+void
+pgp_sub_packet_end(struct nettle_buffer *buffer, unsigned start);
+
+
+int
+pgp_put_userid(struct nettle_buffer *buffer,
+	       unsigned length,
+	       const uint8_t *name);
+
+uint32_t
+pgp_crc24(unsigned length, const uint8_t *data);
+
+int
+pgp_armor(struct nettle_buffer *buffer,
+	  const char *tag,
+	  unsigned length,
+	  const uint8_t *data);
+
+/* Values that can be passed to pgp_put_header when the size of the
+ * length field, but not the length itself, is known. Also the minimum length
+ * for the given field size. */
+enum pgp_lengths
+  {
+    PGP_LENGTH_ONE_OCTET = 0,
+    PGP_LENGTH_TWO_OCTETS = 192,
+    PGP_LENGTH_FOUR_OCTETS = 8384,
+  };
+
+enum pgp_public_key_algorithm
+  {
+    PGP_RSA = 1,
+    PGP_RSA_ENCRYPT = 2,
+    PGP_RSA_SIGN = 3,
+    PGP_EL_GAMAL_ENCRYPT = 16,
+    PGP_DSA = 17,
+    PGP_EL_GAMAL = 20,
+  };
+
+enum pgp_symmetric_algorithm
+  {
+    PGP_PLAINTEXT = 0,
+    PGP_IDEA = 1,
+    PGP_3DES = 2,
+    PGP_CAST5 = 3,
+    PGP_BLOWFISH = 4,
+    PGP_SAFER_SK = 5,
+    PGP_AES128 = 7,
+    PGP_AES192 = 8,
+    PGP_AES256 = 9,
+  };
+
+enum pgp_compression_algorithm
+  {
+    PGP_UNCOMPRESSED = 0,
+    PGP_ZIP = 1,
+    PGP_ZLIB = 2,
+  };
+
+enum pgp_hash_algorithm
+  {
+    PGP_MD5 = 1,
+    PGP_SHA1 = 2,
+    PGP_RIPEMD = 3,
+    PGP_MD2 = 5,
+    PGP_TIGER192 = 6,
+    PGP_HAVAL = 7,
+  };
+
+enum pgp_tag
+  {
+    PGP_TAG_PUBLIC_SESSION_KEY = 1,
+    PGP_TAG_SIGNATURE = 2,
+    PGP_TAG_SYMMETRIC_SESSION_KEY = 3,
+    PGP_TAG_ONE_PASS_SIGNATURE = 4,
+    PGP_TAG_SECRET_KEY = 5,
+    PGP_TAG_PUBLIC_KEY = 6,
+    PGP_TAG_SECRET_SUBKEY = 7,
+    PGP_TAG_COMPRESSED = 8,
+    PGP_TAG_ENCRYPTED = 9,
+    PGP_TAG_MARKER = 10,
+    PGP_TAG_LITERAL = 11,
+    PGP_TAG_TRUST = 12,
+    PGP_TAG_USERID = 13,
+    PGP_TAG_PUBLIC_SUBKEY = 14,
+  };
+
+enum pgp_signature_type
+  {
+    PGP_SIGN_BINARY = 0,
+    PGP_SIGN_TEXT = 1,
+    PGP_SIGN_STANDALONE = 2,
+    PGP_SIGN_CERTIFICATION = 0x10,
+    PGP_SIGN_CERTIFICATION_PERSONA = 0x11,
+    PGP_SIGN_CERTIFICATION_CASUAL = 0x12,
+    PGP_SIGN_CERTIFICATION_POSITIVE = 0x13,
+    PGP_SIGN_SUBKEY = 0x18,
+    PGP_SIGN_KEY = 0x1f,
+    PGP_SIGN_REVOCATION = 0x20,
+    PGP_SIGN_REVOCATION_SUBKEY = 0x28,
+    PGP_SIGN_REVOCATION_CERTIFICATE = 0x30,
+    PGP_SIGN_TIMESTAMP = 0x40,
+  };
+
+#endif /* NETTLE_PGP_H_INCLUDED */
-- 
GitLab