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

Add HD wallets support #68

Merged
merged 15 commits into from
Jan 26, 2024
1 change: 0 additions & 1 deletion .github/workflows/deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ jobs:
| sed -e '/ALL_DEPS_DIFF/{r all-deps-diff' -e 'd}' \
> pr-comment
- name: Publish PR comment
continue-on-error: true
uses: marocchino/sticky-pull-request-comment@v2
with:
header: deps-diff
Expand Down
36 changes: 32 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ on:
branches: [ "m" ]
pull_request:
branches: [ "*" ]
# schedule:
# - cron: '0 5 * * *'

env:
CARGO_TERM_COLOR: always
Expand All @@ -21,21 +19,52 @@ jobs:
with:
cache-on-failure: "true"
- name: Build
run: cargo build --release
run: cargo build --release --all-features
- uses: actions/upload-artifact@v3
with:
name: benchmark-tool
path: target/release/measure_perf
# Run tests without HD wallets support
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: "true"
- name: Run tests
run: cargo test -r
# Run tests including HD wallets support
test-hd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: "true"
- name: Run tests
run: cargo test -r --all-features
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check formatting
run: cargo fmt --all -- --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: "true"
- name: Run clippy
run: cargo clippy --all --lib --exclude cggmp21-tests -- --no-deps -D clippy::all -D clippy::unwrap_used -D clippy::expect_used
- name: Run clippy tests
run: cargo clippy --tests --lib -- -D clippy::all
bench:
runs-on: ubuntu-latest
permissions:
pull-requests: write
needs: build
steps:
- uses: actions/checkout@v3
Expand All @@ -49,7 +78,6 @@ jobs:
./measure_perf -n 3 --no-bench-primes-gen > perf_output
sed -e '/PERF_OUTPUT/{r perf_output' -e 'd}' .github/pr-comment.tpl > pr-comment
- name: Leave PR comment
continue-on-error: true
uses: marocchino/sticky-pull-request-comment@v2
with:
header: bench
Expand Down
9 changes: 2 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,5 @@
.helix/
.rstags

spec/main.aux

spec/main.log

spec/main.out

spec/main.pdf
!spec/main.tex
spec/main.*
38 changes: 31 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
.PHONY: docs docs-open docs-private readme readme-check

docs:
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --no-deps
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --all-features --no-deps

docs-open:
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --no-deps --open
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --all-features --no-deps --open

docs-private:
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --no-deps --document-private-items
RUSTDOCFLAGS="--html-in-header katex-header.html --cfg docsrs" cargo +nightly doc --all-features --no-deps --document-private-items

readme:
cargo readme -i src/lib.rs -r cggmp21/ -t ../docs/README.tpl --no-indent-headings \
| sed -E 's/(\/\*.+\*\/)/\1;/' \
| sed -E '/^\[`.+`\]:/d' \
| sed -E 's/\[`([^`]*)`\]\(.+?\)/`\1`/g' \
| sed -E 's/\[`([^`]*)`\]/`\1`/g' \
| perl -ne 's/(?<!!)\[([^\[]+?)\]\([^\(]+?\)/\1/g; print;' \
| sed -E '/^#$$/d' \
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This crate implements:
* 3+1 rounds threshold and non-threshold signing
* Auxiliary info generation protocol
* Key refresh for non-threshold keys
* HD-wallets support based on [slip10] standard (compatible with [bip32]) \
Requires `hd-wallets` feature

We also provide auxiliary tools like:
* Secret key reconstruction (exporting key from TSS)
Expand Down Expand Up @@ -142,6 +144,19 @@ Alternatively, you can generate presignature and use it to sign data:
**Never reuse presignatures!** If you use the same presignature to sign two different messages,
it leaks private key to anyone who can observe the signatures.

## HD wallets support
Library supports non-hardened deterministic key derivation based on [slip10] standard (compatible
with [bip32]). It allows signers to generate a master key once, and then use it to instantaneously
derive as many child keys as needed. Child key derivation takes place within signing protocol
practically at no cost.

In order to use HD wallets, `hd-wallets` feature must be enabled. Then, a master key needs to be
generated by running a regular key generation protocol with `hd_wallet`
set to `true`.

When master key is generated, you can issue a signature for child key by setting
derivation path in the signing.

## SPOF code: Key Import and Export
CGGMP21 protocol is designed to avoid Single Point of Failure by guaranteeing that attacker would
need to compromise threshold amount of nodes to obtain a secret key. However, some use-cases may
Expand All @@ -165,6 +180,8 @@ they are all documented in [the spec].
[CGGMP21]: https://ia.cr/2021/060
[the spec]: https://dfns.github.io/cggmp21/cggmp21-spec.pdf
[security guidelines]: #security-guidelines
[slip10]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md
[bip32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

## Timing attacks
Timing attacks are type of side-channel attacks that leak sensitive information through duration of
Expand Down
5 changes: 4 additions & 1 deletion cggmp21/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ futures = "0.3"
thiserror = "1"

serde = { version = "1", features = ["derive", "rc"] }
serde_with = "2"
serde_with = { version = "2" }
hex = { version = "0.4", default-features = false, features = ["serde"] }

slip-10 = { git = "https://github.com/dfns/slip-10", branch = "m", optional = true, features = ["std"] }

[dev-dependencies]
round-based = { version = "0.2", features = ["derive", "dev"] }

Expand All @@ -46,6 +48,7 @@ all-curves = ["curve-secp256k1", "curve-secp256r1", "curve-stark"]
curve-secp256k1 = ["generic-ec/curve-secp256k1"]
curve-secp256r1 = ["generic-ec/curve-secp256r1"]
curve-stark = ["generic-ec/curve-stark"]
hd-wallets = ["dep:slip-10"]
spof = []

[package.metadata.docs.rs]
Expand Down
56 changes: 56 additions & 0 deletions cggmp21/src/key_share.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ pub struct DirtyIncompleteKeyShare<E: Curve> {
pub public_shares: Vec<Point<E>>,
/// Verifiable secret sharing setup, present if key was generated using VSS scheme
pub vss_setup: Option<VssSetup<E>>,
/// Chain code generated at keygen if HD wallets support was enabled
#[cfg(feature = "hd-wallets")]
#[serde(
default,
with = "serde_with::As::<Option<crate::utils::serde::HexOrBin>>"
)]
pub chain_code: Option<slip_10::ChainCode>,
/// Secret share $x_i$
#[serde_as(as = "Compact")]
pub x: SecretScalar<E>,
Expand Down Expand Up @@ -210,6 +217,44 @@ impl<E: Curve> DirtyIncompleteKeyShare<E> {
}
Ok(())
}

/// Checks whether HD wallet support was enabled for this key
///
/// In order to generate an HD wallet, [`.hd_wallet(true)`](crate::keygen::GenericKeygenBuilder::hd_wallet)
/// needs to be set at key generation.
#[cfg(feature = "hd-wallets")]
pub fn is_hd_wallet(&self) -> bool {
self.chain_code.is_some()
}

/// Returns extended public key, if HD support was enabled
#[cfg(feature = "hd-wallets")]
pub fn extended_public_key(&self) -> Option<slip_10::ExtendedPublicKey<E>> {
Some(slip_10::ExtendedPublicKey {
public_key: self.shared_public_key,
chain_code: self.chain_code?,
})
}

/// Derives child public key, if it's HD key
#[cfg(feature = "hd-wallets")]
pub fn derive_child_public_key<ChildIndex>(
&self,
derivation_path: impl IntoIterator<Item = ChildIndex>,
) -> Result<
slip_10::ExtendedPublicKey<E>,
HdError<<ChildIndex as TryInto<slip_10::NonHardenedIndex>>::Error>,
>
where
slip_10::NonHardenedIndex: TryFrom<ChildIndex>,
{
let epub = self.extended_public_key().ok_or(HdError::DisabledHd)?;
slip_10::try_derive_child_public_key_with_path(
&epub,
derivation_path.into_iter().map(|index| index.try_into()),
)
.map_err(HdError::InvalidPath)
}
}

impl<L: SecurityLevel> AuxInfo<L> {
Expand Down Expand Up @@ -788,3 +833,14 @@ enum ReconstructErrorReason {
#[error("interpolation failed (seems like a bug)")]
Interpolation,
}

/// Error related to HD key derivation
#[derive(Debug, Error)]
pub enum HdError<E> {
/// HD derivation is disabled for the key
#[error("HD derivation is disabled for the key")]
DisabledHd,
/// Derivation path is not valid
#[error("derivation path is not valid")]
InvalidPath(#[source] E),
}
Loading
Loading