/cpp-hsslms

C++ Implementation of Leighton-Micali Hash-Based Signatures

Primary LanguageC++MIT LicenseMIT

cpp-hsslms

This is an implementation of Leighton-Micali Hash-Based Signatures in C++ according to RFC 8554. It is a stateful hash-based signature scheme, see also NIST.

The implementation is meant as a reference and for educational purposes. A similar implementation in Python can be found here.

The implementation provides 4 classes:

  • LM-OTS One-Time Signatures, i.e. LM_OTS_Priv, LM_OTS_Pub. These are one-time signatures; each private key MUST be used at most one time to sign a message.
  • Leighton-Micali Signatures (LMS), i.e. LMS_Priv, LMS_Pub. This system holds a fixed number of one-time signatures, i.e. LM-OTS.
  • Hierarchical Signatures (HSS), i.e. HSS_Priv, HSS_Pub. This system uses a sequence of LMS.
  • Persistent Hierarchical Signatures, i.e. PersHSS_Priv. A child of HSS_Priv where the private key is stored in an encrypted file.

The only dependency is OpenSSL for key derivation PKCS5_PBKDF2_HMAC, random number generation RAND_priv_bytes, hashing SHA-256 and encryption with autentication AES-GCM. If the class PersHSS_Priv is not needed the only dependency to be fulfilled is for random number generation and hashing.

The projects compiles to an executable hbslms with a command line interface.

>> hbslms -h
Hierarchical Signature System of Leighton-Micali Hash-Based Signatures according to RFC 8554

Returns 0 if a command is executed without error and if a verification succeeded.

optional arguments:
  -h            show this help message and exit

possible commands:
  -a {key-gen,sign,verify,test,performance}


command -a key-gen:
  generates a key pair  arguments:
    -l [1234][56789]+  one LMOTS-typecode and at least one LMS-typecode
    -o filename  filename of private key, ".pub" is appended to the filename of the pubklic key
    -p password  password to encrypt the private key
    -c number  number of cpu cores (1-#logical cores) for computation

command -a pubkey-gen:
  generates the public key from a private key  arguments:
    -k filename  filename of private key
    -p password  password to encrypt the private key
    -o filename  filename of the public key

command -a sign:
  generates a signature  arguments:
    -k filename  filename of private key
    -m filename  filename of message to be read and signed
    -o filename  filename of signature to be stored
    -p password  password to encrypt the private key
    -c number  number of cpu cores (1-#logical cores) for computation

command -a verify:
  verifies a signature  arguments:
    -k filename  filename of public key
    -m filename  filename of message to be read
    -s filename  filename of signature to be read

command -a test:
  performs a number of tests.
command -a performance:
  does some performance measurements  arguments:
    -c number  number of cpu cores (1-#logical cores) for computation

Compilation

cmake --build "target directory/" --target all

Example Usage of the Command Line Interface

If the return code of the executable hsslms is not 0 an error occurred.

Key Generation

The following command computes a key pair with with LMOTS algorithm type LMOTS_SHA256_N32_W8 and LMS algorithms types LMS_SHA256_M32_H10, LMS_SHA256_M32_H5. The private key is stored to testkey the public key to testkey.pub. The private key ist protected by the password password. The computation uses 2 threads.

hbslms -l 465 -k testkey -p password -c 2

Signature Generation

The following command computes a signature with the private key testkey for the message message.bin. The signature ist stored to signature.bin.

hbslms -k testkey -p password -m message.bin -o signature.bin -c 2

Signature Verification

The signature generated by the last command can be verified by:

hbslms -k testkey.pub -m message.bin -s signature.bin

Example Usage in C++

LM-OTS

#include <cstring>
#include "lmots.h"

int main(int argc, char *argv[]) {
  std::array<uint8_t, 16> I {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
  LM_OTS_Priv sk = LM_OTS_Priv(LMOTS_SHA256_N32_W1, I, 99);
  std::string signature = sk.sign("abc");
  LM_OTS_Pub vk = sk.gen_pub();
  try {
    vk.verify("abc", signature);
    std::cout << "Vaild (-:" << std::endl;
  }
  catch (INVALID &e) {
    std::cout << "Invaild )-: " << e.what() << std::endl;
  }
}

LMS

#include <cstring>
#include "lms.h"

int main(int argc, char *argv[]) {
  LMS_Priv sk_lms = LMS_Priv(LMS_SHA256_M32_H5, LMOTS_SHA256_N32_W8, NUM_THREADS);
  signature = sk_lms.sign("abc");
  LMS_Pub vk_lms = sk_lms.gen_pub();
  try {
      vk_lms.verify("abc", signature);
      std::cout << "Vaild (-:" << std::endl;
  }
  catch (INVALID &e) {
      std::cout << "Invaild )-: " << e.what() << std::endl;
  }
}

HSS

#include <cstring>
#include "hss.h"

int main(int argc, char *argv[]) {
  HSS_Priv sk_hss = HSS_Priv(std::vector<LMS_ALGORITHM_TYPE>{LMS_SHA256_M32_H15, LMS_SHA256_M32_H15}, LMOTS_SHA256_N32_W8, NUM_THREADS);
  signature = sk_hss.sign("abc");
  HSS_Pub vk_hss = sk_hss.gen_pub();
  try {
      vk_hss.verify("abc", signature);
      std::cout << "Vaild (-:" << std::endl;
  }
  catch (INVALID &e) {
      std::cout << "Invaild )-: " << e.what() << std::endl;
  }
}

Performance Measurements

The measurements are done on a Ryzen 5800X, where multiprocessing features are used with 6 cores.

Key Generation

Key-Type Time[s] #Signatures Size of Signature
w 1 2 4 8   1 2 4 8
H5 0.001 0.002 0.001 0.003 32 8688 4464 2352 1296
H10 0.01 0.01 0.02 0.1 1024 8848 4624 2512 1456
H15 0.2 0.2 0.3 2.5 32768 9008 4784 2672 1616
H20 6.3 5.7 10.9 82 1048576 9168 4944 2832 1776
H25       2593 33554432       1936
H10/H10 0.02 0.02 0.03 0.2 1048576 17748 9300 5076 2964
H10/H15 0.2 0.2 0.3 2.7 33554432 17908 9460 5236 3124
H15/H15 0.4 0.4 0.7 5.1 1073741824 18068 9620 5396 3284

Performance of Signature Generation:

Key-Type Time[ms]
w 1 2 4 8
H15 0.01 0.02 0.03 0.2

Performance of Signature Verification:

Key-Type Time[ms]
w 1 2 4 8
H15 0.02 0.0 0.03 0.2

License

MIT