[tahoe-dev] [pycryptopp] #13: DSA "semi-private"/intermediate keys

pycryptopp trac at allmydata.org
Tue May 12 17:19:37 PDT 2009


#13: DSA "semi-private"/intermediate keys
------------------------+---------------------------------------------------
Reporter:  warner       |           Owner:       
    Type:  enhancement  |          Status:  new  
Priority:  major        |         Version:  0.5.1
Keywords:               |   Launchpad_bug:       
------------------------+---------------------------------------------------

Comment(by warner):

 There's been a lot of discussion about semi-private keys on
 http://allmydata.org/trac/tahoe/ticket/217 , which is the feature in Tahoe
 that we hope to implement by using semi-private keys. I'm trying to drag
 the
 discussion about non-Tahoe things back here, to the pycryptopp ticket.

 The proposal so far:
 {{{
 privkey=x (uniformly chosen from [1..n-1])
 semiprivkey = g^x
 y = H(g^x), truncated to fit [1..n-1], "re-roll" if necessary
 signingkey = x*y
 verifyingkey = g^(x*y)
 }}}

 Where the holder of the privkey can derive everything, the holder of the
 semiprivkey can derive the verifying key, and the holder of the verifying
 key
 can't derive anything else.

 Now, Zooko responded to one concern by adding the "re-roll if necessary"
 clause: since the group size "n" is never equal to a power of two, a
 uniformly-distributed hash function needs some massaging to use it to
 obtain
 a uniformly-distributed number in the range {{{[1..n-1]}}}. You could
 truncate it to some {{{2^m}}} such that {{{m <= n-1}}}, but that leaves a
 lot
 of the range uncovered. You could truncate it to {{{2^(m+1)}}} and take
 the
 result mod n, but that double-covers a lot of the range. The best (only?)
 way
 to get a uniform distribution is to truncate it to slightly larger than
 the
 {{{[1..n-1]}}} range, but then have a process to "re-roll" if you luck out
 and hit that edge case. For us, that would mean something like:

 {{{
 def make_y(x, n):
     i = 0
     while True:
         h = sha256("%s-%s" % (g**x, i)).digest()
         num = truncate_and_integerify(h, ceil(log2(n)))
         if 1 <= num <= n-1:
             return num
         i += 1
 }}}

 But! I don't think that's what Shawn was concerned about. Instead (and
 Shawn,
 please correct me if I'm wrong), I think he was concerned with the fact
 that
 "y" will not have nearly the same range as x does. It's basically another
 form of the "birthday paradox". There is a one-to-one mapping from {{{x}}}
 to
 {{{g^x}}}, but there is a many-to-one mapping from our truncated-hash
 {{{y}}}
 function (even without the re-roll clause).

 I wrote up a little program to exercise this. Hashing 65536 ({{{2^16}}})
 consecutive strings and then truncating them into a 16-bit value only
 produces about 41500 unique values. Doing the same with {{{2^18}}} 18-bit
 strings results in about 165000 unique values out of the 262144 possible
 ones. A 20-bit run gives 663k unique values out of 1048k possible.

 So I think that Shawn's concern is that the range of "y" is reduced
 (perhaps
 by 1.0 or 0.5 bits), and therefore the range of the {{{x*y}}} signing key
 will be reduced, weakening the security of the system.

-- 
Ticket URL: <http://allmydata.org/trac/pycryptopp/ticket/13#comment:2>
pycryptopp <http://allmydata.org/trac/pycryptopp>
Python bindings for the Crypto++ library


More information about the tahoe-dev mailing list