aboutsummaryrefslogtreecommitdiffstats
path: root/src/m_paper_key/output.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/m_paper_key/output.cpp')
-rw-r--r--src/m_paper_key/output.cpp304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/m_paper_key/output.cpp b/src/m_paper_key/output.cpp
new file mode 100644
index 0000000..8c6291b
--- /dev/null
+++ b/src/m_paper_key/output.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#include <io.h>
+#endif
+#include "output.h"
+#include "packets.h"
+
+extern unsigned int output_width;
+extern char *comment;
+
+static enum data_type output_type;
+static FILE *output;
+static unsigned int line_items;
+static unsigned long all_crc = CRC24_INIT;
+
+#define CRC24_POLY 0x864CFBL
+
+void do_crc24(unsigned long *crc, const unsigned char *buf, size_t len) {
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ int j;
+
+ *crc ^= buf[i] << 16;
+ for (j = 0; j < 8; j++) {
+ *crc <<= 1;
+ if (*crc & 0x1000000) *crc ^= CRC24_POLY;
+ }
+ }
+}
+
+static void print_base16(const unsigned char *buf, size_t length) {
+ static unsigned long line_crc = CRC24_INIT;
+ static unsigned int line = 0;
+
+ if (buf) {
+ size_t i;
+ static unsigned int offset = 0;
+
+ for (i = 0; i < length; i++, offset++) {
+ if (offset % line_items == 0) {
+ if (line) {
+ fprintf(output, "%06lX\n", line_crc & 0xFFFFFFL);
+ line_crc = CRC24_INIT;
+ }
+
+ fprintf(output, "%3u: ", ++line);
+ }
+
+ fprintf(output, "%02X ", buf[i]);
+
+ do_crc24(&line_crc, &buf[i], 1);
+ }
+ } else {
+ fprintf(output, "%06lX\n", line_crc & 0xFFFFFFL);
+ fprintf(output, "%3u: %06lX\n", line + 1, all_crc & 0xFFFFFFL);
+ }
+}
+
+void print_bytes(FILE *stream, const unsigned char *buf, size_t length) {
+ size_t i;
+
+ for (i = 0; i < length; i++) fprintf(stream, "%02X", buf[i]);
+}
+
+void output_file_format(FILE *stream, const char *prefix) {
+ fprintf(stream, "%sFile format:\n", prefix);
+ fprintf(stream,
+ "%sa) 1 octet: Version of the paperkey format (currently 0).\n",
+ prefix);
+ fprintf(stream,
+ "%sb) 1 octet: OpenPGP key or subkey version (currently 4)\n",
+ prefix);
+ fprintf(stream,
+ "%sc) n octets: Key fingerprint (20 octets for a version 4 key or "
+ "subkey)\n",
+ prefix);
+ fprintf(
+ stream,
+ "%sd) 2 octets: 16-bit big endian length of the following secret data\n",
+ prefix);
+ fprintf(stream,
+ "%se) n octets: Secret data: a partial OpenPGP secret key or subkey "
+ "packet as\n",
+ prefix);
+ fprintf(stream,
+ "%s specified in RFC 4880, starting with the "
+ "string-to-key usage\n",
+ prefix);
+ fprintf(stream,
+ "%s octet and continuing until the end of the packet.\n",
+ prefix);
+ fprintf(stream,
+ "%sRepeat fields b through e as needed to cover all subkeys.\n",
+ prefix);
+ fprintf(stream, "%s\n", prefix);
+ fprintf(
+ stream,
+ "%sTo recover a secret key without using the paperkey program, use the\n",
+ prefix);
+ fprintf(stream,
+ "%skey fingerprint to match an existing public key packet with the\n",
+ prefix);
+ fprintf(stream,
+ "%scorresponding secret data from the paper key. Next, append this "
+ "secret\n",
+ prefix);
+ fprintf(stream,
+ "%sdata to the public key packet. Finally, switch the public key "
+ "packet tag\n",
+ prefix);
+ fprintf(stream,
+ "%sfrom 6 to 5 (14 to 7 for subkeys). This will recreate the "
+ "original secret\n",
+ prefix);
+ fprintf(stream,
+ "%skey or secret subkey packet. Repeat as needed for all public key "
+ "or subkey\n",
+ prefix);
+ fprintf(stream,
+ "%spackets in the public key. All other packets (user IDs, "
+ "signatures, etc.)\n",
+ prefix);
+ fprintf(stream, "%smay simply be copied from the public key.\n", prefix);
+}
+
+int output_start(const char *name, enum data_type type,
+ unsigned char fingerprint[20]) {
+ if (name) {
+ if (type == RAW)
+ output = fopen(name, "wb");
+ else
+ output = fopen(name, "w");
+
+ if (!output) return -1;
+ } else {
+ if (type == RAW) set_binary_mode(stdout);
+
+ output = stdout;
+ }
+
+ output_type = type;
+
+ switch (type) {
+ case RAW:
+ break;
+
+ case AUTO:
+ case BASE16: {
+ time_t now = time(NULL);
+
+ line_items = (output_width - 5 - 6) / 3;
+ fprintf(output, "# Secret portions of key ");
+ print_bytes(output, fingerprint, 20);
+ fprintf(output, "\n");
+ fprintf(output, "# Base16 data extracted %.24s\n", ctime(&now));
+ fprintf(output,
+ "# Created with "
+ "Paper Key Module of GpgFrontend"
+ " by Saturneric\n#\n");
+ output_file_format(output, "# ");
+ fprintf(output,
+ "#\n# Each base16 line ends with a CRC-24 of that line.\n");
+ fprintf(output,
+ "# The entire block of data ends with a CRC-24 of the entire "
+ "block of data.\n\n");
+ // if (comment != nullptr) fprintf(output, "# %s\n\n", comment);
+ } break;
+ }
+
+ return 0;
+}
+
+ssize_t output_bytes(const unsigned char *buf, size_t length) {
+ ssize_t ret = -1;
+
+ do_crc24(&all_crc, buf, length);
+
+ switch (output_type) {
+ case RAW:
+ if (buf == NULL) {
+ unsigned char crc[3];
+
+ crc[0] = (all_crc & 0xFFFFFFL) >> 16;
+ crc[1] = (all_crc & 0xFFFFFFL) >> 8;
+ crc[2] = (all_crc & 0xFFFFFFL);
+
+ ret = fwrite(crc, 1, 3, output);
+ } else
+ ret = fwrite(buf, 1, length, output);
+ break;
+
+ case AUTO:
+ case BASE16:
+ print_base16(buf, length);
+ ret = length;
+ break;
+ }
+
+ return ret;
+}
+
+ssize_t output_length16(size_t length) {
+ unsigned char encoded[2];
+
+ assert(length <= 65535);
+
+ encoded[0] = length >> 8;
+ encoded[1] = length;
+
+ return output_bytes(encoded, 2);
+}
+
+ssize_t output_openpgp_header(unsigned char tag, size_t length) {
+ unsigned char encoded[6];
+ size_t bytes;
+
+ /* We use the same "tag under 16, use old-style packets" rule that
+ many OpenPGP programs do. This helps make the resulting key
+ byte-for-byte identical. It's not a guarantee, as it is legal
+ for the generating program to use whatever packet style it likes,
+ but does help avoid questions why the input to paperkey might not
+ equal the output. */
+
+ if (tag < 16) {
+ if (length > 65535) {
+ encoded[0] = 0x80 | (tag << 2) | 2;
+ encoded[1] = length >> 24;
+ encoded[2] = length >> 16;
+ encoded[3] = length >> 8;
+ encoded[4] = length;
+ bytes = 5;
+ } else if (length > 255) {
+ encoded[0] = 0x80 | (tag << 2) | 1;
+ encoded[1] = length >> 8;
+ encoded[2] = length;
+ bytes = 3;
+ } else {
+ encoded[0] = 0x80 | (tag << 2);
+ encoded[1] = length;
+ bytes = 2;
+ }
+ } else {
+ encoded[0] = 0xC0 | tag;
+
+ if (length > 8383) {
+ encoded[1] = 0xFF;
+ encoded[2] = length >> 24;
+ encoded[3] = length >> 16;
+ encoded[4] = length >> 8;
+ encoded[5] = length;
+ bytes = 6;
+ } else if (length > 191) {
+ encoded[1] = 192 + ((length - 192) >> 8);
+ encoded[2] = (length - 192);
+ bytes = 3;
+ } else {
+ encoded[1] = length;
+ bytes = 2;
+ }
+ }
+
+ return output_bytes(encoded, bytes);
+}
+
+void output_finish(void) {
+ output_bytes(nullptr, 0);
+ if (output != nullptr && output != stdout) fclose(output);
+}
+
+void set_binary_mode(FILE *stream) {
+#ifdef _WIN32
+ if (_setmode(_fileno(stream), _O_BINARY) == -1) {
+ fprintf(stderr, "Unable to set stream mode to binary: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+#else
+ (void)stream;
+#endif
+} \ No newline at end of file