/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2013 T.Linden.
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 3 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, see .
You can contact me by mail: .
*/
#include "ed.h"
unsigned char * pcp_ed_verify(unsigned char *signature, size_t siglen, pcp_pubkey_t *p) {
unsigned char *message = ucmalloc(siglen - crypto_sign_BYTES);
size_t mlen;
if(crypto_sign_open(message, &mlen, signature, siglen, p->edpub) != 0) {
fatal("Failed to open the signature using the public key 0x%s!\n", p->id);
goto errve1;
}
return message;
errve1:
free(message);
return NULL;
}
unsigned char *pcp_ed_sign(unsigned char *message, size_t messagesize, pcp_key_t *s) {
size_t mlen = messagesize + crypto_sign_BYTES;
unsigned char *signature = ucmalloc(mlen);
crypto_sign(signature, &mlen, message, messagesize, s->edsecret);
return signature;
}
size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85) {
unsigned char in_buf[PCP_BLOCK_SIZE];
size_t cur_bufsize = 0;
crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state));
unsigned char hash[crypto_generichash_BYTES_MAX];
crypto_generichash_init(st, NULL, 0, 0);
if(z85)
fprintf(out, "%s\nHash: Blake2\n\n", PCP_SIG_HEADER);
while(!feof(in)) {
cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in);
if(cur_bufsize <= 0)
break;
crypto_generichash_update(st, in_buf, cur_bufsize);
fwrite(in_buf, cur_bufsize, 1, out);
}
if(ferror(out) != 0) {
fatal("Failed to write encrypted output!\n");
free(st);
return 0;
}
crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX);
size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX;
unsigned char *signature = ucmalloc(mlen);
crypto_sign(signature, &mlen, hash, crypto_generichash_BYTES_MAX, s->edsecret);
if(z85) {
fprintf(out, "\n%s\nVersion: PCP v%d.%d.%d\n\n", PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH);
size_t zlen;
char *z85encoded = pcp_z85_encode((unsigned char*)signature, crypto_sign_BYTES, &zlen);
fprintf(out, "%s\n%s\n", z85encoded, PCP_SIG_END);
}
else {
fwrite(signature, crypto_sign_BYTES, 1, out);
}
if(fileno(in) != 0)
fclose(in);
if(fileno(out) != 1)
fclose(out);
free(st);
return mlen; // ???
}
unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) {
unsigned char in_buf[PCP_BLOCK_SIZE];
size_t cur_bufsize = 0;
int z85 = 0;
crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state));
unsigned char hash[crypto_generichash_BYTES_MAX];
char *zhead;
size_t hlen = strlen(PCP_SIG_HEADER);
size_t nextsize = 0;
size_t restsize = 0;
size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX;
unsigned char z85encoded[181];
unsigned char sighash[mlen];
unsigned char sig[crypto_sign_BYTES];
char z85sigstart[] = PCP_SIG_START;
char binsigstart[] = PCP_SIGPREFIX;
int offset = -1;
crypto_generichash_init(st, NULL, 0, 0);
// determine sig type, clear or bin
cur_bufsize = hlen + 14; // header + hash name + 3x newline
fread(&in_buf, 1, cur_bufsize, in);
zhead = ucmalloc(cur_bufsize);
memcpy(zhead, in_buf, hlen);
memset(&zhead[hlen], 0, 1);
if(strncmp(zhead, PCP_SIG_HEADER, hlen) == 0)
z85 = 1;
else
nextsize = cur_bufsize;
while(!feof(in)) {
if(z85 == 0 && nextsize > 0) {
// bin sig, read blocksize - header and put zheader in front of it again
cur_bufsize = fread(&in_buf[hlen], 1, PCP_BLOCK_SIZE - hlen, in);
memcpy(in_buf, zhead, hlen);
cur_bufsize += hlen;
}
else
cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in);
if(cur_bufsize <= 0)
break;
if(z85) {
// look if we need to cut something from the current block
offset = _findoffset(in_buf, cur_bufsize, z85sigstart, strlen(z85sigstart));
if(offset > 0) {
// we need to cut
restsize = cur_bufsize - offset; // the start of the armor sig
cur_bufsize -= restsize; // bin stuff in front of it, if any
memcpy(z85encoded, &in_buf[offset], restsize); // save the armor sig chunk
}
}
else {
// do the same for the bin sig
offset = _findoffset(in_buf, cur_bufsize, binsigstart, strlen(binsigstart));
if(offset > 0) {
// we need to cut
restsize = cur_bufsize - offset + strlen(binsigstart); // the start of the bin sig
cur_bufsize -= offset; // bin stuff in front of it, if any
memcpy(sighash, &in_buf[offset + strlen(binsigstart)], restsize); // save the armor sig chunk
}
}
if(offset == -1)
crypto_generichash_update(st, in_buf, cur_bufsize);
}
if(offset == -1) {
fatal("Error, the signature doesn't contain the ed25519 signed hash\n");
goto errvb1;
}
crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX);
// pull in the remainder
cur_bufsize = fread(&in_buf, 1, restsize, in);
// put hashsig together
if(z85) {
memcpy(&z85encoded[restsize], in_buf, cur_bufsize);
char *z85block = pcp_readz85string(z85encoded, restsize + cur_bufsize);
if(z85block == NULL)
goto errvb1;
size_t dstlen;
unsigned char *z85decoded = pcp_z85_decode(z85block, &dstlen);
if(dstlen != mlen) {
fatal("z85 decoded signature didn't result in a proper signed hash\n");
goto errvb1;
}
memcpy(sighash, z85decoded, mlen);
}
else {
memcpy(&sighash[restsize], in_buf, cur_bufsize); // FIXME: check if cur_bufsize holds enough
}
// huh, how did we made it til here?
unsigned char *verifiedhash = NULL;
if(p == NULL) {
pcphash_iteratepub(p) {
verifiedhash = pcp_ed_verify(sighash, mlen, p);
if(verifiedhash != NULL)
break;
}
}
else {
verifiedhash = pcp_ed_verify(sighash, mlen, p);
}
if(verifiedhash == NULL)
goto errvb1;
if(memcmp(verifiedhash, hash, crypto_generichash_BYTES_MAX) != 0) {
// sig verified, but the hash doesn't
fatal("signed hash doesn't match with actual hash of signed file content\n");
free(verifiedhash);
}
return verifiedhash;
errvb1:
free(st);
free(zhead);
return NULL;
}