[sip-comm-dev] GSoC 09 - OTR - First term results (DSA Signature sign/verify using BC/libgcrypt)

Werner Dittmann Werner.Dittmann at t-online.de
Tue Jul 14 20:27:47 CEST 2009


Goerge,

I'll take care of this. The author of libgcrypt is Werner Koch that I know
personally and I'm also subscribed to the libgcrypt list. To inform Werner
Koch I summarize your findings (you've done a great job here) and see what
he says to this issue.

As for the time being I agree with you to perform the sign/verify "manually"
and not using BC. I assume you have copied and modified the BC functions?
Can you upload the modifications to the otr4j source repository? Then I can
update mine here as well and we can work on the "modified very lightweigth
BC" library.

I'll keep you informed.

Regards,
Werner

Geekius Caesar schrieb:
> libgcrypt verifies the signature in cipher/dsa.c:verify.
> 
> To test my hypothesis, if I modified BouncyCastle as to not use calculateE,
> and signature verification worked fine between SIP Communicator/otr4j/BC +
> Pidgin/libotr/libgcrypt.
> 
> Still I would like a confirmation. Also if this is indeed the case we may
> have to generate/verify signatures without BouncyCastle, but within otr4j
> using the buggy process, at least until libgcrypt fixes the problem and the
> change has been incorporated in major linux distributions.
> 
> On Tue, Jul 14, 2009 at 3:16 PM, Geekius Caesar <geekius.caesar at gmail.com>wrote:
> 
>> Hello all,
>>
>> According to FIPS-186-3/p. DSA Signature Generation, (r,s) should be
>> generated as follows:
>>
>> *Let N be the bit length of q. Let min(N, outlen) denote the minimum of
>> the positive integers N and outlen, where outlen is the bit length of the
>> hash function output block. The signature of a message M consists of the
>> pair of numbers r and s that is computed according to the following
>> equations:
>>
>> r = (g^k mod p) mod q.
>> z = the leftmost min(N, outlen) bits of Hash(M).
>> s = (k^-1 (z + xr)) mod q.
>>
>> * BouncyCastle has method
>> org.bouncycastle.crypto.signers.DSASigner.class:calculateE that calculates z
>> and then calculates s.
>>
>> libgcrypt on the other hand seems to directly calculate s from M.
>>
>> If this is the case, I think this is a bug in libgcrypt, can anyone confirm
>> this?
>>
>> On Mon, Jul 13, 2009 at 4:27 AM, Geekius Caesar <geekius.caesar at gmail.com>wrote:
>>
>>> Hello Werner,
>>>
>>> Thank you for your reply, I have modified the code based on your
>>> suggestions. The problem now is that the calculated v is not equal with r
>>> (libgcrypt-1.4.1/cipher/dsa.c:380) so verification fails again.
>>>
>>> I use a custom build libgcrypt-1.4.1 and I have inserted the following
>>> calls in libgcrypt-1.4.1/cipher/dsa.c:verify
>>>
>>> log_mpidump ("  r:", r);
>>> log_mpidump ("  s:", s);
>>> log_mpidump ("  hash:", hash);
>>> log_mpidump ("  q:" , pkey->q);
>>> log_mpidump ("  p:" , pkey->p);
>>> log_mpidump ("  g:" , pkey->g);
>>> log_mpidump ("  y:" , pkey->y);
>>>
>>> I do the same in otr4j (
>>> http://code.google.com/p/otr4j/source/browse/src/net/java/otr4j/StateMachine.java#787
>>> )
>>>
>>> This way I can verify r in otr4j/bc is the same as r in libotr/libgcrypt,
>>> s in otr4j/bc is the same as s in libotr/libgcrypt, e.t.c.
>>>
>>> I will insert more logging calls in libgcrypt/bouncycastle to see why
>>> exactly the resulting v is not equal with r.
>>>
>>> kind regards,
>>> George.
>>>
>>>
>>> On Sun, Jul 12, 2009 at 2:48 PM, Werner Dittmann <
>>> Werner.Dittmann at t-online.de> wrote:
>>>
>>>> Goerge,
>>>>
>>>>
>>>> Geekius Caesar schrieb:
>>>>
>>>> <SNIP --- SNAP>
>>>>
>>>>> I did not proceed into creating transformation sets for the rest of the
>>>>> protocols because there is an issue that the DSA signature does not
>>>> verify
>>>>> in Pidgin/libotr, this action is taken in the OTR Protocol Draft, High
>>>> Level
>>>>> Overview, Authenticated Key Exchange, Step 9. I tried to sign using
>>>>> BouncyCastle Leightweight API instead of the Standard JCE, but that
>>>> failed
>>>>> too.
>>>>>
>>>>> To make sure the signature I use is correct, I created a set of test
>>>> cases
>>>>> to verify signatures created with bouncycastle using the Sun JCE
>>>> provider
>>>>> and visa versa, but that failed too.
>>>> As per specification and an according cross-check of libotr
>>>> implementation
>>>> IMHO you cannot use the JCE DSA variant to sign. The JCE variant _always_
>>>> hashes the data, then signs it. The OTR specification reads like this:
>>>>
>>>> sigA(MA) (SIG)
>>>>    This is the signature, using the private part of the key pubA, of the
>>>>    32-byte MA (which does not need to be hashed again to produce the
>>>>    signature).
>>>>
>>>> IMHO "which does not need to be hashed" is a bit ambigous and should read
>>>> "must not be hased". The libotr implementation also reflects this (see
>>>> code
>>>> snippet below). Each signature value (r and s) is copied into a 20 byte
>>>> field,
>>>> padded with zero if r or s are shorter than 20 bytes. This padding is not
>>>> explictily stated in the spec (it should be stated however).
>>>>
>>>> IMHO you need to rebuild the specific DSA signature mode using the
>>>> appropriate BC functions (SHA256 HMAC, DSA) to be compliant to the spec
>>>> and
>>>> libotr. As said, the JCE methods cannot be used because of their
>>>> "embedded"
>>>> hash mechanism.
>>>>
>>>> Here the function from libotr that performs the signature (inline
>>>> comments
>>>> added by me):
>>>>
>>>>
>>>> /* Sign data using a private key.  The data must be small enough to be
>>>>  * signed (i.e. already hashed, if necessary).  The signature will be
>>>>  * returned in *sigp, which the caller must free().  Its length will be
>>>>  * returned in *siglenp. */
>>>>
>>>> gcry_error_t otrl_privkey_sign(unsigned char **sigp, size_t *siglenp,
>>>>        OtrlPrivKey *privkey, const unsigned char *data, size_t len)
>>>> {
>>>>    gcry_mpi_t r,s, datampi;
>>>>    gcry_sexp_t dsas, rs, ss, sigs, datas;
>>>>    size_t nr, ns;
>>>>    const enum gcry_mpi_format format = GCRYMPI_FMT_USG; //wd: raw int
>>>> format
>>>>
>>>>    if (privkey->pubkey_type != OTRL_PUBKEY_TYPE_DSA)
>>>>        return gcry_error(GPG_ERR_INV_VALUE);
>>>>
>>>>    *sigp = malloc(40);       //wd: get signature storage
>>>>    if (sigp == NULL) return gcry_error(GPG_ERR_ENOMEM);
>>>>    *siglenp = 40;
>>>>
>>>>    if (len) {                //wd: transform raw data into a gcrypt
>>>> multi-precision int
>>>>        gcry_mpi_scan(&datampi, GCRYMPI_FMT_USG, data, len, NULL);
>>>>    } else {
>>>>        datampi = gcry_mpi_set_ui(NULL, 0);
>>>>    }
>>>>    gcry_sexp_build(&datas, NULL, "(%m)", datampi);  //wd: build
>>>> s-expression that holds data now
>>>>    gcry_mpi_release(datampi);
>>>>    gcry_pk_sign(&sigs, datas, privkey->privkey);    //wd: sign the
>>>> s-expr, return a s-expr in sigs
>>>>    gcry_sexp_release(datas);
>>>>    dsas = gcry_sexp_find_token(sigs, "dsa", 0);     //wd: get "dsa" sub
>>>> s-expr (contains r and s)
>>>>    gcry_sexp_release(sigs);
>>>>    rs = gcry_sexp_find_token(dsas, "r", 0);         //wd: get r s-expr
>>>>    ss = gcry_sexp_find_token(dsas, "s", 0);         //wd: get s s-expr
>>>>    gcry_sexp_release(dsas);
>>>>    r = gcry_sexp_nth_mpi(rs, 1, GCRYMPI_FMT_USG);   //wd: get r MPI
>>>>    gcry_sexp_release(rs);
>>>>    s = gcry_sexp_nth_mpi(ss, 1, GCRYMPI_FMT_USG);   //wd: get s MPI
>>>>    gcry_sexp_release(ss);
>>>>    gcry_mpi_print(format, NULL, 0, &nr, r);         //wd: dummy print
>>>> just to get lentth in "nr"
>>>>    gcry_mpi_print(format, NULL, 0, &ns, s);         //wd: dummy print
>>>> just to get length in "ns"
>>>>    memset(*sigp, 0, 40);                            //wd: fill with zero
>>>> to assure zero padding
>>>>    gcry_mpi_print(format, (*sigp)+(20-nr), nr, NULL, r); //wd: extract
>>>> data from MPI into signature buffer
>>>>    gcry_mpi_print(format, (*sigp)+20+(20-ns), ns, NULL, s); //wd: with
>>>> length as computed above
>>>>    gcry_mpi_release(r);
>>>>    gcry_mpi_release(s);
>>>>
>>>>    return gcry_error(GPG_ERR_NO_ERROR);
>>>> }
>>>>
>>>> Verification is just the reversed way :-) .
>>>>
>>>> Regards,
>>>> Werner
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe at sip-communicator.dev.java.net
>>>> For additional commands, e-mail: dev-help at sip-communicator.dev.java.net
>>>>
>>>>
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe at sip-communicator.dev.java.net
For additional commands, e-mail: dev-help at sip-communicator.dev.java.net





More information about the dev mailing list