signify untrusted comments

2014-01-07 Thread Ted Unangst
A little background: Among the many ways a signature can fail to
verify is the user trying to verify with the wrong public key. In a
setup like signify where users may be expected (required) at times to
specify the public key, I expect this to be one of the leading failure
modes. (Somewhere on this list is also using the wrong private key to
create the signature.)

This can be a frustrating error because the only message you get back
is signature failed. The obvious solution is to encode some
identifier information into the key and signature files. That provides
a quick check where we can easily say this public key belongs to X,
but the signature was created by Y.

Why doesn't signify do this? Because that identity isn't trusted. Just
because a public key says it came from root@openbsd doesn't mean it
did. I didn't want to open up a social engineering avenue whereby
somebody provides a user with a backdoor pkg and matching public key,
allegedly from root@openbsd. Hey, openbsd is using a new key now, but
as you can see, it comes from them. Nothing in a signify key or
signature should be mistaken as a voucher for its own integrity.

As a concession to usability, I did include a comment line in the
files. If you accidentally mix up some key files, for whatever reason,
maybe that can help set you straight. The signify tool itself ignores
this line, as documented. Now we're back to asking, can this be used
to trick the user? I was hoping that users would know the comment
isn't to be trusted, but that may be asking too much.

To that end, I think the comment should be marked as untrusted, and
signify should even check that it says untrusted. Hopefully this makes
it a little harder to con somebody into believing the comment actually
should be trusted. Is this really necessary? Honestly, I think so. The
weakest link here is an adversary's ability to trick the user into using
the wrong key; I don't want to assist them in any way.

