Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SST scheme #93

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/lms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,49 @@ jobs:
package: ${{ github.workflow }}
target: ${{ matrix.target }}
features: ${{ matrix.features }}

# Demo scripts
demo-lms:
needs: set-msrv
strategy:
matrix:
rust:
- stable
features:
- default

runs-on: ubuntu-latest
defaults:
run:
# Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml
working-directory: .
steps:
- uses: actions/checkout@v4
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ matrix.rust }}
- run: sh scripts/lms-demo.sh

# Demo scripts
demo-sst:
needs: set-msrv
strategy:
matrix:
rust:
- stable
features:
- default

runs-on: ubuntu-latest
defaults:
run:
# Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml
working-directory: .
steps:
- uses: actions/checkout@v4
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ matrix.rust }}
- run: sh scripts/sst-demo.sh
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ rand = { version = "0.8.3", optional = true }


[dev-dependencies]
clap = "3.0.0"
clap = "4.0.0"
tempfile = "3.2.0"
hex = "0.4.3"
rand = "0.8.3"
Expand Down
41 changes: 11 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,8 @@ This implementation is binary compatible with the reference implementation found
This crate does not require the standard library (i.e. no_std capable) and can be easily used for bare-metal programming.

## Demo
A demo application is located in the `examples` folder to demonstrate the use of the library.
This demo application can be used in the console as follows:

```
# Key generation
# Generates `mykey.prv`, `mykey.pub` with merkle tree height 10 and winternitz parameter 2
cargo run --release --example lms-demo -- genkey mykey 10/2 --seed 0123456701234567012345670123456701234567012345670123456701234567

# Signing
# Generates `message.txt.sig`
cargo run --release --example lms-demo -- sign mykey message.txt

# Signing (fast_verification)
# Generates `message.txt_mut`, `message.txt_mut.sig`
HBS_LMS_MAX_HASH_OPTIMIZATIONS=1000 HBS_LMS_THREADS=2 cargo run --release --example lms-demo \
--features fast_verify -- sign_mut mykey message.txt

# Verification
# Verifies `message.txt` with `message.txt.sig` against `mykey.pub`
cargo run --release --example lms-demo -- verify mykey message.txt
```
Two demo applications are located in the `examples` folder to demonstrate the use of the library.
The examples are the [lms-demo](scripts/lms-demo.sh) and the [sst-demo](scripts/sst-demo.sh).

## Naming conventions wrt to the IETF RFC
The naming in the RFC is done by using a single character.
Expand All @@ -41,15 +22,15 @@ The following table shows the mapping between the RFC and the library naming inc

| RFC Naming | Library Naming | Meaning |
|------------|----------------------|-----------------------------------------------------------|
| I | lms_tree_identifier | 16-byte random value to identify a single LMS tree |
| q | lms_leaf_identifier | 4-byte value to identify all leafs in a single LMS tree |
| C | signature_randomizer | 32-byte random value added to every signature |
| Q | message_hash | Output of hashed message together with I, q, D_MESG and C |
| y | signature_data | The actual data of the signature |
| p | hash_chain_count | The number of hash chains for a certain W parameter |
| ls | checksum_left_shift | How many bits the checksum is shifted into the coef-value |
| n | hash_function_output_size | Number of bytes that the lm_ots hash functions generates |
| m | hash_function_output_size | Number of bytes that the lms hash functions generates |
| I | lms_tree_identifier | 16-byte random value to identify a single LMS tree |
| q | lms_leaf_identifier | 4-byte value to identify all leafs in a single LMS tree |
| C | signature_randomizer | 32-byte random value added to every signature |
| Q | message_hash | Output of hashed message together with I, q, D_MESG and C |
| y | signature_data | The actual data of the signature |
| p | num_winternitz_chains | The number of hash chains for a certain W parameter |
| ls | checksum_left_shift | How many bits the checksum is shifted into the coef-value |
| n | hash_function_output_size | Number of bytes that the lm_ots hash functions generates |
| m | hash_function_output_size | Number of bytes that the lms hash functions generates |

## Minimum Supported Rust Version
The crate in this repository supports Rust **1.63** or higher.
Expand Down
52 changes: 34 additions & 18 deletions benches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,13 @@ mod tests {
signing_key
}

