continue new pk-expoert format

This commit is contained in:
TLINDEN
2014-02-08 20:35:34 +01:00
parent 8b19871046
commit b9841bfb06
10 changed files with 360 additions and 161 deletions

View File

@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2014 T.v.Dein.
Copyright (C) 2013-2014 T.v.Dein.
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
@@ -69,7 +69,10 @@ void buffer_resize(Buffer *b, size_t len);
/* get some chunk of data from the buffer, starting from offset til len,
it doesn't allocate the returned data (void *buf, the 2nd argument).
*/
size_t buffer_get(Buffer *b, void *buf, size_t len);
size_t buffer_get_chunk(Buffer *b, void *buf, size_t len);
/* return the whole buffer contents */
unsigned char *buffer_get(Buffer *b);
/* same as buffer_get() but fetch some data chunk from somewhere
in the middle of the buffer */

View File

@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2014 T.v.Dein.
Copyright (C) 2013-2014 T.v.Dein.
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
@@ -38,7 +38,6 @@
#include "uthash.h"
#include "jenhash.h"
#include "scrypt.h"
#include "buffer.h"
/*
PCP private key structure. Most fields are self explanatory.
@@ -143,75 +142,6 @@ typedef struct _pcp_rec_t pcp_rec_t;
/* RFC4880 alike public key export with some simplifications:
In sig subpackets we're using fixed sized fields instead
of the mess they use in rfc4880. Sorry. We use only these types:
2 = Signature Creation Time (4 byte)
3 = Signature Expiration Time (4 byte)
9 = Key Expiration Time (4 bytes)
20 = Notation Data (4 byte flags, N bytes name+value)
27 = Key Flags (1 byte, use 0x02, 0x08 and 0x80
The actual signature field doesn't contain the 1st 16 bits
of the hash, since crypto_sign() created signatures consist
of the hash+signature anyway.
So, a full pubkey export looks like this
version
ctime
cipher
3 x raw keys \
sigheader > calc hash from this
sigsubs (header+data) /
hash
signature
We use big-endian always.
http://tools.ietf.org/html/rfc4880#section-5.2.3
*/
struct _pcp_rfc_pubkey_header_t {
uint8_t version;
uint32_t ctime;
uint8_t cipher;
};
struct _pcp_rfc_pubkey_0x21_t {
byte sig_ed25519_pub[crypto_sign_PUBLICKEYBYTES];
byte ed25519_pub[crypto_sign_PUBLICKEYBYTES];
byte curve25519_pub[crypto_box_PUBLICKEYBYTES];
};
struct _pcp_rfc_pubkey_sigheader_0x21_t {
uint8_t version;
uint8_t type; /* 0x1F only, self signed */
uint8_t pkcipher;
uint8_t hashcipher;
uint16_t numsubs;
};
struct _pcp_rfc_pubkey_sigsub_0x21_t {
uint32_t size;
uint8_t type;
};
struct _pcp_rfc_pubkey_sig_0x21_t {
byte signature[crypto_generichash_BYTES_MAX + crypto_sign_BYTES];
};
typedef struct _pcp_rfc_pubkey_header_t rfc_pub_h;
typedef struct _pcp_rfc_pubkey_0x21_t rfc_pub_k;
typedef struct _pcp_rfc_pubkey_sigheader_0x21_t rfc_pub_sig_h;
typedef struct _pcp_rfc_pubkey_sigsub_0x21_t rfc_pub_sig_s;
typedef struct _pcp_rfc_pubkey_sig_0x21_t rfc_pub_sig;
void pcp_cleanhashes();
pcp_key_t *pcpkey_new ();
@@ -256,4 +186,5 @@ int pcp_sanitycheck_pub(pcp_pubkey_t *key);
int pcp_sanitycheck_key(pcp_key_t *key);
#endif /* _HAVE_PCP_KEYPAIR_H */

134
include/pcp/mgmt.h Normal file
View File

