diff options
author | saturneric <[email protected]> | 2024-07-28 08:18:33 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-07-28 08:18:33 +0000 |
commit | c27c541257585878e4db705559c5cb953dc860b7 (patch) | |
tree | 7dc69115de96151a40acfc8891bf9a065eee0a9b /src/m_paper_key/parse.cpp | |
parent | fix: solve a memory leak issue (diff) | |
download | Modules-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.cpp | 537 |
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 |