aboutsummaryrefslogtreecommitdiffstats
path: root/src/m_paper_key/parse.cpp
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2024-07-28 08:18:33 +0000
committersaturneric <[email protected]>2024-07-28 08:18:33 +0000
commitc27c541257585878e4db705559c5cb953dc860b7 (patch)
tree7dc69115de96151a40acfc8891bf9a065eee0a9b /src/m_paper_key/parse.cpp
parentfix: solve a memory leak issue (diff)
downloadModules-c27c541257585878e4db705559c5cb953dc860b7.tar.gz
Modules-c27c541257585878e4db705559c5cb953dc860b7.zip
feat: add pinentry module and paper key module
Diffstat (limited to 'src/m_paper_key/parse.cpp')
-rw-r--r--src/m_paper_key/parse.cpp537
1 files changed, 537 insertions, 0 deletions
diff --git a/src/m_paper_key/parse.cpp b/src/m_paper_key/parse.cpp
new file mode 100644
index 0000000..32cb704
--- /dev/null
+++ b/src/m_paper_key/parse.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2007, 2008, 2012, 2017 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 "parse.h"
+
+#include <qcryptographichash.h>
+#include <qdatastream.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "GFSDKBasic.h"
+#include "output.h"
+#include "packets.h"
+
+extern int verbose;
+extern int ignore_crc_error;
+
+struct packet *parse2(QDataStream &stream, unsigned char want,
+ unsigned char stop) {
+ int byte;
+ struct packet *packet = NULL;
+
+ while (!stream.atEnd()) {
+ stream >> byte;
+
+ unsigned char type;
+ unsigned int length;
+
+ if ((byte & 0x80) != 0) {
+ int tmp;
+
+ type = byte & 0x3F;
+
+ /* Old-style packet type */
+ if (!(byte & 0x40)) type >>= 2;
+
+ if (type == stop) {
+ stream << byte;
+ break;
+ }
+
+ if (byte & 0x40) {
+ /* New-style packets */
+ stream >> byte;
+ if (byte == EOF) goto fail;
+
+ if (byte == 255) {
+ /* 4-byte length */
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length = tmp << 24;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp << 16;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp << 8;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp;
+ } else if (byte >= 224) {
+ /* Partial body length, so fail (keys can't use
+ partial body) */
+ fprintf(stderr, "Invalid partial packet encoding\n");
+ goto fail;
+ } else if (byte >= 192) {
+ /* 2-byte length */
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length = ((byte - 192) << 8) + tmp + 192;
+ } else
+ length = byte;
+ } else {
+ /* Old-style packets */
+ switch (byte & 0x03) {
+ case 0:
+ /* 1-byte length */
+ stream >> byte;
+ if (byte == EOF) goto fail;
+ length = byte;
+ break;
+
+ case 1:
+ /* 2-byte length */
+ stream >> byte;
+ if (byte == EOF) goto fail;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length = byte << 8;
+ length |= tmp;
+ break;
+
+ case 2:
+ /* 4-byte length */
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length = tmp << 24;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp << 16;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp << 8;
+ stream >> tmp;
+ if (tmp == EOF) goto fail;
+ length |= tmp;
+ break;
+
+ default:
+ fprintf(stderr, "Error: unable to parse old-style length\n");
+ goto fail;
+ }
+ }
+
+ if (verbose > 1)
+ fprintf(stderr, "Found packet of type %d, length %d\n", type, length);
+ } else {
+ fprintf(stderr,
+ "Error: unable to parse OpenPGP packets"
+ " (is this armored data?)\n");
+ goto fail;
+ }
+
+ if (want == 0 || type == want) {
+ packet =
+ static_cast<struct packet *>(GFAllocateMemory(sizeof(struct packet)));
+ packet->type = type;
+ packet->buf = static_cast<unsigned char *>(GFAllocateMemory(length));
+ packet->len = length;
+ packet->size = length;
+ if (stream.readRawData(reinterpret_cast<char *>(packet->buf),
+ packet->len) < packet->len) {
+ fprintf(stderr, "Short read on packet type %d\n", type);
+ goto fail;
+ }
+ break;
+ } else {
+ /* We don't want it, so skip the packet. We don't use fseek
+ here since the input might be on stdin and that isn't
+ seekable. */
+
+ size_t i;
+
+ for (i = 0; i < length; i++) stream >> byte;
+ }
+ }
+
+ return packet;
+
+fail:
+ return nullptr;
+}
+
+struct packet *parse(FILE *input, unsigned char want, unsigned char stop) {
+ int byte;
+ struct packet *packet = NULL;
+
+ while ((byte = fgetc(input)) != EOF) {
+ unsigned char type;
+ unsigned int length;
+
+ if (byte & 0x80) {
+ int tmp;
+
+ type = byte & 0x3F;
+
+ /* Old-style packet type */
+ if (!(byte & 0x40)) type >>= 2;
+
+ if (type == stop) {
+ ungetc(byte, input);
+ break;
+ }
+
+ if (byte & 0x40) {
+ /* New-style packets */
+ byte = fgetc(input);
+ if (byte == EOF) goto fail;
+
+ if (byte == 255) {
+ /* 4-byte length */
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length = tmp << 24;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp << 16;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp << 8;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp;
+ } else if (byte >= 224) {
+ /* Partial body length, so fail (keys can't use
+ partial body) */
+ fprintf(stderr, "Invalid partial packet encoding\n");
+ goto fail;
+ } else if (byte >= 192) {
+ /* 2-byte length */
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length = ((byte - 192) << 8) + tmp + 192;
+ } else
+ length = byte;
+ } else {
+ /* Old-style packets */
+ switch (byte & 0x03) {
+ case 0:
+ /* 1-byte length */
+ byte = fgetc(input);
+ if (byte == EOF) goto fail;
+ length = byte;
+ break;
+
+ case 1:
+ /* 2-byte length */
+ byte = fgetc(input);
+ if (byte == EOF) goto fail;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length = byte << 8;
+ length |= tmp;
+ break;
+
+ case 2:
+ /* 4-byte length */
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length = tmp << 24;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp << 16;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp << 8;
+ tmp = fgetc(input);
+ if (tmp == EOF) goto fail;
+ length |= tmp;
+ break;
+
+ default:
+ fprintf(stderr, "Error: unable to parse old-style length\n");
+ goto fail;
+ }
+ }
+
+ if (verbose > 1)
+ fprintf(stderr, "Found packet of type %d, length %d\n", type, length);
+ } else {
+ fprintf(stderr,
+ "Error: unable to parse OpenPGP packets"
+ " (is this armored data?)\n");
+ goto fail;
+ }
+
+ if (want == 0 || type == want) {
+ packet =
+ static_cast<struct packet *>(GFAllocateMemory(sizeof(struct packet)));
+ packet->type = type;
+ packet->buf = static_cast<unsigned char *>(GFAllocateMemory(length));
+ packet->len = length;
+ packet->size = length;
+ if (fread(packet->buf, 1, packet->len, input) < packet->len) {
+ fprintf(stderr, "Short read on packet type %d\n", type);
+ goto fail;
+ }
+ break;
+ } else {
+ /* We don't want it, so skip the packet. We don't use fseek
+ here since the input might be on stdin and that isn't
+ seekable. */
+
+ size_t i;
+
+ for (i = 0; i < length; i++) fgetc(input);
+ }
+ }
+
+ return packet;
+
+fail:
+ return NULL;
+}
+
+int calculate_fingerprint(struct packet *packet, size_t public_len,
+ unsigned char fingerprint[20]) {
+ if (packet->buf[0] == 3) {
+ return -1;
+ } else if (packet->buf[0] == 4) {
+ QCryptographicHash sha(QCryptographicHash::Sha1);
+ QByteArray head;
+
+ head.append(static_cast<char>(0x99));
+ head.append(static_cast<char>(public_len >> 8));
+ head.append(static_cast<char>(public_len & 0xFF));
+
+ sha.addData(head);
+ sha.addData(reinterpret_cast<const char *>(packet->buf), public_len);
+ QByteArray result = sha.result();
+
+ if (result.size() != 20) {
+ return -1;
+ }
+
+ std::memcpy(fingerprint, result.constData(), 20);
+ }
+
+ return 0;
+}
+
+#define MPI_LENGTH(_start) (((((_start)[0] << 8 | (_start)[1]) + 7) / 8) + 2)
+
+ssize_t extract_secrets(struct packet *packet) {
+ size_t offset;
+
+ if (packet->len == 0) return -1;
+
+ /* Secret keys consist of a public key with some secret material
+ stuck on the end. To get to the secrets, we have to skip the
+ public stuff. */
+
+ if (packet->buf[0] == 3) {
+ fprintf(stderr, "Version 3 (PGP 2.x style) keys are not supported.\n");
+ return -1;
+ } else if (packet->buf[0] == 4) {
+ /* Jump 5 bytes in. That gets us past 1 byte of version, and 4
+ bytes of timestamp. */
+
+ offset = 5;
+ } else
+ return -1;
+
+ if (packet->len <= offset) return -1;
+
+ switch (packet->buf[offset++]) {
+ case 1: /* RSA */
+ /* Skip 2 MPIs */
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ break;
+
+ case 16: /* Elgamal */
+ /* Skip 3 MPIs */
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ break;
+
+ case 17: /* DSA */
+ /* Skip 4 MPIs */
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ break;
+
+ case 18: /* ECDH */
+ /* Skip the curve ID and its length byte, plus an MPI, plus the
+ KDF parameters and their length byte */
+ offset += packet->buf[offset] + 1;
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ offset += packet->buf[offset] + 1;
+ if (packet->len <= offset) return -1;
+ break;
+
+ case 19: /* ECDSA */
+ case 22: /* EdDSA - note that this is from an expired draft
+ https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04,
+ but GnuPG is using algorithm 22 for it. */
+ /* Skip the curve ID and its length byte, plus an MPI */
+ offset += packet->buf[offset] + 1;
+ if (packet->len <= offset) return -1;
+ offset += MPI_LENGTH(&packet->buf[offset]);
+ if (packet->len <= offset) return -1;
+ break;
+
+ default:
+ /* What algorithm? */
+ fprintf(stderr, "Unable to parse algorithm %u\n",
+ packet->buf[offset - 1]);
+ return -1;
+ }
+
+ return offset;
+}
+
+struct packet *read_secrets_file(FILE *secrets, enum data_type input_type) {
+ struct packet *packet = NULL;
+ int final_crc = 0;
+ unsigned long my_crc = 0;
+
+ if (input_type == RAW) {
+ unsigned char buffer[1024];
+ size_t got;
+
+ while ((got = fread(buffer, 1, 1024, secrets)))
+ packet = append_packet(packet, buffer, got);
+
+ if (got == 0 && !feof(secrets)) {
+ fprintf(stderr, "Error: unable to read secrets file\n");
+ free_packet(packet);
+ return NULL;
+ }
+
+ if (packet->len >= 3) {
+ /* Grab the last 3 bytes to be the CRC24 */
+ my_crc = packet->buf[packet->len - 3] << 16;
+ my_crc |= packet->buf[packet->len - 2] << 8;
+ my_crc |= packet->buf[packet->len - 1];
+ final_crc = 1;
+ packet->len -= 3;
+ }
+ } else {
+ char line[1024];
+ unsigned int next_linenum = 1;
+
+ while (fgets(line, 1024, secrets)) {
+ unsigned int linenum, did_digit = 0;
+ unsigned long line_crc = CRC24_INIT;
+ char *tok;
+
+ if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') continue;
+
+ linenum = atoi(line);
+ if (linenum != next_linenum) {
+ fprintf(stderr, "Error: missing line number %u (saw %u)\n",
+ next_linenum, linenum);
+ free_packet(packet);
+ return NULL;
+ } else
+ next_linenum = linenum + 1;
+
+ tok = strchr(line, ':');
+ if (tok) {
+ tok = strchr(tok, ' ');
+
+ while (tok) {
+ char *next;
+
+ while (*tok == ' ') tok++;
+
+ next = strchr(tok, ' ');
+
+ if (next == NULL) {
+ /* End of line, so check the CRC. */
+ unsigned long new_crc;
+
+ if (sscanf(tok, "%06lX", &new_crc)) {
+ if (did_digit) {
+ if ((new_crc & 0xFFFFFFL) != (line_crc & 0xFFFFFFL)) {
+ fprintf(stderr,
+ "CRC on line %d does not"
+ " match (%06lX!=%06lX)\n",
+ linenum, new_crc & 0xFFFFFFL, line_crc & 0xFFFFFFL);
+ if (!ignore_crc_error) {
+ free_packet(packet);
+ return NULL;
+ }
+ }
+ } else {
+ final_crc = 1;
+ my_crc = new_crc;
+ }
+ }
+ } else {
+ unsigned int digit;
+
+ if (sscanf(tok, "%02X", &digit)) {
+ unsigned char d = digit;
+ packet = append_packet(packet, &d, 1);
+ do_crc24(&line_crc, &d, 1);
+ did_digit = 1;
+ }
+ }
+
+ tok = next;
+ }
+ } else {
+ fprintf(stderr, "No colon ':' found in line %u\n", linenum);
+ free_packet(packet);
+ return NULL;
+ }
+ }
+ }
+
+ if (final_crc) {
+ unsigned long all_crc = CRC24_INIT;
+
+ do_crc24(&all_crc, packet->buf, packet->len);
+
+ if ((my_crc & 0xFFFFFFL) != (all_crc & 0xFFFFFFL)) {
+ fprintf(stderr, "CRC of secret does not match (%06lX!=%06lX)\n",
+ my_crc & 0xFFFFFFL, all_crc & 0xFFFFFFL);
+ if (!ignore_crc_error) {
+ free_packet(packet);
+ return NULL;
+ }
+ }
+ } else {
+ fprintf(stderr, "CRC of secret is missing\n");
+ if (!ignore_crc_error) {
+ free_packet(packet);
+ return NULL;
+ }
+ }
+
+ return packet;
+} \ No newline at end of file