mirror of
https://codeberg.org/scip/pcp.git
synced 2025-12-17 12:00:56 +01:00
finally got pbp key export/import to work. in order to make it happen, pbp needs to be patched (see pbp issue#10 for details!) to enable padding.
This commit is contained in:
@@ -108,8 +108,10 @@ struct _pcp_pubkey_t {
|
|||||||
/* keys.mp+keys.cp+keys.sp+keys.name */
|
/* keys.mp+keys.cp+keys.sp+keys.name */
|
||||||
struct _pbp_pubkey_t {
|
struct _pbp_pubkey_t {
|
||||||
byte sigpub[crypto_sign_PUBLICKEYBYTES];
|
byte sigpub[crypto_sign_PUBLICKEYBYTES];
|
||||||
byte pub[crypto_box_PUBLICKEYBYTES];
|
|
||||||
byte edpub[crypto_sign_PUBLICKEYBYTES];
|
byte edpub[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
byte pub[crypto_box_PUBLICKEYBYTES];
|
||||||
|
char iso_ctime[32];
|
||||||
|
char iso_expire[32];
|
||||||
char name[1024];
|
char name[1024];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static void prep_base85(void)
|
|||||||
int decode_85(char *dst, const char *buffer, int len)
|
int decode_85(char *dst, const char *buffer, int len)
|
||||||
{
|
{
|
||||||
prep_base85();
|
prep_base85();
|
||||||
|
int pos = 0;
|
||||||
say1("len: %d\n", len);
|
say1("len: %d\n", len);
|
||||||
say2("decode 85 <%.*s>\n", len / 4 * 5, buffer);
|
say2("decode 85 <%.*s>\n", len / 4 * 5, buffer);
|
||||||
while (len) {
|
while (len) {
|
||||||
@@ -47,13 +47,13 @@ int decode_85(char *dst, const char *buffer, int len)
|
|||||||
ch = *buffer++;
|
ch = *buffer++;
|
||||||
de = de85[ch];
|
de = de85[ch];
|
||||||
if (--de < 0)
|
if (--de < 0)
|
||||||
return error("invalid base85 alphabet <%c>\n", ch);
|
return error("invalid base85 alphabet <%02x> de: %d\n", ch, de);
|
||||||
acc = acc * 85 + de;
|
acc = acc * 85 + de;
|
||||||
} while (--cnt);
|
} while (--cnt);
|
||||||
ch = *buffer++;
|
ch = *buffer++;
|
||||||
de = de85[ch];
|
de = de85[ch];
|
||||||
if (--de < 0)
|
if (--de < 0 && ch != '\0')
|
||||||
return error("invalid base85 alphabet <%c>\n", ch);
|
return error("invalid base85 alphabet <%02x> left\n", ch);
|
||||||
|
|
||||||
|
|
||||||
/* Detect overflow. */
|
/* Detect overflow. */
|
||||||
@@ -64,7 +64,6 @@ int decode_85(char *dst, const char *buffer, int len)
|
|||||||
/* say1(" %08x\n", acc); */
|
/* say1(" %08x\n", acc); */
|
||||||
say1("%.5s", buffer-5);
|
say1("%.5s", buffer-5);
|
||||||
say2(" => %08x (len: %d)\n", acc, len);
|
say2(" => %08x (len: %d)\n", acc, len);
|
||||||
|
|
||||||
cnt = (len < 4) ? len : 4;
|
cnt = (len < 4) ? len : 4;
|
||||||
len -= cnt;
|
len -= cnt;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -60,9 +60,17 @@ void _xorbuf(unsigned char *iv, unsigned char *buf, size_t xlen) {
|
|||||||
|
|
||||||
/* print some binary data to stderr */
|
/* print some binary data to stderr */
|
||||||
void _dump(char *n, unsigned char *d, size_t s) {
|
void _dump(char *n, unsigned char *d, size_t s) {
|
||||||
fprintf(stderr, "%s (%ld): ", n, s);
|
int l = strlen(n) + 9;
|
||||||
|
fprintf(stderr, "%s (%04ld): ", n, s);
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i=0; i<s; ++i)
|
int c;
|
||||||
|
for (i=0; i<s; ++i) {
|
||||||
fprintf(stderr, "%02x", d[i]);
|
fprintf(stderr, "%02x", d[i]);
|
||||||
|
if(i % 36 == 35 && i > 0) {
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
for(c=0; c<l; ++c)
|
||||||
|
fprintf(stderr, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
79
man/pcp1.pod
79
man/pcp1.pod
@@ -360,9 +360,9 @@ Verification by recipient:
|
|||||||
=head1 SIGNED ENCRYPTION
|
=head1 SIGNED ENCRYPTION
|
||||||
|
|
||||||
Beside pure encryption and signatures pcp1 also supports signed
|
Beside pure encryption and signatures pcp1 also supports signed
|
||||||
encryption. In this mode an input file will be encrypted and
|
encryption. In this mode an input file will be signed your primary
|
||||||
a signature using your primary secret key from a BLAKE2 hash of
|
secret key from a BLAKE2 hash of the file contents and the recipients
|
||||||
the file contents will be appended to it.
|
and then encrypted. The signature is encrypted as well.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -600,6 +600,20 @@ Recipient field format:
|
|||||||
R is calculated using public key encryption using the senders
|
R is calculated using public key encryption using the senders
|
||||||
secret key, the recipients public key and a random nonce.
|
secret key, the recipients public key and a random nonce.
|
||||||
|
|
||||||
|
Pseudocode:
|
||||||
|
|
||||||
|
R = foreach P: N | crypto_box(S, N, P, SK)
|
||||||
|
L = len(R)
|
||||||
|
T = 5
|
||||||
|
write (T | L | R)
|
||||||
|
foreach I: write (N | crypto_secret_box(I, N, S))
|
||||||
|
|
||||||
|
where P is the public key of a recipient, SK is the senders
|
||||||
|
secret key, R is the recipient list, L is the number of recipients,
|
||||||
|
T is the filetype header, I is a block of input with a size
|
||||||
|
of 32k, N is a nonce (new per block) and S the symmetric key.
|
||||||
|
|
||||||
|
|
||||||
=head2 SIGNATURE FORMAT
|
=head2 SIGNATURE FORMAT
|
||||||
|
|
||||||
There are different signature formats. Standard binary NACL
|
There are different signature formats. Standard binary NACL
|
||||||
@@ -620,6 +634,15 @@ signatures have the following format:
|
|||||||
The actual signature is not a signature over the whole content
|
The actual signature is not a signature over the whole content
|
||||||
of an input file but of a BLAKE2 hash of the content.
|
of an input file but of a BLAKE2 hash of the content.
|
||||||
|
|
||||||
|
Pseudo code:
|
||||||
|
|
||||||
|
H = crypto_generichash(C)
|
||||||
|
C | O | H | crypto_sign(H, S)
|
||||||
|
|
||||||
|
where C is the message (content), H is the blake2 hash,
|
||||||
|
O is the offset separator and S is the secret signing key
|
||||||
|
of the sender.
|
||||||
|
|
||||||
Armored signatures have the following format:
|
Armored signatures have the following format:
|
||||||
|
|
||||||
----- BEGIN ED25519 SIGNED MESSAGE -----
|
----- BEGIN ED25519 SIGNED MESSAGE -----
|
||||||
@@ -642,8 +665,54 @@ contents as the binary signature outlined above (hash+sig).
|
|||||||
|
|
||||||
Signed encrypted files are in binary form only. The first part is
|
Signed encrypted files are in binary form only. The first part is
|
||||||
the standard encrypted file as described in B<ENCRYPTED OUTPUT FORMAT>
|
the standard encrypted file as described in B<ENCRYPTED OUTPUT FORMAT>
|
||||||
followed by the binary signature described in B<SIGNATURE FORMAT> without
|
followed by the binary encrypted signature described in B<SIGNATURE FORMAT>
|
||||||
the offset separator.
|
without the offset separator.
|
||||||
|
|
||||||
|
However, not only the hash of the file content will be signed but the
|
||||||
|
recipient list described in B<ENCRYPTED OUTPUT FORMAT> as well. A
|
||||||
|
valid recipient is therefore not able to re-encrypt the decrypted
|
||||||
|
message, append the original signature and send it to other recipients.
|
||||||
|
The signature would not match since the recipient list differs and
|
||||||
|
so recipients know that the signature is forged.
|
||||||
|
|
||||||
|
Formal file description of sign+encrypt format:
|
||||||
|
|
||||||
|
+---------------------------------------------------------+
|
||||||
|
| Field Size Description |
|
||||||
|
+-------------+--------+----------------------------------+
|
||||||
|
| Type | 1 | Filetype, 5=ASYM, 23=SYM |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
| Len R | 4 | Number of recipients (*) |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
| Recipients | R*72 | C(recipient)|C(recipient)... (*) |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
| Encrypted | ~ | The actual encrypted data |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
| Signature | ~ | Encrypted signature(*) |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
|
||||||
|
As usual the encrypted signature consists of a nonce and the
|
||||||
|
actual cipher, which is computed symmetrically (see above)
|
||||||
|
from the following clear signature.
|
||||||
|
|
||||||
|
Before encryption the signature format is:
|
||||||
|
|
||||||
|
+---------------------------------------------------------+
|
||||||
|
| Field Size Description |
|
||||||
|
+-------------+--------+----------------------------------+
|
||||||
|
| Hash | 64 | BLAKE2 hash of content+R (*) |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
| Signature | 64 | ED25519 signature of BLAKE2 Hash |
|
||||||
|
+-------------|--------|----------------------------------+
|
||||||
|
|
||||||
|
where R is: C(recipient)|C(recipient)... (see B<ENCRYPTED OUTPUT FORMAT>).
|
||||||
|
|
||||||
|
Pseudocode:
|
||||||
|
|
||||||
|
N | crypto_secret_box( crypto_sign( crypto_generichash( M + R, SK ) ), N, S)
|
||||||
|
|
||||||
|
where N is the nonce, M the message, R the recipient list, SK is the senders
|
||||||
|
secret signing key and S the symmetric key.
|
||||||
|
|
||||||
=head2 Z85 ENCODING
|
=head2 Z85 ENCODING
|
||||||
|
|
||||||
|
|||||||
@@ -376,9 +376,11 @@ int pcp_importsecret (vault_t *vault, FILE *in) {
|
|||||||
int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||||
pcp_pubkey_t *pub = NULL;
|
pcp_pubkey_t *pub = NULL;
|
||||||
if(pbpcompat == 1) {
|
if(pbpcompat == 1) {
|
||||||
|
char *date = NULL;
|
||||||
char *parts = NULL;
|
char *parts = NULL;
|
||||||
int pnum;
|
int pnum;
|
||||||
pbp_pubkey_t *b = ucmalloc(sizeof(pbp_pubkey_t));
|
pbp_pubkey_t *b = ucmalloc(sizeof(pbp_pubkey_t));
|
||||||
|
pcp_pubkey_t *tmp = ucmalloc(sizeof(pcp_pubkey_t));
|
||||||
pub = ucmalloc(sizeof(pcp_pubkey_t));
|
pub = ucmalloc(sizeof(pcp_pubkey_t));
|
||||||
unsigned char *buf = ucmalloc(2048);
|
unsigned char *buf = ucmalloc(2048);
|
||||||
unsigned char *bin = ucmalloc(2048);
|
unsigned char *bin = ucmalloc(2048);
|
||||||
@@ -396,7 +398,8 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
|||||||
nlen -= 1;
|
nlen -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
klen = (nlen / 5) * 4;
|
|
||||||
|
klen = nlen /5 * 4; /*((nlen + (5 - (nlen % 5))) / 5) * 4;*/
|
||||||
|
|
||||||
if(decode_85((char *)bin, (char *)buf, klen) != 0)
|
if(decode_85((char *)bin, (char *)buf, klen) != 0)
|
||||||
goto errimp1;
|
goto errimp1;
|
||||||
@@ -407,6 +410,9 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
|||||||
goto errimp1;
|
goto errimp1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
_dump("sig", bin, nlen);
|
||||||
|
|
||||||
/* unpad result, if any */
|
/* unpad result, if any */
|
||||||
for(i=klen; i>0; --i) {
|
for(i=klen; i>0; --i) {
|
||||||
if(bin[i] != '\0' && i < klen) {
|
if(bin[i] != '\0' && i < klen) {
|
||||||
@@ -418,6 +424,19 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
|||||||
/* use first part as sig and verify */
|
/* use first part as sig and verify */
|
||||||
memcpy(b, &bin[crypto_sign_BYTES], klen - crypto_sign_BYTES);
|
memcpy(b, &bin[crypto_sign_BYTES], klen - crypto_sign_BYTES);
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
_dump("sig", bin, klen);
|
||||||
|
|
||||||
|
/* parse the date */
|
||||||
|
date = ucmalloc(19);
|
||||||
|
memcpy(date, b->iso_ctime, 18);
|
||||||
|
date[19] = '\0';
|
||||||
|
struct tm c;
|
||||||
|
if(strptime(date, "%Y-%m-%dT%H:%M:%S", &c) == NULL) {
|
||||||
|
fatal("Failed to parse creation time in PBP public key file (<%s>)\n", date);
|
||||||
|
goto errimp2;
|
||||||
|
}
|
||||||
|
|
||||||
/* parse the name */
|
/* parse the name */
|
||||||
parts = strtok (b->name, "<>");
|
parts = strtok (b->name, "<>");
|
||||||
pnum = 0;
|
pnum = 0;
|
||||||
@@ -433,22 +452,35 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
|||||||
|
|
||||||
if(strlen(b->name) == 0) {
|
if(strlen(b->name) == 0) {
|
||||||
char *owner = pcp_getstdin("Enter the name of the key owner");
|
char *owner = pcp_getstdin("Enter the name of the key owner");
|
||||||
memcpy(b->name, owner, strlen(owner) + 1);
|
memcpy(pub->owner, owner, strlen(owner) + 1);
|
||||||
free(owner);
|
free(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill in the fields */
|
/* fill in the fields */
|
||||||
pub->ctime = (long)time(0); /* pbp exports no ctime */
|
pub->ctime = (long)mktime(&c);
|
||||||
pub->type = PCP_KEY_TYPE_PUBLIC;
|
pub->type = PCP_KEY_TYPE_PUBLIC;
|
||||||
pub->version = PCP_KEY_VERSION;
|
pub->version = PCP_KEY_VERSION;
|
||||||
pub->serial = arc4random();
|
pub->serial = arc4random();
|
||||||
memcpy(pub->id, pcp_getpubkeyid(pub), 17);
|
|
||||||
memcpy(pub->pub, b->pub, crypto_box_PUBLICKEYBYTES);
|
memcpy(pub->pub, b->pub, crypto_box_PUBLICKEYBYTES);
|
||||||
memcpy(pub->edpub, b->edpub, crypto_sign_PUBLICKEYBYTES);
|
memcpy(pub->edpub, b->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
memcpy(pub->id, pcp_getpubkeyid(pub), 17);
|
||||||
|
|
||||||
|
/* edpub used for signing, might differ */
|
||||||
|
memcpy(tmp->edpub, b->sigpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
|
||||||
|
if(debug) {
|
||||||
|
_dump(" mp", tmp->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
_dump(" cp", pub->pub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
_dump(" sp", pub->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
_dump("name", (unsigned char *)pub->owner, strlen(pub->owner));
|
||||||
|
_dump(" sig", bin, klen);
|
||||||
|
fprintf(stderr, "<%s>\n", (unsigned char *)pub->owner);
|
||||||
|
pcp_dumppubkey(pub);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *sig = pcp_ed_verify(bin, klen, tmp);
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
fprintf(stderr, "edpub: "); pcpprint_bin(stderr, pub->edpub, crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, " sig: "); pcpprint_bin(stderr, bin, klen); fprintf(stderr, "\n");
|
|
||||||
unsigned char *sig = pcp_ed_verify(bin, klen, pub);
|
|
||||||
if(sig == NULL)
|
if(sig == NULL)
|
||||||
goto errimp1;
|
goto errimp1;
|
||||||
|
|
||||||
@@ -458,6 +490,8 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
|||||||
free(bin);
|
free(bin);
|
||||||
goto kimp;
|
goto kimp;
|
||||||
|
|
||||||
|
errimp2:
|
||||||
|
free(date);
|
||||||
|
|
||||||
errimp1:
|
errimp1:
|
||||||
free(bin);
|
free(bin);
|
||||||
|
|||||||
@@ -233,23 +233,52 @@ void pcppubkey_print(pcp_pubkey_t *key, FILE* out, int pbpcompat) {
|
|||||||
|
|
||||||
secret = pcpkey_decrypt(secret, passphrase);
|
secret = pcpkey_decrypt(secret, passphrase);
|
||||||
if(secret != NULL) {
|
if(secret != NULL) {
|
||||||
size_t pbplen = crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES+crypto_sign_PUBLICKEYBYTES+strlen(key->owner);
|
size_t pbplen = crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES+crypto_sign_PUBLICKEYBYTES+strlen(key->owner)+64;
|
||||||
|
|
||||||
|
/* we need to do the padding here, since pbp verifies the sig including the pad */
|
||||||
|
/*
|
||||||
|
int pad = pbplen % 4;
|
||||||
|
if(pad > 0) {
|
||||||
|
pad = 4 - pad;
|
||||||
|
pbplen += pad;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
unsigned char *blob = ucmalloc(pbplen);
|
unsigned char *blob = ucmalloc(pbplen);
|
||||||
|
|
||||||
fprintf(stderr, "edpub: "); pcpprint_bin(stderr, key->edpub, crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
if(debug) {
|
||||||
fprintf(stderr, " pub: "); pcpprint_bin(stderr, key->pub, crypto_box_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
_dump(" mp", secret->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
fprintf(stderr, "edpub: "); pcpprint_bin(stderr, key->edpub, crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
_dump(" cp", key->pub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
_dump(" sp", key->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
|
_dump("name", (unsigned char *)key->owner, strlen(key->owner));
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(blob, key->edpub, crypto_sign_PUBLICKEYBYTES);
|
/* pkt = keys.sign(keys.mp+keys.sp+keys.cp+dates+keys.name, master=True) */
|
||||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES], key->pub, crypto_box_PUBLICKEYBYTES);
|
memcpy(blob, secret->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES], key->edpub, crypto_sign_PUBLICKEYBYTES);
|
memcpy(&blob[crypto_sign_PUBLICKEYBYTES], key->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES+crypto_sign_PUBLICKEYBYTES],
|
memcpy(&blob[crypto_sign_PUBLICKEYBYTES*2], key->pub, crypto_box_PUBLICKEYBYTES);
|
||||||
key->owner, strlen(key->owner));
|
|
||||||
|
struct tm *v;
|
||||||
|
time_t vt = t + 31536000;
|
||||||
|
v = localtime(&vt);
|
||||||
|
|
||||||
|
char *date = ucmalloc(65);
|
||||||
|
|
||||||
|
sprintf(date, "%04d-%02d-%02dT%02d:%02d:%02d.000000 %04d-%02d-%02dT%02d:%02d:%02d.000000 ",
|
||||||
|
c->tm_year+1900-1, c->tm_mon+1, c->tm_mday, // wtf? why -1?
|
||||||
|
c->tm_hour, c->tm_min, c->tm_sec,
|
||||||
|
v->tm_year+1900-1, v->tm_mon+1, v->tm_mday,
|
||||||
|
v->tm_hour, v->tm_min, v->tm_sec);
|
||||||
|
|
||||||
|
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES*2], date, 64);
|
||||||
|
|
||||||
|
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES*2+64], key->owner, strlen(key->owner));
|
||||||
|
|
||||||
unsigned char *sig = pcp_ed_sign(blob, pbplen, secret);
|
unsigned char *sig = pcp_ed_sign(blob, pbplen, secret);
|
||||||
fprintf(stderr, " sig: "); pcpprint_bin(stderr, sig, pbplen+crypto_sign_BYTES); fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "siglen: %d, inlen: %ld\n", crypto_sign_BYTES, pbplen);
|
if(debug)
|
||||||
|
_dump(" sig", sig, crypto_sign_BYTES+pbplen);
|
||||||
|
|
||||||
if(sig != NULL) {
|
if(sig != NULL) {
|
||||||
size_t siglen = pbplen + crypto_sign_BYTES;
|
size_t siglen = pbplen + crypto_sign_BYTES;
|
||||||
size_t blen = ((siglen / 4) * 5) + siglen;
|
size_t blen = ((siglen / 4) * 5) + siglen;
|
||||||
|
|||||||
Reference in New Issue
Block a user