@@ -0,0 +1,134 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2014 T.v.Dein.
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 <http://www.gnu.org/licenses/>.
You can contact me by mail: <tom AT vondein DOT org>.
*/
#ifndef _HAVE_PCP_MGMT_H
#define _HAVE_PCP_MGMT_H
#include <sodium.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "defines.h"
#include "platform.h"
#include "mem.h"
#include "ed.h"
#include "key.h"
#include "buffer.h"
/* RFC4880 alike public key export with some simplifications:
In sig subpackets we're using fixed sized fields instead
of the mess they use in rfc4880. Sorry. We use only these types:
2 = Signature Creation Time (4 byte)
3 = Signature Expiration Time (4 byte)
9 = Key Expiration Time (4 bytes)
20 = Notation Data (4 byte flags, N bytes name+value)
27 = Key Flags (1 byte, use 0x02, 0x08 and 0x80
The actual signature field doesn't contain the 1st 16 bits
of the hash, since crypto_sign() created signatures consist
of the hash+signature anyway.
So, a full pubkey export looks like this
version
ctime
cipher
3 x raw keys \
sigheader > calc hash from this
sigsubs (header+data) /
hash
signature
We use big-endian always.
http://tools.ietf.org/html/rfc4880#section-5.2.3
*/
struct _pcp_rfc_pubkey_header_t {
uint8_t version;
uint32_t ctime;
uint8_t cipher;
};
struct _pcp_rfc_pubkey_0x21_t {
byte sig_ed25519_pub[crypto_sign_PUBLICKEYBYTES];
byte ed25519_pub[crypto_sign_PUBLICKEYBYTES];
byte curve25519_pub[crypto_box_PUBLICKEYBYTES];
};
struct _pcp_rfc_pubkey_sigheader_0x21_t {
uint8_t version;
uint8_t type;
uint8_t pkcipher;
uint8_t hashcipher;
uint16_t numsubs;
};
struct _pcp_rfc_pubkey_sigsub_0x21_t {
uint32_t size;
uint8_t type;
};
struct _pcp_rfc_pubkey_sig_0x21_t {
byte signature[crypto_generichash_BYTES_MAX + crypto_sign_BYTES];
};
typedef struct _pcp_rfc_pubkey_header_t rfc_pub_h;
typedef struct _pcp_rfc_pubkey_0x21_t rfc_pub_k;
typedef struct _pcp_rfc_pubkey_sigheader_0x21_t rfc_pub_sig_h;
typedef struct _pcp_rfc_pubkey_sigsub_0x21_t rfc_pub_sig_s;
typedef struct _pcp_rfc_pubkey_sig_0x21_t rfc_pub_sig;
#define EXP_PK_CIPHER 0x21
#define EXP_PK_CIPHER_NAME "CURVE25519-ED25519-POLY1305-SALSA20"
#define EXP_HASH_CIPHER 0x22
#define EXP_HASH_NAME "BLAKE2"
#define EXP_SIG_CIPHER 0x23
#define EXP_SIG_CIPHER_NAME "ED25519"
#define EXP_SIG_VERSION 0x01
#define EXP_SIG_TYPE 0x1F /* self signed */
/* sig sub notiation we support */
#define EXP_SIG_SUB_CTIME 2
#define EXP_SIG_SUB_SIGEXPIRE 3
#define EXP_SIG_SUB_KEYEXPIRE 9
#define EXP_SIG_SUB_NOTATION 20
#define EXP_SIG_SUB_KEYFLAGS 27
/* in armored mode, we're using the usual head+foot */
#define EXP_PK_HEADER "-----BEGIN ED25519-CURVE29915 PUBLIC KEY-----"
#define EXP_PK_FOOTER "------END ED25519-CURVE29915 PUBLICKEY------"
/* export public key */
Buffer *pcp_get_rfc_pub (pcp_pubkey_t *key, pcp_key_t *sk);
#endif // _HAVE_PCP_MGMT_H

View File

