Modules/src/m_paper_key/restore.cpp

182 lines
4.7 KiB
C++

/*
* Copyright (C) 2007, 2012 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 "restore.h"
#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "GFSDKBasic.h"
#include "output.h"
#include "packets.h"
#include "parse.h"
struct key {
unsigned char fpr[20];
struct packet *packet;
struct key *next;
};
static auto extract_keys(struct packet *packet) -> struct key * {
struct key *key = NULL;
size_t idx = 1;
/* Check the version */
if (packet->len && packet->buf[0] != 0) {
fprintf(stderr, "Cannot handle secrets file version %d\n", packet->buf[0]);
return NULL;
}
while (idx < packet->len) {
/* 1+20+2 == version + fingerprint + length */
if (idx + 1 + 20 + 2 <= packet->len) {
if (packet->buf[idx] == 4) {
unsigned int len;
struct key *newkey;
newkey =
static_cast<struct key *>(GFAllocateMemory(sizeof(struct key)));
newkey->next = NULL;
idx++;
memcpy(newkey->fpr, &packet->buf[idx], 20);
idx += 20;
len = packet->buf[idx++] << 8;
len |= packet->buf[idx++];
if (idx + len <= packet->len) {
newkey->packet = append_packet(NULL, &packet->buf[idx], len);
idx += len;
} else {
fprintf(stderr, "Warning: Short data in secret image\n");
free(newkey);
break;
}
newkey->next = key;
key = newkey;
} else {
fprintf(stderr, "Warning: Corrupt data in secret image\n");
break;
}
} else {
fprintf(stderr, "Warning: Short header in secret image\n");
break;
}
}
return key;
}
static void free_keys(struct key *key) {
while (key) {
struct key *keytmp = key;
free_packet(key->packet);
key = key->next;
free(keytmp);
}
}
auto restore(FILE *pubring, FILE *secrets, enum data_type input_type,
const char *outname) -> int {
struct packet *secret;
if (input_type == AUTO) {
int test = fgetc(secrets);
if (test == EOF) {
fprintf(stderr, "Unable to check type of secrets file\n");
return 1;
} else if (isascii(test) && isprint(test))
input_type = BASE16;
else
input_type = RAW;
ungetc(test, secrets);
}
secret = read_secrets_file(secrets, input_type);
if (secret) {
struct packet *pubkey;
struct key *keys;
int did_pubkey = 0;
/* Build a list of all keys. We need to do this since the
public key we are transforming might have the subkeys in a
different order than (or not match subkeys at all with) our
secret data. */
keys = extract_keys(secret);
if (keys) {
output_start(outname, RAW, NULL);
while ((pubkey = parse(pubring, 0, 0))) {
unsigned char ptag;
if (pubkey->type == 6 || pubkey->type == 14) {
/* Public key or subkey */
unsigned char fpr[20];
struct key *keyidx;
if (pubkey->type == 6 && did_pubkey) break;
calculate_fingerprint(pubkey, pubkey->len, fpr);
/* Do we have a secret key that matches? */
for (keyidx = keys; keyidx; keyidx = keyidx->next) {
if (memcmp(fpr, keyidx->fpr, 20) == 0) {
if (pubkey->type == 6) {
ptag = 5;
did_pubkey = 1;
} else
ptag = 7;
/* Match, so create a secret key. */
output_openpgp_header(ptag, pubkey->len + keyidx->packet->len);
output_packet(pubkey);
output_packet(keyidx->packet);
}
}
} else if (did_pubkey) {
/* Copy the usual user ID, sigs, etc, so the key is
well-formed. */
output_openpgp_header(pubkey->type, pubkey->len);
output_packet(pubkey);
}
free_packet(pubkey);
}
free_keys(keys);
} else {
fprintf(stderr, "Unable to parse secret data\n");
return 1;
}
} else {
fprintf(stderr, "Unable to read secrets file\n");
return 1;
}
return 0;
}