(I'm also open to reconsidering whether keys should include
identifiers. Perhaps a random id created during key generation? Just
enough to say you're using the wrong key.)

Index: signify.c
===
RCS file: /cvs/src/usr.bin/signify/signify.c,v
retrieving revision 1.12
diff -u -p -r1.12 signify.c
--- signify.c   6 Jan 2014 01:50:54 -   1.12
+++ signify.c   30 Dec 2013 16:14:25 -
@@ -38,6 +38,9 @@
 #define PKALG Ed
 #define KDFALG BK
 
+#define COMMENTHDR untrusted comment:
+#define COMMENTHDRLEN 18
+
 struct enckey {
uint8_t pkalg[2];
uint8_t kdfalg[2];
@@ -118,8 +121,9 @@ readb64file(const char *filename, void *
if (rv == -1)
err(1, read from %s, filename);
commentend = strchr(b64, '\n');
-   if (!commentend)
-   errx(1, no newline in %s, filename);
+   if (!commentend || commentend - b64 = COMMENTHDRLEN ||
+   memcmp(b64, COMMENTHDR, COMMENTHDRLEN))
+   errx(1, invalid comment in %s, filename);
rv = b64_pton(commentend + 1, buf, len);
if (rv != len)
errx(1, invalid b64 encoding in %s, filename);
@@ -172,7 +176,8 @@ writeb64file(const char *filename, const
int fd, rv;
 
fd = xopen(filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_RDWR, mode);
-   snprintf(header, sizeof(header), signify -- %s\n, comment);
+   snprintf(header, sizeof(header), %s signify %s\n, COMMENTHDR,
+   comment);
writeall(fd, header, strlen(header), filename);
if ((rv = b64_ntop(buf, len, b64, sizeof(b64)-1)) == -1)
errx(1, b64 encode failed);







Re: signify untrusted comments

2014-01-07 Thread Christian Weisgerber
Ted Unangst t...@tedunangst.com wrote:

 To that end, I think the comment should be marked as untrusted, and
 signify should even check that it says untrusted. Hopefully this makes
 it a little harder to con somebody into believing the comment actually
 should be trusted.

I think somebody who can be conned into accepting a key will not
understand or think about untrusted comment.

 (I'm also open to reconsidering whether keys should include
 identifiers. Perhaps a random id created during key generation? Just
 enough to say you're using the wrong key.)

I'm in favor.

-- 
Christian naddy Weisgerber  na...@mips.inka.de



Re: signify untrusted comments

2014-01-07 Thread Marc Espie
On Tue, Jan 07, 2014 at 04:54:59PM +, Christian Weisgerber wrote:
  (I'm also open to reconsidering whether keys should include
  identifiers. Perhaps a random id created during key generation? Just
  enough to say you're using the wrong key.)
 
 I'm in favor.

Yeah, looks like a good idea.
64 bits random number ?



Re: signify untrusted comments

2014-01-07 Thread Ted Unangst
On Tue, Jan 07, 2014 at 16:54, Christian Weisgerber wrote:
 Ted Unangst t...@tedunangst.com wrote:
 
 To that end, I think the comment should be marked as untrusted, and
 signify should even check that it says untrusted. Hopefully this makes
 it a little harder to con somebody into believing the comment actually
 should be trusted.
 
 I think somebody who can be conned into accepting a key will not
 understand or think about untrusted comment.

hmm.
1. people who don't trust strangers. we don't need to protect them.
2. people who do trust strangers. we can't protect them.
3. people who are initially suspicious, but have a mistaken belief
that they can spot mischief. maybe they will be fooled regardless, but
I don't want to provide anything that can be used as leverage against
them.

 
 (I'm also open to reconsidering whether keys should include
 identifiers. Perhaps a random id created during key generation? Just
 enough to say you're using the wrong key.)
 
 I'm in favor.

Here's a diff with that too. I print out the fingerprints after a
mismatch, but the fingerprints are in the opaque part of the file, so
I'm not sure how useful that is. At least it provides a hint to check
the command line arguments.

Index: signify.c
===
RCS file: /cvs/src/usr.bin/signify/signify.c,v
retrieving revision 1.12
diff -u -p -r1.12 signify.c
--- signify.c   6 Jan 2014 01:50:54 -   1.12
+++ signify.c   30 Dec 2013 18:11:45 -
@@ -37,6 +37,10 @@
 
 #define PKALG Ed
 #define KDFALG BK
+#define FPLEN 8
+
+#define COMMENTHDR untrusted comment:
+#define COMMENTHDRLEN 18
 
 struct enckey {
uint8_t pkalg[2];
@@ -44,16 +48,19 @@ struct enckey {
uint32_t kdfrounds;
uint8_t salt[16];
uint8_t checksum[8];
+   uint8_t fingerprint[FPLEN];
uint8_t seckey[SECRETBYTES];
 };
 
 struct pubkey {
uint8_t pkalg[2];
+   uint8_t fingerprint[FPLEN];
uint8_t pubkey[PUBLICBYTES];
 };
 
 struct sig {
uint8_t pkalg[2];
+   uint8_t fingerprint[FPLEN];
uint8_t sig[SIGBYTES];
 };
 
@@ -118,8 +125,10 @@ readb64file(const char *filename, void *
if (rv == -1)
err(1, read from %s, filename);
commentend = strchr(b64, '\n');
-   if (!commentend)
-   errx(1, no newline in %s, filename);
+   if (!commentend || commentend - b64 = COMMENTHDRLEN ||
+   memcmp(b64, COMMENTHDR, COMMENTHDRLEN))
+   errx(1, invalid comment in %s; must start with '%s',
+   filename, COMMENTHDR);
rv = b64_pton(commentend + 1, buf, len);
if (rv != len)
errx(1, invalid b64 encoding in %s, filename);
@@ -172,7 +181,8 @@ writeb64file(const char *filename, const
int fd, rv;
 
fd = xopen(filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_RDWR, mode);
-   snprintf(header, sizeof(header), signify -- %s\n, comment);
+   snprintf(header, sizeof(header), %s signify %s\n, COMMENTHDR,
+   comment);
writeall(fd, header, strlen(header), filename);
if ((rv = b64_ntop(buf, len, b64, sizeof(b64)-1)) == -1)
errx(1, b64 encode failed);
@@ -239,10 +249,12 @@ generate(const char *pubkeyfile, const c
struct pubkey pubkey;
struct enckey enckey;
uint8_t xorkey[sizeof(enckey.seckey)];
+   uint8_t fingerprint[FPLEN];
SHA2_CTX ctx;
int i;
 
crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
+   arc4random_buf(fingerprint, sizeof(fingerprint));
 
SHA512Init(ctx);
SHA512Update(ctx, enckey.seckey, sizeof(enckey.seckey));
@@ -251,6 +263,7 @@ generate(const char *pubkeyfile, const c
memcpy(enckey.pkalg, PKALG, 2);
memcpy(enckey.kdfalg, KDFALG, 2);
enckey.kdfrounds = htonl(rounds);
+   memcpy(enckey.fingerprint, fingerprint, FPLEN);
arc4random_buf(enckey.salt, sizeof(enckey.salt));
kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
@@ -264,6 +277,7 @@ generate(const char *pubkeyfile, const c
memset(enckey, 0, sizeof(enckey));
 
memcpy(pubkey.pkalg, PKALG, 2);
+   memcpy(pubkey.fingerprint, fingerprint, FPLEN);
writeb64file(pubkeyfile, public key, pubkey,
sizeof(pubkey), 0666);
 }
@@ -299,6 +313,7 @@ sign(const char *seckeyfile, const char 
msg = readmsg(inputfile, msglen);
 
signmsg(enckey.seckey, msg, msglen, sig.sig);
+   memcpy(sig.fingerprint, enckey.fingerprint, FPLEN);
memset(enckey, 0, sizeof(enckey));
 
memcpy(sig.pkalg, PKALG, 2);
@@ -317,6 +332,14 @@ verify(const char *pubkeyfile, const cha
 
readb64file(pubkeyfile, pubkey, sizeof(pubkey));
readb64file(sigfile, sig, sizeof(sig));
+
+   if (memcmp(pubkey.fingerprint, sig.fingerprint, FPLEN)) {
+   char fp1[(FPLEN + 2) / 3 * 4 + 

Re: signify untrusted comments

2014-01-07 Thread Marc Espie
On Tue, Jan 07, 2014 at 01:17:12PM -0500, Ted Unangst wrote:
 On Tue, Jan 07, 2014 at 16:54, Christian Weisgerber wrote:
  Ted Unangst t...@tedunangst.com wrote:
  
  To that end, I think the comment should be marked as untrusted, and
  signify should even check that it says untrusted. Hopefully this makes
  it a little harder to con somebody into believing the comment actually
  should be trusted.
  
  I think somebody who can be conned into accepting a key will not
  understand or think about untrusted comment.
 
 hmm.
 1. people who don't trust strangers. we don't need to protect them.
 2. people who do trust strangers. we can't protect them.
 3. people who are initially suspicious, but have a mistaken belief
 that they can spot mischief. maybe they will be fooled regardless, but
 I don't want to provide anything that can be used as leverage against
 them.
 
  
  (I'm also open to reconsidering whether keys should include
  identifiers. Perhaps a random id created during key generation? Just
  enough to say you're using the wrong key.)
  
  I'm in favor.
 
 Here's a diff with that too. I print out the fingerprints after a
 mismatch, but the fingerprints are in the opaque part of the file, so
 I'm not sure how useful that is. At least it provides a hint to check
 the command line arguments.

I think the fingerprint mismatch error message is too technical.
errx(1, Signature failed: checked against the wrong key);
looks like enough for me.

Apart from that, I'm okay with that code.

If you want to display fingerprints, well, you have to make it possible
to display fingerprints for any key-like message.
Not that it's much code, but is it really necessary ?