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 */
|
||||
struct _pbp_pubkey_t {
|
||||
byte sigpub[crypto_sign_PUBLICKEYBYTES];
|
||||
byte pub[crypto_box_PUBLICKEYBYTES];
|
||||
byte edpub[crypto_sign_PUBLICKEYBYTES];
|
||||
byte pub[crypto_box_PUBLICKEYBYTES];
|
||||
char iso_ctime[32];
|
||||
char iso_expire[32];
|
||||
char name[1024];
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ static void prep_base85(void)
|
||||
int decode_85(char *dst, const char *buffer, int len)
|
||||
{
|
||||
prep_base85();
|
||||
|
||||
int pos = 0;
|
||||
say1("len: %d\n", len);
|
||||
say2("decode 85 <%.*s>\n", len / 4 * 5, buffer);
|
||||
while (len) {
|
||||
@@ -47,13 +47,13 @@ int decode_85(char *dst, const char *buffer, int len)
|
||||
ch = *buffer++;
|
||||
de = de85[ch];
|
||||
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;
|
||||
} while (--cnt);
|
||||
ch = *buffer++;
|
||||
de = de85[ch];
|
||||
if (--de < 0)
|
||||
return error("invalid base85 alphabet <%c>\n", ch);
|
||||
if (--de < 0 && ch != '\0')
|
||||
return error("invalid base85 alphabet <%02x> left\n", ch);
|
||||
|
||||
|
||||
/* Detect overflow. */
|
||||
@@ -64,7 +64,6 @@ int decode_85(char *dst, const char *buffer, int len)
|
||||
/* say1(" %08x\n", acc); */
|
||||
say1("%.5s", buffer-5);
|
||||
say2(" => %08x (len: %d)\n", acc, len);
|
||||
|
||||
cnt = (len < 4) ? len : 4;
|
||||
len -= cnt;
|
||||
do {
|
||||
|
||||
@@ -60,9 +60,17 @@ void _xorbuf(unsigned char *iv, unsigned char *buf, size_t xlen) {
|
||||
|
||||
/* print some binary data to stderr */
|
||||
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;
|
||||
for (i=0; i<s; ++i)
|
||||
int c;
|
||||
for (i=0; i<s; ++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");
|
||||
}
|
||||
|
||||
79
man/pcp1.pod
79
man/pcp1.pod
@@ -360,9 +360,9 @@ Verification by recipient:
|
||||
=head1 SIGNED ENCRYPTION
|
||||
|
||||
Beside pure encryption and signatures pcp1 also supports signed
|
||||
encryption. In this mode an input file will be encrypted and
|
||||
a signature using your primary secret key from a BLAKE2 hash of
|
||||
the file contents will be appended to it.
|
||||
encryption. In this mode an input file will be signed your primary
|
||||
secret key from a BLAKE2 hash of the file contents and the recipients
|
||||
and then encrypted. The signature is encrypted as well.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -600,6 +600,20 @@ Recipient field format:
|
||||
R is calculated using public key encryption using the senders
|
||||
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
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
----- 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
|
||||
the standard encrypted file as described in B<ENCRYPTED OUTPUT FORMAT>
|
||||
followed by the binary signature described in B<SIGNATURE FORMAT> without
|
||||
the offset separator.
|
||||
followed by the binary encrypted signature described in B<SIGNATURE FORMAT>
|
||||
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
|
||||
|
||||
|
||||
@@ -376,9 +376,11 @@ int pcp_importsecret (vault_t *vault, FILE *in) {
|
||||
int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||
pcp_pubkey_t *pub = NULL;
|
||||
if(pbpcompat == 1) {
|
||||
char *date = NULL;
|
||||
char *parts = NULL;
|
||||
int pnum;
|
||||
pbp_pubkey_t *b = ucmalloc(sizeof(pbp_pubkey_t));
|
||||
pcp_pubkey_t *tmp = ucmalloc(sizeof(pcp_pubkey_t));
|
||||
pub = ucmalloc(sizeof(pcp_pubkey_t));
|
||||
unsigned char *buf = ucmalloc(2048);
|
||||
unsigned char *bin = ucmalloc(2048);
|
||||
@@ -396,7 +398,8 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||
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)
|
||||
goto errimp1;
|
||||
@@ -407,6 +410,9 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||
goto errimp1;
|
||||
}
|
||||
|
||||
if(debug)
|
||||
_dump("sig", bin, nlen);
|
||||
|
||||
/* unpad result, if any */
|
||||
for(i=klen; i>0; --i) {
|
||||
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 */
|
||||
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 */
|
||||
parts = strtok (b->name, "<>");
|
||||
pnum = 0;
|
||||
@@ -433,22 +452,35 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||
|
||||
if(strlen(b->name) == 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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->version = PCP_KEY_VERSION;
|
||||
pub->serial = arc4random();
|
||||
memcpy(pub->id, pcp_getpubkeyid(pub), 17);
|
||||
memcpy(pub->pub, b->pub, crypto_box_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)
|
||||
goto errimp1;
|
||||
|
||||
@@ -458,6 +490,8 @@ int pcp_importpublic (vault_t *vault, FILE *in, int pbpcompat) {
|
||||
free(bin);
|
||||
goto kimp;
|
||||
|
||||
errimp2:
|
||||
free(date);
|
||||
|
||||
errimp1:
|
||||
free(bin);
|
||||
|
||||
@@ -233,23 +233,52 @@ void pcppubkey_print(pcp_pubkey_t *key, FILE* out, int pbpcompat) {
|
||||
|
||||
secret = pcpkey_decrypt(secret, passphrase);
|
||||
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);
|
||||
|
||||
fprintf(stderr, "edpub: "); pcpprint_bin(stderr, key->edpub, crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
||||
fprintf(stderr, " pub: "); pcpprint_bin(stderr, key->pub, crypto_box_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
||||
fprintf(stderr, "edpub: "); pcpprint_bin(stderr, key->edpub, crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "\n");
|
||||
if(debug) {
|
||||
_dump(" mp", secret->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||
_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);
|
||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES], key->pub, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES], key->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES+crypto_box_PUBLICKEYBYTES+crypto_sign_PUBLICKEYBYTES],
|
||||
key->owner, strlen(key->owner));
|
||||
/* pkt = keys.sign(keys.mp+keys.sp+keys.cp+dates+keys.name, master=True) */
|
||||
memcpy(blob, secret->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES], key->edpub, crypto_sign_PUBLICKEYBYTES);
|
||||
memcpy(&blob[crypto_sign_PUBLICKEYBYTES*2], key->pub, crypto_box_PUBLICKEYBYTES);
|
||||
|
||||
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);
|
||||
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) {
|
||||
size_t siglen = pbplen + crypto_sign_BYTES;
|
||||
size_t blen = ((siglen / 4) * 5) + siglen;
|
||||
|
||||
Reference in New Issue
Block a user