jedisct1 / libsodium

A modern, portable, easy to use crypto library.
https://libsodium.org
Other
12.18k stars 1.73k forks source link

Detached Signature Test Vector Issue #964

Closed skaht closed 4 years ago

skaht commented 4 years ago

The following code was whittled down from code that was unreliable at generating results to a test vector documented in an IETF draft. The reliability of generating matching results was a function of the source code and was independent upon the compiler (e.g., using clang++ or g++) and the distribution of the code (e.g., using Homebrew or compiling straight from https://github.com/jedisct1/libsodium/tree/stable). An independent 3rd party replicated the same issue within their development environment.

Any assistance for trouble shooting the following standalone code will be greatly appreciated.

cat sign_isolate_D.cpp

// TRL 3  - exploration code

#include <iostream>  // for C++ version of strlen(), strcmp()
#include <stdio.h>

#include <sodium.h>

// Signatures - https://doc.libsodium.org/public-key_cryptography/public-key_signatures#detached-mode
// 
// SOURCES FOR TEST VECTOR:
//          SECRET_KEY  from Section A.2 of https://tools.ietf.org/pdf/draft-koch-eddsa-for-openpgp-04.pdf
//          MESSAGE     from Section A.2 of https://tools.ietf.org/pdf/draft-koch-eddsa-for-openpgp-04.pdf
//          ASCII_ENCODED_MESSAGE_SIGNATURE obtained by plugging SECRET_KEY and MESSAGE into https://quirks.ed25519.info/basics/
//          HEX_ENCODED_MESSAGE_SIGNATURE   obtained from R & S components of Section A.2 of https://tools.ietf.org/pdf/draft-koch-eddsa-for-openpgp-04.pdf

const char *SECRET_KEY = "1a8b1ff05ded48e18bf50166c664ab023ea70003d78d9e41f5758a91d850f8d2",
           *MESSAGE    = "f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280";

const char *ASCII_ENCODED_MESSAGE_SIGNATURE = "551d9557ccab19d2699dcddd408c13484d941ca99b98c816f4fdb72779dc72d6aab726e8a37a2f831349012dcfee4e1358ae2c00d1ad1c2078171eb924d7f604",
           *HEX_ENCODED_MESSAGE_SIGNATURE   = "56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed3366d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404";

int main()
{
  if( sodium_init() < 0 )
  {
    fprintf( stderr, "Error: the Sodium Library couldn't be initialized, not safe to use!\n" );
    return( 1 );
  }

  const char          *hex_secret_key = (const char*)SECRET_KEY;
  const unsigned char *message        = (const unsigned char*)MESSAGE;
  int                  message_length = (int)strlen( (const char*)message );
  unsigned char        sk[crypto_sign_ed25519_SECRETKEYBYTES] = { 0 };        
  char                 hex_ed_signature[129];

  fprintf( stdout, " Seed                                  : %s\n", hex_secret_key );
  sodium_hex2bin( sk, sizeof(sk), (const char*)hex_secret_key, message_length, NULL, NULL, NULL ); // converts hex secret key to binary sk

// MESSAGE TO BE SIGNED IS ASCII ENCODED
  {
    unsigned char     signature[crypto_sign_BYTES];

    if( crypto_sign_detached( signature, NULL, message, message_length, sk ) < 0 )
      fprintf( stderr, "Failed to create an ED25519 Signature for round 1! \n" );
    sodium_bin2hex( hex_ed_signature, 129, signature, 64 );

    if( strcmp( ASCII_ENCODED_MESSAGE_SIGNATURE, hex_ed_signature ) )
      fprintf( stderr, "\nFailed ASCII Message Signature Calculation Test!" );

    fprintf( stdout, "\n" );
    fprintf( stdout, " Message is ASCII                      : %s\n", (char*)message );
    fprintf( stdout, "         ED25519 R Signature Component : %.64s\n", &hex_ed_signature[0]  );
    fprintf( stdout, "         ED25519 S Signature Component : %.64s\n", &hex_ed_signature[64] );
  }

// MESSAGE TO BE SIGNED IS HEX ENCODED  (For the test vector above, assumes the message is HEX-encoded binary. Hence, the message length is divided by 2 below.)
  {
    unsigned char  MESSAGE_SHORT[1024];
    unsigned char  signature[crypto_sign_BYTES];

    sodium_hex2bin( MESSAGE_SHORT, sizeof(MESSAGE_SHORT), (const char*)message, message_length, NULL, NULL, NULL );

    if( crypto_sign_detached( signature, NULL, MESSAGE_SHORT, message_length/2, sk ) < 0 )
      fprintf( stderr, "Failed to create an ED25519 Signature for round 2! \n" );
    sodium_bin2hex( hex_ed_signature, 129, signature, 64 );

    if( strcmp( HEX_ENCODED_MESSAGE_SIGNATURE, hex_ed_signature ) )
      fprintf( stderr, "\nFailed Hex ASCII Signature Calculation Test!" );

    fprintf( stdout, "\n" );
    fprintf( stdout, " Message is binary encoded as HEX      : %s\n", (char*)message );
    fprintf( stdout, "         ED25519 R Signature Component : %.64s\n", &hex_ed_signature[0]  );
    fprintf( stdout, "         ED25519 S Signature Component : %.64s\n", &hex_ed_signature[64] );
  }

  fprintf( stdout, "\n" );

  return( 0 );
}

If executes properly, the results will be:

% ./sign

 Seed                                  : 1a8b1ff05ded48e18bf50166c664ab023ea70003d78d9e41f5758a91d850f8d2

 Message is ASCII                      : f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280
         ED25519 R Signature Component : 551d9557ccab19d2699dcddd408c13484d941ca99b98c816f4fdb72779dc72d6
         ED25519 S Signature Component : aab726e8a37a2f831349012dcfee4e1358ae2c00d1ad1c2078171eb924d7f604

 Message is binary encoded as HEX      : f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280
         ED25519 R Signature Component : 56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed3366
         ED25519 S Signature Component : d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404

Actual results for executing the code above:

% ./sign1

Seed                                  : 1a8b1ff05ded48e18bf50166c664ab023ea70003d78d9e41f5758a91d850f8d2

Failed ASCII Message Signature Calculation Test!
 Message is ASCII                      : f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280
         ED25519 R Signature Component : 551d9557ccab19d2699dcddd408c13484d941ca99b98c816f4fdb72779dc72d6
         ED25519 S Signature Component : 184d677dcfa8065d9a8280f45fb0c09707d7c77c33f7197e326f28ac18366f07

Failed Hex ASCII Signature Calculation Test!
 Message is binary encoded as HEX      : f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280
         ED25519 R Signature Component : 56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed3366
         ED25519 S Signature Component : 6cc7e5de6c3da0ba7f9f1f236faa2adfc11e762d6b25245fbc8cf3eb13844301

Note that the R components of the signatures match, but the S components differ.

jedisct1 commented 4 years ago

SECRET_KEY = "1a8b1ff05ded48e18bf50166c664ab023ea70003d78d9e41f5758a91d850f8d2"

This is 32 bytes, while crypto_sign_SECRETBYTES is 64.

Your secret is actually the seed. In order to create an actual keypair from the seed, use crypto_sign_seed_keypair().

skaht commented 4 years ago

Your assistance is greatly appreciated:-)

I'm using SLIP 10 technology as the basis to synthesize the private keys. So I'm discovering by trial-and-error the hidden libsodium state that needs to be properly set to initialize libsodium's private keys to properly synthesize associated public keys, and to fully initialize libsodium's private keys to perform signature operations.

Added the following to the code above to fix the issue identified:

After "unsigned char sk[crypto_sign_ed25519_SECRETKEYBYTES] = { 0 };" :

unsigned char seed[crypto_sign_SEEDBYTES]; unsigned char ed25519_pk[crypto_sign_ed25519_PUBLICKEYBYTES];

After " sodium_hex2bin( sk, sizeof(sk), (const char*)hex_secret_key, message_length, NULL, NULL, NULL ); // converts hex secret key to binary sk":

crypto_sign_ed25519_sk_to_seed( seed, sk ); crypto_sign_seed_keypair( ed25519_pk, sk, seed );