@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2014 T.v.Dein.
Copyright (C) 2013-2014 T.v.Dein.
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
@@ -65,7 +65,14 @@ void buffer_resize(Buffer *b, size_t len) {
}
}
size_t buffer_get(Buffer *b, void *buf, size_t len) {
unsigned char *buffer_get(Buffer *b) {
if(b->end > 0)
return b->buf;
else
return NULL;
}
size_t buffer_get_chunk(Buffer *b, void *buf, size_t len) {
if(len > b->end - b->offset || len == 0) {
fatal("[buffer %s] attempt to read %ld data from buffer with size %ld %p at offset %ld",
len, b->size, b->offset);

View File

@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2013 T.Linden.
Copyright (C) 2013-2014 T.v.Dein.
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
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact me by mail: <tlinden AT cpan DOT org>.
You can contact me by mail: <tom AT vondein DOT org>.
*/

View File

@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2013 T.Linden.
Copyright (C) 2013-2014 T.v.Dein.
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
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact me by mail: <tlinden AT cpan DOT org>.
You can contact me by mail: <tom AT vondein DOT org>.
*/
@@ -394,70 +394,3 @@ int pcp_sanitycheck_key(pcp_key_t *key) {
return 0;
}
int pcp_get_rfc_pub (pcp_pubkey_t *key) {
Buffer *out = buffer_new(1024, "bo1");
Buffer *raw = buffer_new(1024, "bs1");
/* add the header */
buffer_add8(out, PCP_KEY_VERSION);
buffer_add32(out, key->ctime);
buffer_add8(out, PCP_RFC_CIPHER);
/* add the keys */
buffer_add(raw, key->edpub, 32);
buffer_add(raw, key->edpub, 32);
buffer_add(raw, key->pub, 32);
/* add the sig header */
buffer_add8(raw, PCP_KEY_VERSION);
buffer_add8(raw, 0x1F); // FIXME: define
buffer_add8(raw, PCP_RFC_CIPHER);
buffer_add8(raw, PCP_RFC_CIPHER);
buffer_add16(raw, 5);
/* add sig ctime */
buffer_add32be(raw, 4);
buffer_add8(raw, 2);
buffer_add32be(raw, time(0));
/* add sig expire time */
buffer_add32be(raw, 4);
buffer_add8(raw, 3);
buffer_add32be(raw, time(0) + 31536000);
/* add key expire time */
buffer_add32be(raw, 4);
buffer_add8(raw, 9);
buffer_add32be(raw, key->ctime);
/* add name */
size_t notation_size = strlen(key->owner) + 4 + 5;
buffer_add32be(raw, notation_size);
buffer_add8(raw, 20);
buffer_add16be(raw, 5);
buffer_add16be(raw, strlen(key->owner));
buffer_add(raw, "owner", 5);
buffer_add(raw, key->owner, strlen(key->owner));
/* add mail */
notation_size = strlen(key->mail) + 4 + 4;
buffer_add32be(raw, notation_size);
buffer_add8(raw, 20);
buffer_add16be(raw, 4);
buffer_add16be(raw, strlen(key->mail));
buffer_add(raw, "mail", 4);
buffer_add(raw, key->mail, strlen(key->mail));
/* add key flags */
buffer_add32be(raw, 1);
buffer_add8(raw, 27);
buffer_add8(raw, 0x02 & 0x08 & 0x80);
/* FIXME:
Now, calculate the signature from the raw buffer,
add it to the output buffer, add the sig to the
output buffer and finally return it. */
return 0;
}

110
libpcp/mgmt.c Normal file
View File

@@ -0,0 +1,110 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
Copyright (C) 2013-2014 T.v.Dein.
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 <http://www.gnu.org/licenses/>.
You can contact me by mail: <tom AT vondein DOT org>.
*/
#include "mgmt.h"
Buffer *pcp_get_rfc_pub (pcp_pubkey_t *key, pcp_key_t *sk) {
Buffer *out = buffer_new(320, "bo1");
Buffer *raw = buffer_new(256, "bs1");
/* add the header */
buffer_add8(out, PCP_KEY_VERSION);
buffer_add32(out, key->ctime);
buffer_add8(out, EXP_PK_CIPHER);
/* add the keys */
buffer_add(raw, key->edpub, 32);
buffer_add(raw, key->edpub, 32);
buffer_add(raw, key->pub, 32);
/* add the sig header */
buffer_add8(raw, EXP_SIG_VERSION);
buffer_add8(raw, EXP_SIG_TYPE);
buffer_add8(raw, EXP_SIG_CIPHER);
buffer_add8(raw, EXP_HASH_CIPHER);
buffer_add16(raw, 5); /* we add 5 sub sigs always */
/* add sig ctime */
buffer_add32be(raw, 4);
buffer_add8(raw, EXP_SIG_SUB_CTIME);
buffer_add32be(raw, time(0));
/* add sig expire time */
buffer_add32be(raw, 4);
buffer_add8(raw, EXP_SIG_SUB_SIGEXPIRE);
buffer_add32be(raw, time(0) + 31536000);
/* add key expire time */
buffer_add32be(raw, 4);
buffer_add8(raw, EXP_SIG_SUB_KEYEXPIRE);
buffer_add32be(raw, key->ctime + 31536000);
/* add name notation sub*/
size_t notation_size = strlen(key->owner) + 4 + 5;
buffer_add32be(raw, notation_size);
buffer_add8(raw, EXP_SIG_SUB_NOTATION);
buffer_add16be(raw, 5);
buffer_add16be(raw, strlen(key->owner));
buffer_add(raw, "owner", 5);
buffer_add(raw, key->owner, strlen(key->owner));
/* add mail notation sub */
notation_size = strlen(key->mail) + 4 + 4;
buffer_add32be(raw, notation_size);
buffer_add8(raw, EXP_SIG_SUB_NOTATION);
buffer_add16be(raw, 4);
buffer_add16be(raw, strlen(key->mail));
buffer_add(raw, "mail", 4);
buffer_add(raw, key->mail, strlen(key->mail));
/* add key flags */
buffer_add32be(raw, 1);
buffer_add8(raw, 27);
buffer_add8(raw, 0x02 & 0x08 & 0x80);
/* create a hash from the PK material and the raw signature packet */
crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state));
unsigned char *hash = ucmalloc(crypto_generichash_BYTES_MAX);
crypto_generichash_init(st, NULL, 0, 0);
crypto_generichash_update(st, buffer_get(raw), buffer_size(raw));
crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX);
/* sign the hash */
unsigned char *sig = pcp_ed_sign(hash, crypto_generichash_BYTES_MAX, sk->secret);
/* append the signature packet to the output */
buffer_add(out, buffer_get(raw), buffer_size(raw));
/* append the signed hash */
buffer_add(out, sig, crypto_generichash_BYTES_MAX + crypto_generichash_BYTES_MAX);
/* and that's it. wasn't that easy? :) */
buffer_free(raw);
memset(hash, 0, crypto_generichash_BYTES_MAX);
free(hash);
memset(sig, 0, crypto_generichash_BYTES_MAX + crypto_generichash_BYTES_MAX);
free(sig);
return out;
}

