diff options
Diffstat (limited to 'src/m_paper_key/restore.cpp')
| -rw-r--r-- | src/m_paper_key/restore.cpp | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/m_paper_key/restore.cpp b/src/m_paper_key/restore.cpp new file mode 100644 index 0000000..e1c8146 --- /dev/null +++ b/src/m_paper_key/restore.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2012 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 "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; +} |
