Zoltan Fridrich <zfrid...@redhat.com> writes:

> We want to use SLH-DSA for document signing. I think I will need to add the
> pure and prehash API signing functions. But otherwise whether shake or sha2
> variant is available it does not matter.

Do you think it would be reasonable to drop the sha2 variants in the
first iteration? Then we wouldn't get the complexities of dealing with
either "normal" or "compressed" addresses.

>> For the api, I think it should be possible to support "pure" signatures
>> without requiring all of the message in memory at once, using a
>> init/update/final-style interface for sign and verify, based on the
>> update function of the underlying hash. Is that worth while?
>
> Sounds like a good idea. I can add it in.

Unfortunately, after a closer look I don't think this is doable. For
pure signing, the message is hashed twice, once with the secret prf to
produce the randomizer, and then together with randomizer and public key
to produce the "digest". It should be possibly to do for the verify
operation only, but I don't think that's worth the effort.

> It seems to be a typo in the comment. When I checked the logic it is indeed
> pk: [PUB_SEED || root]

That's good.

>> For the Nettle api, I would also consider to not include the pubkey as
>> part of the secret key, but instead pass both pubkey and secret key to
>> the secret key functions. Similar to, e.g., ed25519_sha512_sign.
>
> That seems redundant to me when the spec defines a secret key to contain
> the public key too.
> Did you mean that the secret key shouldnt contain the public key part?

I'm thinking of something analogous to Nettle's ed25519 api,

  void
  ed25519_sha512_sign (const uint8_t *pub,
                       const uint8_t *priv,                    
                       size_t length, const uint8_t *msg,
                       uint8_t *signature);
  
  int
  ed25519_sha512_verify (const uint8_t *pub,
                         size_t length, const uint8_t *msg,
                         const uint8_t *signature);

Here, the pub argument is the same for both functions, and the priv
argument is the additional private information needed for signing. If we
do this for dsa-slh, pub would include PK.seed and PK.root, and priv
would include SK.seed and SK.prf, which seems straightforward to me.

(Signing applications would still be free to store public and private
parts together whenever convenient).

Nettle isn't totally consistent though, some older signing functions
take only a private key argument which then needs to include everything,
others take public key as an additional argument. But I prefer the
separate style (it's seems more flexible, and it's also more explicit
about what's public and private), if there's no strong reason to do it
differently.

>> I think I would prefer separate top-level functions for the different
>> algorithm variants, rather than the enum slh_dsa_mode. They could all
>> call the same internal function, passing the appropriate params struct
>> as argument.
>
> What about the pure and pre-hash variants and init, update and final api?
> Can you describe how you would like the API to look like?

Since it seems we can't have the init/update/final variant, I would suggest

  void
  dsa_slh_shake128_sign (const uint8_t *pub,
                         const uint8_t *priv,                    
                         size_t length, const uint8_t *msg,
                         uint8_t *signature);
  void
  dsa_slh_shake128_verify (const uint8_t *pub,
                           size_t length, const uint8_t *msg,
                           const uint8_t *signature);

for the pure variant, and

  void
  dsa_slh_shake128_ph_sha256_sign (const uint8_t *pub,
                                   const uint8_t *priv,                    
                                   const uint8_t *digest,
                                   uint8_t *signature);
  void
  dsa_slh_shake128_ph_sha256_verify (const uint8_t *pub,
                                     const uint8_t *digest,
                                     const uint8_t *signature);

for the prehash with sha256 variant. There will be lots of functions,
but they should all be simple wrappers around some internal function
like

  void
  _nettle_dsa_slh_sign_internal (const struct dsa_slh_variant *alg,
                                 const uint8_t *pub,
                                 const uint8_t *priv,
                                 size_t prefix_length, const uint8_t *prefix,
                                 size_t length, const uint8_t *msg,
                                 uint8_t *signature);

(with the prefix, including the extra zero bytes or prehash object id,
passed as a separate argument to avoid allocation and copying of the
message).

For link-time dependencies, it's desirable that code specific to one
slh-dsa variant is referenced indirectly via the struct dsa_slh_variant,
and that source files are organized so that applications using only a
single variant don't link-depend on unrelated code.

(The name dsa_slh_variant is a tentative name I made up, feel free to
use something different).

Another naming question, should it be dsa_slh_shake128, or maybe
dsa_slh128_shake ? There's a shake128 algorithm, but that's *not* used
for dsa-slh, it uses shake256. Instead, 128/192/256 refers to the size
of the values used internally in slh-dsa and in the resulting signature.

>> It's unclear how this differs from _nettle_treehash further above? Or in
>> general, whats the meaning of "x1" suffix on some functions?
>
> I don't exactly know but I think it is some kind of optimization where the
> number determines how many addresses are processed at once.
> Here is an example of x8 from the reference implementation
> https://github.com/sphincs/sphincsplus/blob/7ec789ace6874d875f4bb84cb61b81155398167e/sha2-avx2/utilsx8.c#L38

I don't get exactly how that 8-way operations works. It would be
interesting to know what performance improvements they see from that
(if it's a significant performance boost, we should consider adding
something like that later).

And it's still unclear to me what's the difference between
_nettle_treehash and _nettle_treehashx1 in the patch.

>> Not sure I get why wots_k_mask is used? Is it intended to improve
>> sidechannel-silence or performance?
>
> To me it seems to only switch the logic to generate WOTS signatures. I
> don't know whether it aims to improve either performance or
> sidechannel-silence.

I think it makes the code harder to understand when signature bytes are
copied out to the result in a conditional in the middle a loop (both for
wots and for the merkle inclusion proofs). I like the structure in my
prototype better, see 
https://git.lysator.liu.se/nisse/poc-slh-dsa/-/blob/main/wots.c?ref_type=heads#L120

  wots_chain (public_seed, addr, 0, msg, sig, sig);
  sha3_256_update (ctx, _SLH_DSA_128_SIZE,
                   wots_chain (public_seed, addr, msg, 15 - msg, sig, pub));

where first line produces the signature, and second line extends the
chain to produce the corresponding public key. But you probably don't
want to do any extensive refactoring now.

To aid later refactoring, it would help with unit tests for the various
components (even just using the intermadiate values from the top-level tests
is helpful; that's what I did for my prototype).

Another useful addition would be benchmarking in examples/hogweed-benchmark.c.

Regards,
/Niels

-- 
Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
Internet email is subject to wholesale government surveillance.
_______________________________________________
nettle-bugs mailing list -- nettle-bugs@lists.lysator.liu.se
To unsubscribe send an email to nettle-bugs-le...@lists.lysator.liu.se

Reply via email to