View File

@@ -336,7 +336,7 @@ int pcp_importsecret (vault_t *vault, FILE *in) {
}
if(clen != PCP_RAW_KEYSIZE) {
fatal("Error: decoded input didn't result to a proper sized key! (got %d bytes)\n", clen);
fatal("Error: decoded input didn't result to a proper sized key! (got %ld bytes, expected %ld)\n", clen, PCP_RAW_KEYSIZE);
free(z85decoded);
return 1;
}
@@ -624,3 +624,69 @@ char *pcp_find_id_byrec(char *recipient) {
return id;
}
/*
Experimental RFC4880-alike public key export. Once stable and
flexible enough, this will become the PCP default, I hope. */
void pcp_exportpublic2(char *passwd, char *outfile, int armor) {
pcp_pubkey_t *key = NULL;
pcp_key_t *s = NULL;
s = pcp_find_primary_secret();
if(s == NULL) {
fatal("There's no primary secret key in the vault %s!\n", vault->filename);
free(s);
}
else {
key = pcpkey_pub_from_secret(s);
}
if(key != NULL) {
FILE *out;
if(outfile == NULL) {
out = stdout;
}
else {
if((out = fopen(outfile, "wb+")) == NULL) {
fatal("Could not create output file %s", outfile);
out = NULL;
}
}
if(out != NULL) {
pcp_key_t *sk = pcp_find_primary_secret();
if(sk != NULL) {
char *passphrase;
pcp_readpass(&passphrase, "Enter passphrase to decrypt your secret key for signing the export", NULL, 1);
sk = pcpkey_decrypt(sk, passphrase);
if(sk != NULL) {
Buffer *exported_pk = pcp_get_rfc_pub(key, sk);
if(exported_pk != NULL) {
if(armor == 1) {
size_t zlen;
char *z85 = pcp_z85_encode(buffer_get(exported_pk), buffer_size(exported_pk), &zlen);
fprintf(out, "%s\r\n%s\r\n%s\r\n", EXP_PK_HEADER, z85, EXP_PK_FOOTER);
free(z85);
}
else
fwrite(buffer_get(exported_pk), 1, buffer_size(exported_pk), out);
fclose(out);
if(debug) {
buffer_dump(exported_pk);
buffer_info(exported_pk);
pcp_dumppubkey(key);
}
buffer_free(exported_pk);
}
}
free(passphrase);
}
free(key);
}
}
}

