332 lines
8.9 KiB
C++
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; } |