src/packet.c
/*
* packet.c
* libsimplepgp
*
* Created by Trevor Bentley on 11/1/11.
*
* Copyright 2011 Trevor Bentley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "simplepgp.h"
#include "packet_private.h"
#include "keychain.h"
#include "util.h"
#include "mpi.h"
#include "gcrypt.h"
#include <wchar.h>
#include <locale.h>
#include <string.h>
/**********************************************************************
**
** Static variables
**
***********************************************************************/
/**********************************************************************
**
** Extern variables
**
***********************************************************************/
pthread_mutex_t spgp_mtx;
uint32_t _spgp_err;
jmp_buf exception;
#ifdef DEBUG_LOG_ENABLED
uint8_t debug_log_enabled = 1;
#else
uint8_t debug_log_enabled = 0;
#endif
/**********************************************************************
**
** Static function prototypes
**
***********************************************************************/
static spgp_packet_t* spgp_packet_decode_loop(uint8_t *message,
uint32_t *idx,
uint32_t length);
static uint8_t spgp_parse_header(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt);
static uint32_t spgp_new_header_length(uint8_t *header,
uint8_t *header_len,
uint8_t *is_partial);
static uint8_t spgp_parse_user_id(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt);
static uint8_t spgp_generate_fingerprint(spgp_packet_t *pkt);
static uint8_t spgp_verify_decrypted_data(uint8_t *data, uint32_t length);
static uint8_t spgp_generate_cipher_key(spgp_packet_t *pkt,
uint8_t *passphrase, uint32_t length);
static uint8_t spgp_parse_public_key(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt);
static uint8_t spgp_parse_secret_key(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt);
static spgp_packet_t *spgp_next_secret_key_packet(spgp_packet_t *msg);
static uint8_t spgp_decrypt_secret_key(spgp_packet_t *pkt,
uint8_t *passphrase, uint32_t length);
static uint8_t spgp_parse_compressed_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt);
static uint8_t spgp_parse_encrypted_packet(uint8_t *msg,
uint32_t *idx,
uint32_t *length,
spgp_packet_t *pkt);
static uint8_t spgp_parse_literal_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt);
static uint8_t spgp_parse_signature_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt);
static spgp_packet_t *spgp_find_session_packet(spgp_packet_t *chain);
static uint8_t spgp_parse_session_packet(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt);
static spgp_packet_t *spgp_secret_key_matching_id(spgp_packet_t *chain,
uint8_t *keyid);
static uint8_t spgp_read_salt(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_secret_pkt_t *secret);
static uint8_t spgp_read_iv(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_secret_pkt_t *secret);
/**********************************************************************
**
** External function definitions
**
***********************************************************************/
#pragma mark External Function Definitions
uint8_t spgp_init(void) {
if (pthread_mutex_init(&spgp_mtx, NULL)) return -1;
if (spgp_keychain_init()) return -1;
return 0;
}
uint8_t spgp_close(void) {
spgp_packet_t *chain = NULL;
if (spgp_keychain_is_valid()) {
spgp_keychain_iter_start();
while ((chain = spgp_keychain_iter_next()) != NULL) {
spgp_free_packet(&chain);
}
spgp_keychain_iter_end();
spgp_keychain_free();
}
pthread_mutex_destroy(&spgp_mtx);
return 0;
}
spgp_packet_t *spgp_decode_message(uint8_t *message, uint32_t length) {
spgp_packet_t *head = NULL;
// spgp_packet_t *pkt = NULL;
uint32_t idx = 0;
LOG_PRINT("begin\n");
switch (setjmp(exception)) {
case 0:
break; /* Run logic */
/* Below here are exceptions */
default:
LOG_PRINT("Exception (0x%x)\n",_spgp_err);
spgp_free_packet(&head);
goto end;
}
if (NULL == message || 0 == length) {
RAISE(INVALID_ARGS);
}
#if 0
// There must be at least one packet, yeah?
head = malloc(sizeof(*head));
if (NULL == head) RAISE(OUT_OF_MEMORY);
memset(head, 0, sizeof(*head));
pkt = head;
// Loop to decode every packet in message
while (idx < length-1) {
// Every packet starts with a header
spgp_parse_header(message, &idx, length, pkt);
if (!pkt->header) RAISE(FORMAT_UNSUPPORTED);
// Decode packet contents based on the type marked in its header
switch (pkt->header->type) {
case PKT_TYPE_USER_ID:
spgp_parse_user_id(message, &idx, length, pkt);
break;
case PKT_TYPE_PUBLIC_KEY:
case PKT_TYPE_PUBLIC_SUBKEY:
spgp_parse_public_key(message, &idx, length, pkt);
break;
case PKT_TYPE_SECRET_KEY:
case PKT_TYPE_SECRET_SUBKEY:
spgp_parse_secret_key(message, &idx, length, pkt);
break;
case PKT_TYPE_SESSION:
spgp_parse_session_packet(message, &idx, length, pkt);
break;
case PKT_TYPE_SYM_ENC_INT_DATA:
spgp_parse_encrypted_packet(message, &idx, length, pkt);
break;
case PKT_TYPE_COMPRESSED_DATA:
spgp_parse_compressed_packet(message, &idx, length, pkt);
break;
default:
LOG_PRINT("WARNING: Unsupported packet type %u\n", pkt->header->type);
// Increment to next packet. We add the contentLength, but subtract
// one parse_header() left us on the first byte of content.
if (idx + pkt->header->contentLength - 1 < length)
idx = idx + pkt->header->contentLength - 1;
break;
}
// If we're at the end of the buffer, we're done
if (idx >= length-1) break;
// Allocate space for another packet
pkt->next = malloc(sizeof(*pkt->next));
if (NULL == pkt->next) RAISE(OUT_OF_MEMORY);
memset(pkt->next, 0, sizeof(*pkt->next));
pkt->next->prev = pkt; // make backwards pointer
pkt = pkt->next;
// Packet parser increments to it's own last byte. Need one more to get
// to the next packet's first byte.
SAFE_IDX_INCREMENT(idx, length);
}
#endif
head = spgp_packet_decode_loop(message, &idx, length);
end:
LOG_PRINT("done\n");
return head;
}
char *spgp_get_literal_data(spgp_packet_t *msg, uint32_t *datalen,
char **filename, uint32_t *filenamelen) {
spgp_packet_t *cur = msg;
if (setjmp(exception)) {
LOG_PRINT("Exception (0x%x)\n",_spgp_err);
goto end;
}
if (NULL == msg || NULL == datalen ||
NULL == filename || NULL == filenamelen)
RAISE(INVALID_ARGS);
while (cur) {
if (cur->header->type == PKT_TYPE_LITERAL_DATA) {
*datalen = cur->c.literal->dataLen;
*filenamelen = cur->c.literal->filenameLen;
*filename = cur->c.literal->filename;
return cur->c.literal->data;
}
cur = cur->next;
}
end:
return NULL;
}
uint8_t spgp_decrypt_all_secret_keys(spgp_packet_t *msg,
uint8_t *passphrase, uint32_t length) {
spgp_packet_t *cur = msg;
uint8_t err = 0;
uint8_t haskey = 0;
if (setjmp(exception)) {
LOG_PRINT("Exception (0x%x)\n",_spgp_err);
goto end;
}
if (NULL == msg || NULL == passphrase || length == 0) RAISE(INVALID_ARGS);
while ((cur = spgp_next_secret_key_packet(cur)) != NULL) {
LOG_PRINT("Decrypting secret key\n");
spgp_decrypt_secret_key(cur, passphrase, length);
cur = cur->next;
haskey = 1;
}
// Add decrypted keys to keychain
if (haskey)
if (spgp_keychain_add_packet(msg) != 0) RAISE(KEYCHAIN_ERROR);
end:
return err;
}
void spgp_free_packet(spgp_packet_t **pkt) {
spgp_mpi_t *curMpi, *nextMpi;
if (pkt == NULL)
return;
if (*pkt == NULL)
return;
//LOG_PRINT("Freeing packet: %p\n", pkt);
// Recursively call on the next packet before freeing parent.
if ((*pkt)->next) spgp_free_packet(&((*pkt)->next));
// Release memory allocated for secret key fields
if (((*pkt)->header->type == PKT_TYPE_SECRET_KEY ||
(*pkt)->header->type == PKT_TYPE_SECRET_SUBKEY) &&
(*pkt)->c.secret != NULL) {
if ((*pkt)->c.secret->pub.mpiCount > 0) {
curMpi = (*pkt)->c.secret->pub.mpiHead;
while (curMpi) {
nextMpi = curMpi->next;
if (curMpi->data) free(curMpi->data);
free(curMpi);
curMpi = nextMpi;
}
(*pkt)->c.secret->pub.mpiHead = NULL;
(*pkt)->c.secret->pub.mpiCount = 0;
}
if ((*pkt)->c.secret->pub.fingerprint) {
free((*pkt)->c.secret->pub.fingerprint);
}
if ((*pkt)->c.secret->encryptedData) {
free((*pkt)->c.secret->encryptedData);
}
if ((*pkt)->c.secret->s2kSalt) {
free((*pkt)->c.secret->s2kSalt);
(*pkt)->c.secret->s2kSalt = NULL;
}
if ((*pkt)->c.secret->key) {
free((*pkt)->c.secret->key);
(*pkt)->c.secret->key = NULL;
}
if ((*pkt)->c.secret->iv) {
free((*pkt)->c.secret->iv);
(*pkt)->c.secret->iv = NULL;
}
free((*pkt)->c.secret);
(*pkt)->c.secret = NULL;
}
else if (((*pkt)->header->type == PKT_TYPE_PUBLIC_KEY ||
(*pkt)->header->type == PKT_TYPE_PUBLIC_SUBKEY) &&
(*pkt)->c.pub != NULL) {
if ((*pkt)->c.pub->mpiCount > 0) {
curMpi = (*pkt)->c.pub->mpiHead;
while (curMpi->next) {
nextMpi = curMpi->next;
if (curMpi->data) free(curMpi->data);
free(curMpi);
curMpi = nextMpi;
}
(*pkt)->c.pub->mpiHead = NULL;
(*pkt)->c.pub->mpiCount = 0;
}
if ((*pkt)->c.pub->fingerprint) {
free((*pkt)->c.pub->fingerprint);
}
}
else if ((*pkt)->header->type == PKT_TYPE_USER_ID &&
(*pkt)->c.userid->data != NULL) {
free((*pkt)->c.userid->data);
(*pkt)->c.userid->data = NULL;
free((*pkt)->c.userid);
(*pkt)->c.userid = NULL;
}
else if ((*pkt)->header->type == PKT_TYPE_SESSION &&
(*pkt)->c.session != NULL) {
if ((*pkt)->c.session->key) {
free((*pkt)->c.session->key);
(*pkt)->c.session->key = NULL;
}
if ((*pkt)->c.session->mpi1) {
free((*pkt)->c.session->mpi1->data);
free((*pkt)->c.session->mpi1);
(*pkt)->c.session->mpi1 = NULL;
}
if ((*pkt)->c.session->mpi2) {
free((*pkt)->c.session->mpi2->data);
free((*pkt)->c.session->mpi2);
(*pkt)->c.session->mpi2 = NULL;
}
free((*pkt)->c.session);
}
else if ((*pkt)->header->type == PKT_TYPE_LITERAL_DATA &&
(*pkt)->c.literal != NULL) {
if ((*pkt)->c.literal->filename) {
free((*pkt)->c.literal->filename);
(*pkt)->c.literal->filename = NULL;
}
if ((*pkt)->c.literal->data) {
free((*pkt)->c.literal->data);
(*pkt)->c.literal->data = NULL;
}
free((*pkt)->c.literal);
}
// release header
if ((*pkt)->header) {
free((*pkt)->header);
(*pkt)->header = NULL;
}
// release packet
free(*pkt);
*pkt = NULL;
}
uint32_t spgp_err(void) {
return _spgp_err;
}
const char *spgp_err_str(uint32_t err) {
switch (err) {
case INVALID_ARGS:
return "Invalid arguments given to function.";
case OUT_OF_MEMORY:
return "Not enough memory to continue parsing.";
case INVALID_HEADER:
return "Invalid header format. Corrupted or invalid data.";
case FORMAT_UNSUPPORTED:
return "Message format is valid, but not currently supported.";
case BUFFER_OVERFLOW:
return "Index into buffer exceeded the maximum "
"bound of the buffer.";
default:
return "Unknown/undocumented error.";
}
}
uint8_t spgp_debug_log_enabled(void) {
return debug_log_enabled;
}
void spgp_debug_log_set(uint8_t enable) {
pthread_mutex_lock(&spgp_mtx);
debug_log_enabled = enable;
pthread_mutex_unlock(&spgp_mtx);
}
/**********************************************************************
**
** Static function definitions
**
***********************************************************************/
#pragma mark Static Function Definitions
static spgp_packet_t* spgp_packet_decode_loop(uint8_t *message,
uint32_t *idx,
uint32_t length) {
spgp_packet_t *head = NULL;
spgp_packet_t *pkt = NULL;
// There must be at least one packet, yeah?
head = malloc(sizeof(*head));
if (NULL == head) RAISE(OUT_OF_MEMORY);
memset(head, 0, sizeof(*head));
pkt = head;
// Loop to decode every packet in message
while (*idx < length-1) {
// Every packet starts with a header
spgp_parse_header(message, idx, length, pkt);
if (!pkt->header) RAISE(FORMAT_UNSUPPORTED);
// Decode packet contents based on the type marked in its header
switch (pkt->header->type) {
case PKT_TYPE_USER_ID:
spgp_parse_user_id(message, idx, length, pkt);
break;
case PKT_TYPE_PUBLIC_KEY:
case PKT_TYPE_PUBLIC_SUBKEY:
spgp_parse_public_key(message, idx, length, pkt);
break;
case PKT_TYPE_SECRET_KEY:
case PKT_TYPE_SECRET_SUBKEY:
spgp_parse_secret_key(message, idx, length, pkt);
break;
case PKT_TYPE_SESSION:
spgp_parse_session_packet(message, idx, length, pkt);
break;
case PKT_TYPE_SYM_ENC_INT_DATA:
spgp_parse_encrypted_packet(message, idx, &length, pkt);
break;
case PKT_TYPE_COMPRESSED_DATA:
spgp_parse_compressed_packet(message, idx, length, pkt);
break;
case PKT_TYPE_LITERAL_DATA:
spgp_parse_literal_packet(message, idx, length, pkt);
break;
case PKT_TYPE_SIGNATURE:
spgp_parse_signature_packet(message, idx, length, pkt);
break;
default:
LOG_PRINT("WARNING: Unsupported packet type %u\n", pkt->header->type);
// Increment to next packet. We add the contentLength, but subtract
// one parse_header() left us on the first byte of content.
if (*idx + pkt->header->contentLength - 1 < length)
*idx = *idx + pkt->header->contentLength - 1;
break;
}
// If we're at the end of the buffer, we're done
if (*idx >= length-1) break;
// A packet can contain other packets -- if such a thing was just decoded,
// new packets have already been added to the list. Progress until we
// get to the end of the packet list.
while (pkt->next != NULL) pkt = pkt->next;
// Allocate space for another packet
pkt->next = malloc(sizeof(*pkt->next));
if (NULL == pkt->next) RAISE(OUT_OF_MEMORY);
memset(pkt->next, 0, sizeof(*pkt->next));
pkt->next->prev = pkt; // make backwards pointer
pkt = pkt->next;
// Packet parser increments to it's own last byte. Need one more to get
// to the next packet's first byte.
SAFE_IDX_INCREMENT(*idx, length);
}
return head;
}
static uint8_t spgp_parse_header(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt) {
uint8_t i;
if (NULL == msg || NULL == pkt || NULL == idx || length == 0)
RAISE(INVALID_ARGS);
// Allocate a header
if (pkt->header == NULL) {
LOG_PRINT("Allocating header.\n");
pkt->header = malloc(sizeof(*(pkt->header)));
if (pkt->header == NULL)
RAISE(OUT_OF_MEMORY);
memset(pkt->header, 0, sizeof(*(pkt->header)));
}
// Header points to its parent packet
pkt->header->parent = pkt;
// The first byte is the 'tag byte', which tells us the packet type.
pkt->header->rawTagByte = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("TAG BYTE: 0x%.2X\n", pkt->header->rawTagByte);
// Validate tag byte -- top bit always set
if (!(pkt->header->rawTagByte & 0x80))
RAISE(INVALID_HEADER);
// Second-MSB tells us if this is new or old format header
pkt->header->isNewFormat = pkt->header->rawTagByte & 0x40;
// Read the packet type out of the tag byte
if (pkt->header->isNewFormat)
pkt->header->type = pkt->header->rawTagByte & 0x1F;
else // old style
pkt->header->type = (pkt->header->rawTagByte >> 2) & 0x0F;
LOG_PRINT("TYPE: 0x%.2X\n", pkt->header->type);
// Read the length of the packet's contents. In old packets, the length
// is encoded into the tag byte.
if (pkt->header->isNewFormat == 0) {
switch (pkt->header->rawTagByte & 0x03) {
case 0: pkt->header->headerLength = 2; break;
case 1: pkt->header->headerLength = 3; break;
case 2: pkt->header->headerLength = 5; break;
default:
// "indeterminate length" packet
LOG_PRINT("Indeterminate length packet\n");
pkt->header->headerLength = 1;
pkt->header->contentLength = length-*idx-1;
}
for (i = 0; i < pkt->header->headerLength - 1; i++) {
pkt->header->contentLength <<= 8;
pkt->header->contentLength += msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
}
}
// In new packets, the length is encoded over a variable number of bytes,
// with the range of the first byte determining total number of bytes.
else { // This is new style packet.
pkt->header->contentLength =
spgp_new_header_length(msg+*idx,
&(pkt->header->headerLength),
&(pkt->header->isPartial));
*idx += pkt->header->headerLength - 2;
SAFE_IDX_INCREMENT(*idx, length);
}
LOG_PRINT("LENGTH: %u\n", pkt->header->contentLength);
return 0;
}
static uint32_t spgp_new_header_length(uint8_t *header,
uint8_t *header_len,
uint8_t *is_partial) {
uint32_t content;
uint8_t len[4];
uint8_t i = 0;
if (NULL == header || NULL == header_len) RAISE(INVALID_ARGS);
*is_partial = 0; // default to known length
len[0] = header[i];
if (len[0] <= 191) { // 1-byte length
*header_len = 2;
content = len[0];
}
else if (len[0] > 191 && len[0] <= 223) { // 2-byte length
*header_len = 3;
len[1] = header[i+1];
content = ((len[0]-192)<<8) | (len[1] + 192);
}
else if (len[0] == 255) { // 5-byte length
*header_len = 5;
len[0] = header[i+1];
len[1] = header[i+2];
len[2] = header[i+3];
len[3] = header[i+4];
content = (len[0]<<24) | (len[1]<<16) | (len[2]<<8) | len[3];
}
else {
// indeterminate length
LOG_PRINT("Partial length header!\n");
*header_len = 2;
*is_partial = 1;
content = 1 << (len[0] & 0x1F);
}
return content;
}
static uint8_t spgp_parse_user_id(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt) {
spgp_userid_pkt_t *userid;
LOG_PRINT("Parsing user id.\n");
// Make sure we have enough bytes remaining for the copy
if (length - *idx < pkt->header->contentLength) RAISE(BUFFER_OVERFLOW);
// Allocate userid field in packet
pkt->c.userid = malloc(sizeof(*(pkt->c.userid)));
if (NULL == pkt->c.userid) RAISE(OUT_OF_MEMORY);
userid = pkt->c.userid;
// Allocate space for buffer, plus one byte for NUL terminator
userid->data = malloc(sizeof(*(userid->data))*pkt->header->contentLength + 1);
if (NULL == userid->data) RAISE(OUT_OF_MEMORY);
// Copy bytes from input to structure, and add a NUL terminator
memcpy(userid->data, msg+*idx, pkt->header->contentLength);
userid->data[pkt->header->contentLength] = '\0';
*idx += pkt->header->contentLength - 1;
setlocale(LC_CTYPE, "en_US.UTF-8");
wprintf(L"USER ID: %s\n", pkt->c.userid->data);
return 0;
}
static uint8_t spgp_generate_fingerprint(spgp_packet_t *pkt) {
uint16_t packetSize;
uint8_t packetHeaderSize;
spgp_mpi_t *curMpi;
uint8_t targetMpiCount;
gcry_md_hd_t md;
unsigned char *hash;
int i;
if (NULL == pkt) RAISE(INVALID_ARGS);
// Start with header info: 1 version, 4 creation time, 1 algorithm
packetHeaderSize = sizeof(pkt->c.pub->version) +
sizeof(pkt->c.pub->creationTime) +
sizeof(pkt->c.pub->asymAlgo);
packetSize = packetHeaderSize;
// Figure out how many MPIs to add
switch(pkt->c.pub->asymAlgo) {
case ASYM_ALGO_RSA:
targetMpiCount = 2;
break;
case ASYM_ALGO_DSA:
targetMpiCount = 4;
break;
case ASYM_ALGO_ELGAMAL:
targetMpiCount = 3;
break;
default:
RAISE(FORMAT_UNSUPPORTED);
}
// Add size of each MPI
curMpi = pkt->c.pub->mpiHead;
i = 0;
while (curMpi && i < targetMpiCount) {
packetSize += curMpi->count + 2; // add 2 for MPI header
curMpi = curMpi->next;
i++;
}
// Give data to hash to gcrypt
if (gcry_md_open (&md, GCRY_MD_SHA1, 0) != 0) RAISE(GCRY_ERROR);
gcry_md_putc(md, 0x99 );
gcry_md_putc(md, packetSize >> 8);
gcry_md_putc(md, packetSize);
gcry_md_putc(md, pkt->c.pub->version);
gcry_md_putc(md, pkt->c.pub->creationTime);
gcry_md_putc(md, pkt->c.pub->creationTime >> 8);
gcry_md_putc(md, pkt->c.pub->creationTime >> 16);
gcry_md_putc(md, pkt->c.pub->creationTime >> 24);
gcry_md_putc(md, pkt->c.pub->asymAlgo);
// Write the public key MPIs
curMpi = pkt->c.pub->mpiHead;
i = 0;
while (curMpi && i < targetMpiCount) {
gcry_md_write(md, curMpi->data, curMpi->count + 2);
curMpi = curMpi->next;
i++;
}
// Perform SHA-1 hash
gcry_md_final(md);
hash = gcry_md_read(md, 0);
// Copy hash results (20-bytes) into fingerprint
pkt->c.pub->fingerprint = malloc(20);
if (NULL == pkt->c.pub->fingerprint) RAISE(OUT_OF_MEMORY);
memcpy(pkt->c.pub->fingerprint, hash, 20);
LOG_PRINT("HASH: ");
for (targetMpiCount=0; targetMpiCount < 20; targetMpiCount++) {
fprintf(stderr, "%.2X", pkt->c.pub->fingerprint[targetMpiCount]);
}
fprintf(stderr,"\n");
gcry_md_close(md);
return 0;
}
static uint8_t spgp_verify_decrypted_data(uint8_t *data, uint32_t length) {
gcry_md_hd_t md;
uint32_t hashlen = length - 20; // SHA1 hash is 20 bytes
uint8_t *hashResult;
int result;
if (gcry_md_open (&md, GCRY_MD_SHA1, 0) != 0) RAISE(GCRY_ERROR);
gcry_md_write(md, data, hashlen);
gcry_md_final(md);
hashResult = gcry_md_read(md, 0);
if (NULL == hashResult) RAISE(GCRY_ERROR);
result = memcmp(data+hashlen, hashResult, 20);
gcry_md_close(md);
return result;
}
/**
* Generate cipher key to decrypt secret key packet
*
* The secret portion of a secret key packet can be encrypted with a
* symmetric cipher. This function generates the 'key' that is used as
* the input to the symmetric cipher. This key is generated by hashing
* a randomly generated salt, included in the packet, and the user's
* passphrase (which must be provided).
*
* OpenPGP's standard allows multiple ways of generating the key by varying
* the hash algorithm.
*
* @param pkt A secret key or secret subkey packet
* @param passphrase User's passphrase to decrypt with
* @param length Length (in bytes) of user's passphrase
*
* @return 0 on success. Raises exception on error.
*
*/
static uint8_t spgp_generate_cipher_key(spgp_packet_t *pkt,
uint8_t *passphrase, uint32_t length) {
spgp_secret_pkt_t *secret;
spgp_public_pkt_t *pub;
gcry_md_hd_t md;
uint32_t i;
uint32_t keyBytesRemaining;// Bytes left to generate for key
uint32_t hashLen; // How long the hash is (algo-dependent)
uint32_t hashIters; // How many hash results to combine into key
uint32_t hashBytes; // Total number of bytes to hash each time
uint32_t bufLen; // Length of salt+passphrase
uint32_t curHashCount; // How many times we have performed full hash
uint32_t hashCopies; // How many integer copies of hashBuf per round
uint32_t hashExtraBytes; // How many extra bytes to hash for last round
uint8_t *hashBuf; // Store concatenated salt+passphrase
uint8_t *hashResult; // Store result of actual hash algorithm
if (NULL == pkt || NULL == passphrase) RAISE(INVALID_ARGS);
secret = pkt->c.secret;
pub = pkt->c.pub;
if (pkt->header->type != PKT_TYPE_SECRET_KEY &&
pkt->header->type != PKT_TYPE_SECRET_SUBKEY)
RAISE(INVALID_ARGS);
// Determine how many bytes we need to produce for this cipher
// Only supporting 3DES for this
switch(secret->s2kEncryption) {
case SYM_ALGO_3DES: secret->keyLength = 24; break;
case SYM_ALGO_CAST5: secret->keyLength = 16; break;
default: RAISE(FORMAT_UNSUPPORTED);
}
// Initialize hash algorithm, determine how many bytes produces per round
switch (secret->s2kHashAlgo) {
case HASH_ALGO_SHA1:
if (gcry_md_open (&md, GCRY_MD_SHA1, 0) != 0) RAISE(GCRY_ERROR);
hashLen = 20;
break;
default:
RAISE(FORMAT_UNSUPPORTED);
break;
}
// Determine how many times we have to hash to generate a large enough key
// Ex: 3DES needs 24 bytes, SHA1 makes 20 bytes, so need to SHA1 hashes.
hashIters = (secret->keyLength/hashLen) + (secret->keyLength%hashLen>0)?1:0;
// What hashing mode to use.
// Currently only supporting salted+iterated
switch (secret->s2kSpecifier) {
case S2K_TYPE_ITERATED:
break;
default:
RAISE(FORMAT_UNSUPPORTED);
break;
}
// Allocate space for the key
secret->key = malloc(secret->keyLength);
if (NULL == secret->key) RAISE(OUT_OF_MEMORY);
// Allocate a buffer to store the salt and passphrase combined
// Since this buffer is local only, no exceptions can be raised after
// this point or memory will be leaked.
bufLen = secret->s2kSaltLength + length;
hashBuf = malloc(bufLen);
if (NULL == hashBuf) RAISE(OUT_OF_MEMORY);
// Concatenate salt and passphrase into hashBuf
memcpy(hashBuf, secret->s2kSalt, secret->s2kSaltLength);
memcpy(hashBuf + secret->s2kSaltLength, passphrase, length);
// Magic formula from RFC 4880. This is number of bytes to hash over.
hashBytes = (16 + (secret->s2kCount & 15)) << ((secret->s2kCount >> 4) + 6);
// Figure out how many times to iterate over hashBuf to get hashBytes,
// and how many extra bytes are needed at the end if not an even multiple.
hashCopies = hashBytes / (bufLen);
hashExtraBytes = hashBytes % (bufLen);
keyBytesRemaining = secret->keyLength;
// Loop until we have enough hash bytes to make the key
curHashCount = 0;
while (curHashCount <= hashIters && keyBytesRemaining) {
for (i = 0; i < curHashCount; i++) {
// pad front with 1 NUL byte per round (none on first round)
gcry_md_putc(md, '\0');
}
// Copy the salt+passphrase combo into hash buffer as many times as fits
for (i = 0; i < hashCopies; i++) {
gcry_md_write(md, hashBuf, bufLen);
}
// Copy any leftover bytes into hash buffer to reach |hashBytes|
if (hashExtraBytes) {
gcry_md_write(md, hashBuf, hashExtraBytes);
}
// Perform the hash and append to the key
gcry_md_final(md);
hashResult = gcry_md_read(md, 0);
if (keyBytesRemaining < hashLen) {
memcpy(secret->key+(curHashCount*hashLen),
hashResult,
keyBytesRemaining);
keyBytesRemaining = 0;
}
else {
memcpy(secret->key+(curHashCount*hashLen),
hashResult,
hashLen);
keyBytesRemaining -= hashLen;
}
// Reset hash algorithm for next round
gcry_md_reset(md);
curHashCount++;
}
gcry_md_close(md);
free(hashBuf);
return 0;
}
static uint8_t spgp_parse_public_key(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt) {
spgp_public_pkt_t *pub;
LOG_PRINT("Parsing public key.\n");
// Make sure we have enough bytes remaining for parsing
if (length - *idx < pkt->header->contentLength) RAISE(BUFFER_OVERFLOW);
// Allocate public key if it doesn't already exist. It might exist if
// this packet is a secret key.
if (!(pkt->c.pub)) {
pkt->c.pub = malloc(sizeof(*(pkt->c.pub)));
if (NULL == pkt->c.pub) RAISE(OUT_OF_MEMORY);
memset(pkt->c.pub, 0, sizeof(*(pkt->c.pub)));
}
pub = pkt->c.pub;
pub->version = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
// First byte is the version.
if (pub->version != 4) RAISE(FORMAT_UNSUPPORTED);
// Next 4 bytes are big-endian 'key creation time'
if (length - *idx < 4) RAISE(BUFFER_OVERFLOW);
memcpy(&(pub->creationTime), msg+*idx, 4);
*idx += 3; // this puts us on last byte of creation time
SAFE_IDX_INCREMENT(*idx, length); // this goes to next byte (safely)
// Next byte identifies asymmetric algorithm
pub->asymAlgo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("Asymmetric algorithm: %d\n", pub->asymAlgo);
// Read variable number of MPIs (depends on asymmetric algorithm), each
// of which are variable size.
spgp_read_all_public_mpis(msg, idx, length, pkt->c.pub);
LOG_PRINT("Read %u MPIs\n", pub->mpiCount);
return 0;
}
static uint8_t spgp_parse_secret_key(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt) {
spgp_secret_pkt_t *secret;
spgp_public_pkt_t *pub;
uint32_t startIdx = *idx;
LOG_PRINT("Parsing secret key.\n");
// Make sure we have enough bytes remaining for parsing
if (length - *idx < pkt->header->contentLength) RAISE(BUFFER_OVERFLOW);
// Allocate secret key in packet
pkt->c.secret = malloc(sizeof(*(pkt->c.secret)));
if (NULL == pkt->c.secret) RAISE(OUT_OF_MEMORY);
memset(pkt->c.secret, 0, sizeof(*(pkt->c.secret)));
secret = pkt->c.secret;
pub = pkt->c.pub;
// Parse the public key section that starts it
spgp_parse_public_key(msg, idx, length, pkt);
// idx ends on last byte of public key. One more to start secret key.
SAFE_IDX_INCREMENT(*idx, length);
// S2K Type byte tells how to (or if to) decrypt secret exponent
secret->s2kType = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
switch (secret->s2kType) {
case 0:
// There is no encryption
secret->s2kEncryption = 0;
break;
case 254:
case 255:
// Next byte is encryption type
secret->s2kEncryption = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
break;
default:
// This byte is encryption type
secret->s2kEncryption = secret->s2kType;
break;
}
LOG_PRINT("Encryption: %u\n", secret->s2kEncryption);
if (secret->s2kEncryption) {
// Secret exponent is encrypted (as it should be). Time to decrypt.
// S2K specifier tells us if there is a salt, and how to use it
if (secret->s2kType >= 254) {
secret->s2kSpecifier = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("S2K Specifier: %u\n", secret->s2kSpecifier);
}
// S2K hash algorithm specifies how to hash passphrase into a key
secret->s2kHashAlgo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("Hash algorithm: %u\n", secret->s2kHashAlgo);
// Read the salt if there is one
switch (secret->s2kSpecifier) {
case 1:
spgp_read_salt(msg, idx, length, secret);
break;
case 3:
spgp_read_salt(msg, idx, length, secret);
// S2K Count is number of bytes to hash to make the key
secret->s2kCount = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
break;
default:
break;
}
}
LOG_PRINT("Salt length: %u\n", secret->s2kSaltLength);
// If it's not encrypted, we can just read the secret MPIs
if (!secret->s2kEncryption) {
spgp_read_all_secret_mpis(msg, idx, length, secret);
}
// If it is encrypted, just store it for now. We'll decrypt later.
else {
// There's an initial vector (IV) here:
spgp_read_iv(msg, idx, length, secret);
LOG_PRINT("IV length: %u\n", secret->ivLength);
// Figure out how much is left, and make sure it's available
uint32_t packetOffset = *idx - startIdx;
uint32_t remaining = pkt->header->contentLength - packetOffset;
if (packetOffset >= pkt->header->contentLength) RAISE(BUFFER_OVERFLOW);
// Allocate buffer and copy data
secret->encryptedData = malloc(remaining);
if (NULL == secret->encryptedData) RAISE(OUT_OF_MEMORY);
memcpy(secret->encryptedData, msg+*idx, remaining);
secret->encryptedDataLength = remaining;
*idx += remaining-1;
LOG_PRINT("Stored %u encrypted bytes.\n", remaining);
// This is the end of the data, so we do NOT do a final idx increment
}
// Create and store fingerprint for this packet
spgp_generate_fingerprint(pkt);
return 0;
}
static spgp_packet_t *spgp_next_secret_key_packet(spgp_packet_t *msg) {
spgp_packet_t *cur = msg;
while (cur) {
if (cur->header->type == PKT_TYPE_SECRET_KEY ||
cur->header->type == PKT_TYPE_SECRET_SUBKEY)
return cur;
cur = cur->next;
}
return NULL;
}
static uint8_t spgp_decrypt_secret_key(spgp_packet_t *pkt,
uint8_t *passphrase, uint32_t length) {
gcry_cipher_hd_t hd;
spgp_secret_pkt_t *secret;
spgp_public_pkt_t *pub;
spgp_mpi_t *curMpi;
uint32_t idx;
uint32_t secretMpiCount;
uint8_t *secdata;
uint8_t i;
uint8_t err = 0;
if (NULL == pkt || NULL == passphrase || length == 0) RAISE(INVALID_ARGS);
secret = pkt->c.secret;
pub = pkt->c.pub;
if (pkt->header->type != PKT_TYPE_SECRET_KEY &&
pkt->header->type != PKT_TYPE_SECRET_SUBKEY)
RAISE(INVALID_ARGS);
if (secret->isDecrypted) return err; // already decrypted!
spgp_generate_cipher_key(pkt, passphrase, length);
switch (secret->s2kEncryption) {
case SYM_ALGO_3DES:
case SYM_ALGO_CAST5:
if (gcry_cipher_open(&hd,
spgp_pgp_to_gcrypt_symmetric_algo(secret->s2kEncryption),
GCRY_CIPHER_MODE_CFB,
GCRY_CIPHER_SECURE | GCRY_CIPHER_ENABLE_SYNC) != 0)
RAISE(GCRY_ERROR);
break;
default:
RAISE(FORMAT_UNSUPPORTED);
}
if (NULL == secret->key || NULL == secret->iv) RAISE(INCOMPLETE_PACKET);
if (gcry_cipher_setkey(hd, secret->key, secret->keyLength) != 0)
RAISE(GCRY_ERROR);
if (gcry_cipher_setiv(hd, secret->iv, secret->ivLength) != 0)
RAISE(GCRY_ERROR);
// Allocate secret data memory. Must free it before raising any exceptions!
secdata = malloc(secret->encryptedDataLength);
if (NULL == secdata) RAISE(OUT_OF_MEMORY);
if (gcry_cipher_decrypt(hd,
secdata,
secret->encryptedDataLength,
secret->encryptedData,
secret->encryptedDataLength) != 0) {
free(secdata);
RAISE(GCRY_ERROR);
}
// Verify checksum
if (spgp_verify_decrypted_data(secdata, secret->encryptedDataLength) != 0)
RAISE(DECRYPT_FAILED);
// Decode and store the secret MPIs (algo-specific):
switch(pub->asymAlgo) {
case ASYM_ALGO_RSA:
secretMpiCount = 4;
break;
case ASYM_ALGO_DSA:
case ASYM_ALGO_ELGAMAL:
secretMpiCount = 1;
break;
default:
RAISE(FORMAT_UNSUPPORTED);
}
// Get to the last valid MPI
if (NULL == pub->mpiHead) RAISE(INCOMPLETE_PACKET);
curMpi = pub->mpiHead;
while (curMpi->next) curMpi = curMpi->next;
idx = 0;
for (i = 0; i < secretMpiCount; i++) {
curMpi->next = spgp_read_mpi(secdata, &idx, secret->encryptedDataLength);
if (NULL == curMpi->next) RAISE(GENERIC_ERROR);
curMpi = curMpi->next;
SAFE_IDX_INCREMENT(idx, secret->encryptedDataLength);
pub->mpiCount++;
}
secret->isDecrypted = 1;
gcry_cipher_close(hd);
free(secdata);
end:
return err;
}
#include "zlib.h"
static uint8_t spgp_zlib_decompress_buffer(uint8_t *inbuf, uint32_t inlen,
uint8_t **outbuf, uint32_t *outlen,
uint8_t algo) {
uint32_t maxsize;
z_stream s;
uint8_t *tmpbuf;
int wbits;
int err;
int i;
if (NULL == inbuf || inlen == 0 || NULL == outbuf || NULL == outlen)
RAISE(INVALID_ARGS);
maxsize = inlen * 100;
*outbuf = malloc(maxsize);
if (NULL == *outbuf) RAISE(OUT_OF_MEMORY);
s.zalloc = Z_NULL;
s.zfree = Z_NULL;
s.next_in = inbuf;
s.avail_in = inlen;
s.next_out = *outbuf;
s.avail_out = maxsize;
if (algo == COMPRESSION_ZIP) wbits = -15;
else wbits = 15;
if (inflateInit2(&s, wbits) != Z_OK) RAISE(ZLIB_ERROR);
for (i = s.total_out; i < maxsize; i++)
(*outbuf)[i] = 0x55;
LOG_PRINT("Inflating up to %u bytes\n", maxsize);
while ((err = inflate(&s, Z_NO_FLUSH)) != Z_STREAM_END) {
if (err != Z_OK) RAISE(ZLIB_ERROR);
if (s.avail_in == 0) break; // Done
// If we're here, our output buffer isn't large enough
maxsize <<= 1; // double size
tmpbuf = *outbuf;
*outbuf = realloc(*outbuf, maxsize);
if (NULL == *outbuf) {
free(tmpbuf);
RAISE(OUT_OF_MEMORY);
}
s.next_out = *outbuf + s.total_out;
for (i = s.total_out; i < maxsize; i++)
(*outbuf)[i] = 0x55;
s.avail_out = maxsize - s.total_out;
LOG_PRINT("Grew to up to %u bytes\n", maxsize);
}
LOG_PRINT("Total inflated bytes: %lu\n", s.total_out);
*outlen = s.total_out;
if (inflateEnd(&s) != Z_OK) RAISE(ZLIB_ERROR);
return 0;
}
static uint8_t spgp_parse_compressed_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt) {
int algo;
spgp_packet_t *pkts;
uint8_t *decomp;
uint32_t decomp_len;
uint32_t didx;
if (NULL == msg || NULL == idx || length == 0 || NULL == pkt)
RAISE(INVALID_ARGS);
algo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
switch (algo) {
case 1:
LOG_PRINT("ZIP compressed packet\n");
spgp_zlib_decompress_buffer(msg+*idx, pkt->header->contentLength,
&decomp, &decomp_len, algo);
break;
case 2:
LOG_PRINT("ZLIB compressed packet\n");
spgp_zlib_decompress_buffer(msg+*idx, pkt->header->contentLength,
&decomp, &decomp_len, algo);
break;
default:
LOG_PRINT("Unsupported packet compression: %u\n", algo);
RAISE(FORMAT_UNSUPPORTED);
}
if (NULL == decomp) RAISE(DECRYPT_FAILED);
// Decode all the packets in this compressed packet
didx = 0;
pkts = spgp_packet_decode_loop(decomp, &didx, decomp_len);
if (NULL == pkts) RAISE(INCOMPLETE_PACKET);
// Add packets to the current chain
pkt->next = pkts;
pkts->prev = pkt;
free(decomp);
decomp = NULL;
// Progress index through current chain
*idx += pkt->header->contentLength;
return 0;
}
static uint8_t spgp_parse_encrypted_packet(uint8_t *msg,
uint32_t *idx,
uint32_t *length,
spgp_packet_t *pkt) {
spgp_packet_t *session_pkt;
spgp_session_pkt_t *session;
gcry_cipher_hd_t cipher_hd;
gcry_error_t err;
int version;
unsigned long blksize;
uint32_t encbytes;
uint32_t startidx;
uint8_t headerlen;
uint8_t is_done;
uint8_t is_partial;
if (NULL == msg || NULL == idx || *length == 0 || NULL == pkt)
RAISE(INVALID_ARGS);
version = msg[*idx];
SAFE_IDX_INCREMENT(*idx, *length);
// As of this writing, only version 1 exists
if (version != 1) RAISE(FORMAT_UNSUPPORTED);
session_pkt = spgp_find_session_packet(pkt);
if (NULL == session_pkt) {
LOG_PRINT("No session key found!\n");
RAISE(DECRYPT_FAILED);
}
session = session_pkt->c.session;
startidx = *idx;
is_done = 0;
is_partial = pkt->header->isPartial;
// Drop 1 from contentLength to account for version
encbytes = pkt->header->contentLength - 1;
err = gcry_cipher_open (&cipher_hd,
spgp_pgp_to_gcrypt_symmetric_algo(session->symAlgo),
GCRY_CIPHER_MODE_CFB,
(GCRY_CIPHER_SECURE | GCRY_CIPHER_ENABLE_SYNC |
GCRY_CIPHER_ENABLE_SYNC));
blksize = spgp_iv_length_for_symmetric_algo(session->symAlgo);
err |= gcry_cipher_setkey(cipher_hd, session->key, session->keylen);
err |= gcry_cipher_setiv(cipher_hd, 0, blksize);
if (err) RAISE(GCRY_ERROR);
// Since data packets can have partial length, we loop and decrypt
// here until the whole blasted thing is decrypted.
while (!is_done) {
err = gcry_cipher_decrypt(cipher_hd,
msg+*idx,
encbytes,
NULL,
0);
if (err) RAISE(GCRY_ERROR);
*idx += encbytes - 1; // increment to last byte of data
// Check if we're done
if (!(is_partial)) {
is_done = 1;
continue;
}
// We are processing a partial packet, so figure out next length
SAFE_IDX_INCREMENT(*idx, *length); // inc to first byte of header
encbytes = spgp_new_header_length(msg+*idx,
&(headerlen),
&(is_partial));
LOG_PRINT("%u more bytes\n", encbytes);
// Fuck everything and everyone. Partial lengths are the enemy of
// gentlemen. We shall kill it brutishly, by moving everything down.
// God help you if your buffer is massive.
//
// The issue here is that a packet can technically be massive -- like
// exabytes -- and that's still valid by the OpenPGP spec. You can't
// depend on the ability to create temporary buffers to stick this stuff
// in. But if you leave it in the original buffer, the sub-packets won't
// know if there are "partial body length" headers jammed in the middle of
// their data.
//
// The only proper solution is some sort of dynamic stream data structure,
// but it's too late for that now. memmove() will have to do. If your
// packet is gigabytes long, this is going to take a while.
memmove(msg+*idx, msg+*idx+headerlen-1, *length-(*idx+headerlen-1));
*length -= headerlen-1;
}
// Validate decryption with PGP's MDC doo-hickey.
if (memcmp(msg+startidx+blksize-2, msg+startidx+blksize, 2) != 0) {
LOG_PRINT("Decrypted data block fails validation!\n");
RAISE(DECRYPT_FAILED);
}
LOG_PRINT("Decrypt succeeded.\n");
// At this point, msg has been decrypted in place and now contains
// a bunch of packets. Since it was decoded in place, and since we're
// already in the middle of a packet decode loop, we can just teleport
// idx back to the beginning of the data and exit.
//
// Packet data starts blocksize+2 bytes above decrypted data. One block
// of random data, and 2 extra bytes of verification.
//
// Subtract 1 because startidx is the first byte of the decrypted packet,
// but the packet parser loop expects us to end on the last byte of the
// previous packet.
*idx = startidx + blksize + 2 - 1;
return 0;
}
static uint8_t spgp_parse_literal_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt) {
spgp_literal_pkt_t *literal = NULL;
uint32_t date;
uint32_t startidx;
uint8_t format;
LOG_PRINT("Parsing literal packet\n");
if (NULL == msg || NULL == idx || NULL == pkt || length == 0)
RAISE(INVALID_ARGS);
startidx = *idx;
pkt->c.literal = malloc(sizeof(*(pkt->c.literal)));
if (NULL == pkt->c.literal) RAISE(OUT_OF_MEMORY);
memset(pkt->c.literal, 0, sizeof(*(pkt->c.literal)));
literal = pkt->c.literal;
// Read the format of the message. This is ignored.
format = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
// Read the length byte of the fylename
literal->filenameLen = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
// Read the filename
literal->filename = malloc(literal->filenameLen + 1);
if (NULL == literal->filename) RAISE(OUT_OF_MEMORY);
memcpy(literal->filename, msg+*idx, literal->filenameLen);
literal->filename[literal->filenameLen] = '\0';
*idx += literal->filenameLen - 1;
SAFE_IDX_INCREMENT(*idx, length);
// Read the timestamp. This is ignored.
memcpy(&date, msg+*idx, sizeof(date));
*idx += 3;
SAFE_IDX_INCREMENT(*idx, length);
// Read the actual data in to buffer
literal->dataLen = pkt->header->contentLength - (*idx - startidx);
literal->data = malloc(literal->dataLen);
if (NULL == literal->data) RAISE(OUT_OF_MEMORY);
memcpy(literal->data, msg+*idx, literal->dataLen);
*idx += literal->dataLen - 1;
LOG_PRINT("Stored %u bytes\n", literal->dataLen);
return 0;
}
static uint8_t spgp_parse_signature_packet(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_packet_t *pkt) {
spgp_signature_pkt_t *sig;
spgp_literal_pkt_t *literal;
gcry_md_hd_t md;
uint32_t startidx, stopidx;
unsigned char *hash;
uint32_t totalLen;
LOG_PRINT("Parsing signature packet\n");
if (msg == NULL || idx == NULL || pkt == NULL || 0 == length)
RAISE(INVALID_ARGS);
pkt->c.signature = malloc(sizeof(*(pkt->c.signature)));
if (NULL == pkt->c.signature) RAISE(OUT_OF_MEMORY);
memset(pkt->c.signature, 0, sizeof(*(pkt->c.signature)));
sig = pkt->c.signature;
startidx = *idx;
sig->version = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
if (sig->version != 4) RAISE(FORMAT_UNSUPPORTED);
sig->type = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
sig->asymAlgo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
sig->hashAlgo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("Signature type 0x%X, algo 0x%X, hash 0x%X\n",
sig->type, sig->asymAlgo, sig->hashAlgo);
sig->hashedSubLength = ((msg[*idx] & 0xFF) << 8) | msg[*idx + 1];
*idx += 1;
SAFE_IDX_INCREMENT(*idx, length);
// skip hashed subpackets for now
*idx += sig->hashedSubLength - 1;
SAFE_IDX_INCREMENT(*idx, length);
stopidx = *idx;
sig->unhashedSubLength = ((msg[*idx] & 0xFF) << 8) | msg[*idx + 1];
*idx += 1;
SAFE_IDX_INCREMENT(*idx, length);
// skip unhashed subpackets for now
*idx += sig->unhashedSubLength - 1;
SAFE_IDX_INCREMENT(*idx, length);
sig->hashTest = ((msg[*idx] & 0xFF) << 8) | (msg[*idx + 1] & 0xFF);
*idx += 1;
SAFE_IDX_INCREMENT(*idx, length);
sig->mpiHead = spgp_read_mpi(msg, idx, length);
if (sig->asymAlgo == ASYM_ALGO_DSA) {
SAFE_IDX_INCREMENT(*idx, length);
sig->mpiHead->next = spgp_read_mpi(msg, idx, length);
}
// All data accounted for, idx incremented to the end
// We can exit cleanly any time after this point
if (NULL == pkt->prev ||
pkt->prev->header->type != PKT_TYPE_LITERAL_DATA ||
NULL == pkt->prev->c.literal ||
NULL == pkt->prev->c.literal->data)
return -1;
literal = pkt->prev->c.literal;
if (gcry_md_open (&md, GCRY_MD_SHA1, 0) != 0) RAISE(GCRY_ERROR);
gcry_md_write (md, literal->data, literal->dataLen);
gcry_md_write (md, msg+startidx, stopidx-startidx);
/* They hide this shit in here because they hate us. What's really great
is that the length will always be (hashedSubLength+6), and hashedSubLength
is a 16-bit int, so in the worst case this final length would only be 3
bytes. Top byte in this 32-bit int will always be zero.
RFC 4880 - 5.2.4
V4 signatures also hash in a final trailer of six octets: the
version of the Signature packet, i.e., 0x04; 0xFF; and a four-octet,
big-endian number that is the length of the hashed data from the
Signature packet (note that this number does not include these final
six octets).*/
totalLen = sig->hashedSubLength + 6;
gcry_md_putc(md, sig->version);
gcry_md_putc(md, 0xFF);
gcry_md_putc(md, totalLen >> 24);
gcry_md_putc(md, totalLen >> 16);
gcry_md_putc(md, totalLen >> 8);
gcry_md_putc(md, totalLen >> 0);
gcry_md_final(md);
hash = gcry_md_read(md, 0);
return 0;
}
static spgp_packet_t *spgp_find_session_packet(spgp_packet_t *chain) {
spgp_packet_t *cur;
if (NULL == chain) RAISE(INVALID_ARGS);
cur = chain;
while (cur) {
if (cur->header->type == PKT_TYPE_SESSION &&
cur->c.session->key != NULL)
return cur;
cur = cur->prev;
}
return NULL;
}
static uint8_t spgp_parse_session_packet(uint8_t *msg, uint32_t *idx,
uint32_t length, spgp_packet_t *pkt) {
spgp_session_pkt_t *session;
spgp_packet_t *key, *chain;
gcry_sexp_t sexp_key, sexp_data, sexp_result;
gcry_mpi_t mpis[10], mpi_result;
spgp_mpi_t *cur;
uint32_t checksum, sum;
int i,mpi_count;
unsigned long frame_len;
uint8_t *frame;
LOG_PRINT("Parsing session packet.\n");
if (NULL == msg || NULL == idx || length == 0 || NULL == pkt)
RAISE(INVALID_ARGS);
// Make sure we have enough bytes remaining for parsing
if (length - *idx < pkt->header->contentLength) RAISE(BUFFER_OVERFLOW);
// Allocate a session packet
pkt->c.session = malloc(sizeof(*(pkt->c.session)));
if (NULL == pkt->c.session) RAISE(OUT_OF_MEMORY);
memset(pkt->c.session, 0, sizeof(*(pkt->c.session)));
session = pkt->c.session;
session->version = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("Version: %u\n", session->version);
memcpy(session->keyid, msg+*idx, 8);
*idx += 7;
SAFE_IDX_INCREMENT(*idx, length);
LOG_PRINT("Session for key ID: ");
for (i = 0; i < 8; i++) printf("%.2X",session->keyid[i]);
printf("\n");
session->algo = msg[*idx];
SAFE_IDX_INCREMENT(*idx, length);
// Read first MPI. RSA only has one
session->mpi1 = spgp_read_mpi(msg, idx, length);
// Elgamal has a second MPI
if (session->algo == ASYM_ALGO_ELGAMAL) {
SAFE_IDX_INCREMENT(*idx, length);
session->mpi2 = spgp_read_mpi(msg, idx, length);
}
// DONE READING FROM STREAM AT THIS POINT
// BELOW HERE -- DECRYPT SESSION KEY
if (!spgp_keychain_is_valid()) RAISE(KEYCHAIN_ERROR);
spgp_keychain_iter_start();
while ((chain = spgp_keychain_iter_next()) != NULL) {
if ((key = spgp_secret_key_matching_id(chain, session->keyid)) != NULL) {
LOG_PRINT("Found a matching key in keychain.\n");
break;
}
}
spgp_keychain_iter_end();
if (!key) return -1;
for (cur=key->c.pub->mpiHead,i = 0; cur != NULL; cur = cur->next,i++) {
gcry_mpi_scan (&(mpis[i]), GCRYMPI_FMT_PGP, cur->data, cur->count+2, NULL);
}
gcry_mpi_scan (&(mpis[i++]), GCRYMPI_FMT_PGP,
session->mpi1->data, session->mpi1->count+2, NULL);
if (session->mpi2) {
gcry_mpi_scan (&(mpis[i++]), GCRYMPI_FMT_PGP,
session->mpi2->data, session->mpi2->count+2, NULL);
}
mpi_count = i;
switch (session->algo) {
case ASYM_ALGO_RSA:
gcry_sexp_build(&sexp_key, NULL,
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
mpis[0], mpis[1], mpis[2], mpis[3], mpis[4], mpis[5]);
gcry_sexp_build (&sexp_data, NULL,
"(enc-val(rsa(a%m)))", mpis[6]);
gcry_pk_decrypt (&sexp_result, sexp_data, sexp_key);
mpi_result = gcry_sexp_nth_mpi (sexp_result, 0, GCRYMPI_FMT_STD);
break;
case ASYM_ALGO_ELGAMAL:
gcry_sexp_build(&sexp_key, NULL,
"(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
mpis[0], mpis[1], mpis[2], mpis[3]);
gcry_sexp_build (&sexp_data, NULL,
"(enc-val(elg(a%m)(b%m)))", mpis[4], mpis[5]);
gcry_pk_decrypt (&sexp_result, sexp_data, sexp_key);
mpi_result = gcry_sexp_nth_mpi (sexp_result, 0, GCRYMPI_FMT_STD);
break;
default:
RAISE(FORMAT_UNSUPPORTED);
}
if (!mpi_result) RAISE(GCRY_ERROR);
gcry_mpi_print(GCRYMPI_FMT_PGP, NULL, 0, &frame_len, mpi_result);
frame = malloc(frame_len);
if (NULL == frame) RAISE(OUT_OF_MEMORY);
gcry_mpi_print(GCRYMPI_FMT_PGP, frame, frame_len, NULL, mpi_result);
if (mpi_result) {gcry_mpi_release(mpi_result);}
if (sexp_key) {gcry_sexp_release(sexp_key);}
if (sexp_data) {gcry_sexp_release(sexp_data);}
if (sexp_result) {gcry_sexp_release(sexp_result);}
for (i = 0; i < mpi_count; i++) {
gcry_mpi_release(mpis[i]);
}
i = 2; // skip first two bytes, they're the length of the mpi
if (frame[i++] != 2) RAISE(DECRYPT_FAILED);
while (frame[i++] != 0 && i < frame_len) ; // Find the next 0 in frame
// Algorithm is first byte after the 0
session->symAlgo = frame[i];
// Key length is determined from current index. Drop 3 bytes: 1 for
// algorithm, and 2 for the checksum at the end.
session->keylen = frame_len - i - 3;
i++;
// Actual session key is the remaining bytes, except for the last two
session->key = malloc(session->keylen);
if (NULL == session->key) RAISE(OUT_OF_MEMORY);
if (i+session->keylen >= frame_len) RAISE(DECRYPT_FAILED);
memcpy(session->key, frame+i, session->keylen);
// Checksum is last two bytes in buffer
checksum = frame[frame_len-2]<<8 | frame[frame_len-1];
// Verify checksum
sum = 0;
for (i = 0; i < session->keylen; i++) {
sum = sum + (session->key[i] & 0xFF);
}
if (sum % 65536 != checksum) {
LOG_PRINT("Session key checksum failed!\n");
RAISE(DECRYPT_FAILED);
}
free(frame);
frame = NULL;
LOG_PRINT("Decrypted session key.\n");
return 0;
}
/**
* Find a specific secret key in a chain of packets.
*
* Finds a secret key in the given chain of packets, |chain|, with a key ID
* matching |keyid|. KeyID is the last 8 octets of the 64-octet key
* fingerprint.
*
* @param chain Chain of packets containing at least one secret key
* @param keyid 8-octet key ID
* @return Packet containing matching secret key, or NULL if not found
*
*/
static spgp_packet_t *spgp_secret_key_matching_id(spgp_packet_t *chain,
uint8_t *keyid) {
spgp_packet_t *cur = NULL;
if (NULL == chain || NULL == keyid) RAISE(INVALID_ARGS);
cur = chain;
while ((cur = spgp_next_secret_key_packet(cur)) != NULL) {
if (memcmp((void*)((cur->c.pub->fingerprint)+12),keyid,8) == 0)
return cur;
cur = cur->next;
}
return NULL;
}
static uint8_t spgp_read_salt(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_secret_pkt_t *secret) {
uint8_t saltLen = 0;
if (NULL == msg || NULL == idx || 0 == length || NULL == secret)
RAISE(INVALID_ARGS);
if ((saltLen = spgp_salt_length_for_hash_algo(secret->s2kHashAlgo)) == 0)
RAISE(FORMAT_UNSUPPORTED);
if (length - *idx < saltLen) RAISE(BUFFER_OVERFLOW);
secret->s2kSalt = malloc(sizeof(*(secret->s2kSalt)) * saltLen);
if (NULL == secret->s2kSalt) RAISE(OUT_OF_MEMORY);
secret->s2kSaltLength = saltLen;
memcpy(secret->s2kSalt, msg+*idx, saltLen);
*idx += saltLen-1;
SAFE_IDX_INCREMENT(*idx, length);
return 0;
}
static uint8_t spgp_read_iv(uint8_t *msg,
uint32_t *idx,
uint32_t length,
spgp_secret_pkt_t *secret) {
uint8_t ivLen = 0;
if (NULL == msg || NULL == idx || 0 == length || NULL == secret)
RAISE(INVALID_ARGS);
if ((ivLen = spgp_iv_length_for_symmetric_algo(secret->s2kEncryption)) == 0)
RAISE(FORMAT_UNSUPPORTED);
if (length - *idx < ivLen) RAISE(BUFFER_OVERFLOW);
secret->iv = malloc(sizeof(*(secret->iv)) * ivLen);
if (NULL == secret->iv) RAISE(OUT_OF_MEMORY);
secret->ivLength = ivLen;
memcpy(secret->iv, msg+*idx, ivLen);
*idx += ivLen-1;
SAFE_IDX_INCREMENT(*idx, length);
return 0;
}