View File

@@ -40,6 +40,8 @@
#include "keyhash.h"
#include "util.h"
#include "base85.h"
#include "buffer.h"
#include "mgmt.h"
#define _WITH_GETLINE
@@ -58,4 +60,7 @@ int pcp_importsecret (vault_t *vault, FILE *in);
void pcpdelete_key(char *keyid);
char *pcp_find_id_byrec(char *recipient);
/* Experimental: new rfc4880 style pk export */
void pcp_exportpublic2(char *passwd, char *outfile, int armor);
#endif /* _HAVE_KEYMGMT_H */

View File

@@ -46,7 +46,7 @@ char *default_vault() {
}
int main (int argc, char **argv) {
int opt, mode, usevault, useid, userec, lo, armor, detach, signcrypt, pbpcompat;
int opt, mode, usevault, useid, userec, lo, armor, detach, signcrypt, pbpcompat, rfc;
char *vaultfile = default_vault();
char *outfile = NULL;
char *infile = NULL;
@@ -70,6 +70,7 @@ int main (int argc, char **argv) {
detach = 0;
signcrypt = 0;
pbpcompat = 0;
rfc = 0;
static struct option longopts[] = {
/* generics */
@@ -92,6 +93,7 @@ int main (int argc, char **argv) {
{ "edit-key", no_argument, NULL, 'E' },
{ "export-yaml", no_argument, NULL, 'y' },
{ "pbpcompat", no_argument, NULL, 'b' },
{ "rfc-format", no_argument, NULL, '1' }, /* no short option */
/* crypto */
{ "encrypt", no_argument, NULL, 'e' },
@@ -114,7 +116,7 @@ int main (int argc, char **argv) {
{ NULL, 0, NULL, 0 }
};
while ((opt = getopt_long(argc, argv, "klV:vdehsO:i:I:pSPRtEx:DzZr:gcymf:b",
while ((opt = getopt_long(argc, argv, "klV:vdehsO:i:I:pSPRtEx:DzZr:gcymf:b1",
longopts, NULL)) != -1) {
switch (opt) {
@@ -151,6 +153,9 @@ int main (int argc, char **argv) {
mode += PCP_MODE_IMPORT_SECRET;
usevault = 1;
break;
case '1':
rfc = 1;
break;
case 'R':
mode += PCP_MODE_DELETE_KEY;
usevault = 1;
@@ -372,19 +377,24 @@ int main (int argc, char **argv) {
break;
case PCP_MODE_EXPORT_PUBLIC:
if(useid) {
id = pcp_normalize_id(keyid);
if(id == NULL)
break;
if(rfc) {
pcp_exportpublic2(xpass, outfile, armor);
}
else {
if(useid) {
id = pcp_normalize_id(keyid);
if(id == NULL)
break;
}
if (recipient != NULL)
pcp_exportpublic(id, recipient->value, xpass, outfile, pbpcompat);
else
pcp_exportpublic(id, NULL, xpass, outfile, pbpcompat);
if(xpass != NULL)
free(xpass);
if(recipient != NULL)
free(recipient);
}
if (recipient != NULL)
pcp_exportpublic(id, recipient->value, xpass, outfile, pbpcompat);
else
pcp_exportpublic(id, NULL, xpass, outfile, pbpcompat);
if(xpass != NULL)
free(xpass);
if(recipient != NULL)
free(recipient);
break;
case PCP_MODE_IMPORT_PUBLIC: