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