[openpgp] proof-of-work fingerprints [was: Re: Fingerprint requirements for OpenPGP]

Daniel Kahn Gillmor <dkg@fifthhorseman.net> Tue, 12 April 2016 18:18 UTC

Return-Path: <dkg@fifthhorseman.net>
X-Original-To: openpgp@ietfa.amsl.com
Delivered-To: openpgp@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 1FA0112E8C1 for <openpgp@ietfa.amsl.com>; Tue, 12 Apr 2016 11:18:35 -0700 (PDT)
X-Quarantine-ID: <3dqITY5h32dt>
X-Virus-Scanned: amavisd-new at amsl.com
X-Amavis-Alert: BANNED, message contains text/plain,.exe
X-Spam-Flag: NO
X-Spam-Score: -1.9
X-Spam-Level:
X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5 tests=[BAYES_00=-1.9] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3dqITY5h32dt for <openpgp@ietfa.amsl.com>; Tue, 12 Apr 2016 11:18:32 -0700 (PDT)
Received: from che.mayfirst.org (che.mayfirst.org [162.247.75.118]) by ietfa.amsl.com (Postfix) with ESMTP id AE80212E7D4 for <openpgp@ietf.org>; Tue, 12 Apr 2016 11:18:32 -0700 (PDT)
Received: from fifthhorseman.net (unknown [38.109.115.130]) by che.mayfirst.org (Postfix) with ESMTPSA id 30301F991; Tue, 12 Apr 2016 14:18:31 -0400 (EDT)
Received: by fifthhorseman.net (Postfix, from userid 1000) id AEDE420072; Tue, 12 Apr 2016 14:18:25 -0400 (EDT)
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
To: Werner Koch <wk@gnupg.org>
In-Reply-To: <87potug3s5.fsf@wheatstone.g10code.de>
References: <87vb3nslqh.fsf@alice.fifthhorseman.net> <87potug3s5.fsf@wheatstone.g10code.de>
User-Agent: Notmuch/0.21+128~g620f892 (http://notmuchmail.org) Emacs/24.5.1 (x86_64-pc-linux-gnu)
Date: Tue, 12 Apr 2016 14:18:25 -0400
Message-ID: <87potuadxq.fsf@alice.fifthhorseman.net>
MIME-Version: 1.0
Content-Type: text/plain
Archived-At: <http://mailarchive.ietf.org/arch/msg/openpgp/Npy5IRT2AJDcoS-NdWuTNOgGvsc>
Cc: IETF OpenPGP <openpgp@ietf.org>
Subject: [openpgp] proof-of-work fingerprints [was: Re: Fingerprint requirements for OpenPGP]
X-BeenThere: openpgp@ietf.org
X-Mailman-Version: 2.1.17
Precedence: list
List-Id: "Ongoing discussion of OpenPGP issues." <openpgp.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/openpgp>, <mailto:openpgp-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/openpgp/>
List-Post: <mailto:openpgp@ietf.org>
List-Help: <mailto:openpgp-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/openpgp>, <mailto:openpgp-request@ietf.org?subject=subscribe>
X-List-Received-Date: Tue, 12 Apr 2016 18:18:35 -0000

On Tue 2016-04-12 13:01:14 -0400, Werner Koch <wk@gnupg.org> wrote:
> According to Peter, this means SHA-256.  There may be no proof-of-work
> to for creating a key and thus a structured fingerprint.  (Sometimes you
> have to create a lot of one-off keys.)

The one proposal i've heard that keeps things somewhat simple but still
allows the possibility of an increased cost to an adversary intent on a
preimage attack is related to floating point implementations, and was
suggested by PHB.  I try to describe my understanding of the scheme
below, but writing it all down makes me think that "somewhat simple"
might be overselling it; there are quite a few complications.

Here goes:

 * Start from a design of a fingerprint of X bits, using a truncation of
   digest of size Z (where Z > X).  for example, a 200-bit fingerprint
   from sha-256.  this would typically mean an adversary has to do about
   2^200 sha-256 operations to find a match.  (this is an impossibly
   high bar if we're talking about brute force, but if we assume that
   most cryptanalysis of SHA-256 allows an attacker to shave off some
   sizeable work factor, it represents the "safety margin" against
   unknown cryptanalysis of this type)

 * Instead, make the fingerprint X+N bits, where the first N bits
   indicate the number of leading zeros of the digest.  Now if i do a
   bit of work to generate keys with, say, 10 leading zeros, an attacker
   will probably need to do about 2^10 more digests to find a match.

So if we're doing a 200-bit truncation of sha-256, and we have an 8-bit
count of leading zeros, and the digest produced is (in hex -- i'm not
making an argument about choice of textual representation here, just
showing in hex for discussion):

 001a1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2

then it would result in a fingerprint of (again in hex for discussion):

  0ba1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da5

That is: 11 (0x0b) bits of zeros, followed by a 1 bit, followed by
a1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da5

More precisely, under this scheme, fingerprint generation looks like:

scan1(x) : the position of the left-most 1 bit in bitstring x
  A[n:m] : bits n through m-1 of bitstring A
enc(n,b) : a bitstring of length b encoding n as an unsigned int
 dgst(x) : a fixed-length bitstring digest of bitstring x (e.g. sha-256)
       X : number of explicitly-represented bits
       N : size of bitfield for counting leading zeros
      || : bitstring concatenation

 def fpr(z):
    h = dgst(z)
    k = scan1(z)
    return enc(k,N) || h[z+1:z+1+X]

 * note that first bit that is set to 1 is omitted from the
   representation (it's present by implication).  This isn't just a hack
   to squeeze one more bit of entropy out of the fingerprint, it's
   arguably necessary to ensure that there is exactly one fingerprint
   that corresponds to any given digest. Otherwise, i can represent a
   fingerprint with 3 leading zero bits as:

 <3> 1100101...

   or:

 <2> 01100101...

   or:

 <1> 001100101...

   or:

 <0> 0001100101...

    The other alternative is to require that the leading bit after the
    zero-run prefix is always 1 (unless the zero-run prefix is maxed
    out?), which would mean that certain fingerprints are simply invalid
    (we'd want tools to reject them?)

  * note also that for some choices of dgst and N it would mean there
    could be some keys that are un-fingerprintable.  For example, if we
    chose dgst = sha-256 and N = 5, then any key that has a sha-256
    digest with a leading run of more than 31 zeros might be
    unrepresentable.

With all these caveats, i confess the much-simpler construction "200-bit
truncation of sha-256" looks pretty appealing :)

           --dkg