From b9841bfb06d0c7f01ee0ad2791862819d8729cdd Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Sat, 8 Feb 2014 20:35:34 +0100 Subject: [PATCH] continue new pk-expoert format --- include/pcp/buffer.h | 7 ++- include/pcp/key.h | 73 +---------------------- include/pcp/mgmt.h | 134 +++++++++++++++++++++++++++++++++++++++++++ libpcp/buffer.c | 11 +++- libpcp/crypto.c | 4 +- libpcp/key.c | 71 +---------------------- libpcp/mgmt.c | 110 +++++++++++++++++++++++++++++++++++ src/keymgmt.c | 68 +++++++++++++++++++++- src/keymgmt.h | 5 ++ src/pcp.c | 38 +++++++----- 10 files changed, 360 insertions(+), 161 deletions(-) create mode 100644 include/pcp/mgmt.h create mode 100644 libpcp/mgmt.c diff --git a/include/pcp/buffer.h b/include/pcp/buffer.h index 737b7bb..61fe377 100644 --- a/include/pcp/buffer.h +++ b/include/pcp/buffer.h @@ -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 */ diff --git a/include/pcp/key.h b/include/pcp/key.h index c4bfe49..5d6faa9 100644 --- a/include/pcp/key.h +++ b/include/pcp/key.h @@ -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 */ diff --git a/include/pcp/mgmt.h b/include/pcp/mgmt.h new file mode 100644 index 0000000..be2a3b1 --- /dev/null +++ b/include/pcp/mgmt.h @@ -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 . + + You can contact me by mail: . +*/ + + +#ifndef _HAVE_PCP_MGMT_H +#define _HAVE_PCP_MGMT_H + +#include +#include +#include +#include + +#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 diff --git a/libpcp/buffer.c b/libpcp/buffer.c index 6df481c..4af89e3 100644 --- a/libpcp/buffer.c +++ b/libpcp/buffer.c @@ -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); diff --git a/libpcp/crypto.c b/libpcp/crypto.c index 95775bf..3664f27 100644 --- a/libpcp/crypto.c +++ b/libpcp/crypto.c @@ -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 . - You can contact me by mail: . + You can contact me by mail: . */ diff --git a/libpcp/key.c b/libpcp/key.c index 1dc178a..d63ed7f 100644 --- a/libpcp/key.c +++ b/libpcp/key.c @@ -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 . - You can contact me by mail: . + You can contact me by mail: . */ @@ -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; -} diff --git a/libpcp/mgmt.c b/libpcp/mgmt.c new file mode 100644 index 0000000..c5c3ad0 --- /dev/null +++ b/libpcp/mgmt.c @@ -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 . + + You can contact me by mail: . +*/ + + +#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; +} diff --git a/src/keymgmt.c b/src/keymgmt.c index 8283d23..4fccf77 100644 --- a/src/keymgmt.c +++ b/src/keymgmt.c @@ -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); + } + } +} diff --git a/src/keymgmt.h b/src/keymgmt.h index 49af972..f0d72e7 100644 --- a/src/keymgmt.h +++ b/src/keymgmt.h @@ -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 */ diff --git a/src/pcp.c b/src/pcp.c index 04dd714..b0eddbc 100644 --- a/src/pcp.c +++ b/src/pcp.c @@ -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: