Modules/src/m_paper_key/output.cpp

332 lines
8.9 KiB
C++

/*
* Copyright (C) 2007, 2008, 2009, 2012, 2016 David Shaw <dshaw@jabberwocky.com>
*
* 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 "output.h"
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#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(FILE *fp, enum data_type type, unsigned char fingerprint[20]) {
output = fp;
if (!output) return -1;
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;
}
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); }
void output_end() {
if (output != nullptr) {
fflush(output);
fclose(output);
}
}
void set_binary_mode(FILE *stream) { (void)stream; }