fn generate_verifying_key_and_signature() -> (VerifyingKey<Sha256_256>, Signature) {
fn generate_verifying_key_and_signature(
hss_parameter: &[HssParameter<Sha256_256>],
) -> (VerifyingKey<Sha256_256>, Signature) {
let mut seed = Seed::default();
OsRng.fill_bytes(seed.as_mut_slice());
let (mut signing_key, verifying_key) = keygen::<Sha256_256>(
&[HssParameter::new(
LmotsAlgorithm::LmotsW2,
LmsAlgorithm::LmsH5,
)],
&seed,
None,
)
.unwrap();
let (mut signing_key, verifying_key) =
keygen::<Sha256_256>(&hss_parameter, &seed, None).unwrap();

let signature = signing_key.try_sign(&MESSAGE).unwrap();

Expand Down Expand Up @@ -135,7 +130,7 @@ mod tests {
b.iter(|| {
let mut signing_key = signing_key.clone();
signing_key
.try_sign_with_aux(&MESSAGE, Some(aux_slice))
.try_sign_with_aux(&MESSAGE, Some(aux_slice), None)
.unwrap()
});
}
Expand All @@ -153,7 +148,7 @@ mod tests {
b.iter(|| {
let mut signing_key = signing_key.clone();
signing_key
.try_sign_with_aux(&MESSAGE, Some(aux_slice))
.try_sign_with_aux(&MESSAGE, Some(aux_slice), None)
.unwrap()
});
}
Expand All @@ -171,7 +166,7 @@ mod tests {
b.iter(|| {
let mut signing_key = signing_key.clone();
signing_key
.try_sign_with_aux(&MESSAGE, Some(aux_slice))
.try_sign_with_aux(&MESSAGE, Some(aux_slice), None)
.unwrap()
});
}
Expand Down Expand Up @@ -203,23 +198,44 @@ mod tests {
b.iter(|| {
let mut signing_key = signing_key.clone();
signing_key
.try_sign_with_aux(&MESSAGE, Some(aux_slice))
.try_sign_with_aux(&MESSAGE, Some(aux_slice), None)
.unwrap()
});
}

#[bench]
fn verify(b: &mut Bencher) {
let (verifying_key, signature) = generate_verifying_key_and_signature();
fn verify_h5w2(b: &mut Bencher) {
let hss_parameter = [HssParameter::new(
LmotsAlgorithm::LmotsW2,
LmsAlgorithm::LmsH5,
)];
let (verifying_key, signature) = generate_verifying_key_and_signature(&hss_parameter);

b.iter(|| {
let _ = verifying_key.verify(&MESSAGE, &signature).is_ok();
});
}

#[bench]
fn verify_h5w2_h5w2(b: &mut Bencher) {
let hss_parameter = [
HssParameter::new(LmotsAlgorithm::LmotsW2, LmsAlgorithm::LmsH5),
HssParameter::new(LmotsAlgorithm::LmotsW2, LmsAlgorithm::LmsH5),
];
let (verifying_key, signature) = generate_verifying_key_and_signature(&hss_parameter);

b.iter(|| {
let _ = verifying_key.verify(&MESSAGE, &signature).is_ok();
});
}

#[bench]
fn verify_reference(b: &mut Bencher) {
let (verifying_key, signature) = generate_verifying_key_and_signature();
fn verify_reference_h5w2(b: &mut Bencher) {
let hss_parameter = [HssParameter::new(
LmotsAlgorithm::LmotsW2,
LmsAlgorithm::LmsH5,
)];
let (verifying_key, signature) = generate_verifying_key_and_signature(&hss_parameter);
let ref_signature = VerifierSignature::from_ref(signature.as_ref()).unwrap();

b.iter(|| {
Expand Down
35 changes: 17 additions & 18 deletions examples/lms-demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl DemoError {
}
}

type Hasher = Sha256_256;
type Hasher = Sha256_192;

struct GenKeyParameter {
parameter: Vec<HssParameter<Hasher>>,
Expand Down Expand Up @@ -95,7 +95,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if let Some(args) = matches.subcommand_matches(VERIFY_COMMAND) {
let result = verify(args);
if result {
println!("Successful!");
println!("Verification successful!");
exit(0);
} else {
println!("Wrong signature");
Expand Down Expand Up @@ -146,13 +146,15 @@ fn sign(args: &ArgMatches) -> Result<(), std::io::Error> {
&private_key_data,
&mut private_key_update_function,
Some(aux_slice),
None,
)
} else {
hbs_lms::sign::<Hasher>(
&message_data,
&private_key_data,
&mut private_key_update_function,
None,
None,
)
};

Expand Down Expand Up @@ -289,22 +291,19 @@ fn genkey(args: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let genkey_parameter = parse_genkey_parameter(&get_parameter(PARAMETER_PARAMETER, args));
let parameter = genkey_parameter.parameter;

let seed: Seed<Hasher> = if let Some(seed) = args.value_of(SEED_PARAMETER) {
let decoded = hex::decode(seed)?;
if decoded.len() < Hasher::OUTPUT_SIZE as usize {
let error = format!(
"Seed is too short ({} of {} required bytes)",
decoded.len(),
Hasher::OUTPUT_SIZE
);
return DemoError::raise(error);
}
let mut seed = Seed::default();
seed.as_mut_slice().copy_from_slice(&decoded[..]);
seed
} else {
return DemoError::raise("Seed was not given".to_string());
};
let encoded_seed = args
.value_of(SEED_PARAMETER)
.ok_or(DemoError("No seed given".to_string()))?;
let decoded_seed = hex::decode(encoded_seed)?;
(decoded_seed.len() == Hasher::OUTPUT_SIZE.into())
.then_some(())
.ok_or(DemoError(format!(
"Seed length is {} bytes, but length of {} bytes is expected",
decoded_seed.len(),
Hasher::OUTPUT_SIZE
)))?;
let mut seed = Seed::<Hasher>::default();
seed.as_mut_slice().copy_from_slice(&decoded_seed[..]);

let mut aux_data = vec![0u8; genkey_parameter.aux_data];
let aux_slice: &mut &mut [u8] = &mut &mut aux_data[..];
Expand Down
Loading
Loading