mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
ecdsa: Signer facade (gated under signer feature) (#109)
Adds a `ecdsa::signer::Signer` facade which handles parsing of `SecretKey` data and zeroization of the private scalar. The implementation is generic over the elliptic curve and digest function, providing a `RandomizedDigestSigner` impl, and also when the curve type impls `DigestPrimitive`, a `RandomizedSigner` impl.
This commit is contained in:
+17
-65
@@ -23,82 +23,34 @@ jobs:
|
||||
target:
|
||||
- thumbv7em-none-eabi
|
||||
- wasm32-unknown-unknown
|
||||
toolchain:
|
||||
rust:
|
||||
- 1.41.0 # MSRV
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-rust-${{ matrix.toolchain }}-${{ matrix.target }}-cargo-build-target-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
|
||||
- name: Run cargo build --no-default-features
|
||||
run: cargo build --no-default-features --release --target ${{ matrix.target }}
|
||||
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
|
||||
- run: cargo build --no-default-features --features signer --release --target ${{ matrix.target }}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
toolchain:
|
||||
rust:
|
||||
- 1.41.0 # MSRV
|
||||
- stable
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-rust-${{ matrix.toolchain }}-cargo-build-target-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
|
||||
- name: Run cargo test --lib
|
||||
run: cargo test --lib --release
|
||||
|
||||
- name: Run cargo test --all-features
|
||||
run: cargo test --all-features --release
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo check --all-features
|
||||
- run: cargo test --no-default-features
|
||||
- run: cargo test
|
||||
- run: cargo test --all-features
|
||||
|
||||
Generated
+18
-3
@@ -45,10 +45,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.5.0-pre"
|
||||
source = "git+https://github.com/RustCrypto/traits#f8a916bbcff07aece243db24f17f663d4077baba"
|
||||
source = "git+https://github.com/RustCrypto/traits#cb1fdb7ca135d02f9cadf819e036e95d024248f2"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -61,6 +63,12 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.114"
|
||||
@@ -69,11 +77,12 @@ checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.1.0"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65211b7b6fc3f14ff9fc7a2011a434e3e6880585bd2e9e9396315ae24cbf7852"
|
||||
checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -93,3 +102,9 @@ name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8"
|
||||
|
||||
+4
-1
@@ -20,7 +20,7 @@ default-features = false
|
||||
features = ["weierstrass"]
|
||||
|
||||
[dependencies.signature]
|
||||
version = ">= 1.1.0, < 1.2.0"
|
||||
version = ">= 1.2.2, < 1.3.0"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
@@ -28,7 +28,10 @@ default = ["digest", "std"]
|
||||
dev = []
|
||||
digest = ["signature/digest-preview"]
|
||||
hazmat = []
|
||||
rand = ["elliptic-curve/rand_core", "signature/rand-preview"]
|
||||
signer = ["digest", "hazmat", "rand", "zeroize"] # TODO(tarcieri): deterministic signing
|
||||
std = ["elliptic-curve/std", "signature/std"]
|
||||
zeroize = ["elliptic-curve/zeroize"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["digest", "std"]
|
||||
|
||||
+5
-3
@@ -12,6 +12,7 @@
|
||||
//! FULL PRIVATE KEY RECOVERY!
|
||||
|
||||
use crate::{Signature, SignatureSize};
|
||||
use core::borrow::Borrow;
|
||||
use elliptic_curve::{
|
||||
generic_array::ArrayLength, ops::Invert, weierstrass::Curve, Arithmetic, ScalarBytes,
|
||||
};
|
||||
@@ -34,9 +35,10 @@ where
|
||||
///
|
||||
/// Accepts the following arguments:
|
||||
///
|
||||
/// - `ephemeral_scalar`: ECDSA `k` value (MUST BE UNIFORMLY RANDOM!!!)
|
||||
/// - `hashed_msg`: prehashed message to be signed
|
||||
fn try_sign_prehashed<K: AsRef<C::Scalar> + Invert<Output = C::Scalar>>(
|
||||
/// - `ephemeral_scalar`: ECDSA `k` value. MUST BE UNIFORMLY RANDOM!!!
|
||||
/// - `hashed_msg`: hashed message digest to be signed.
|
||||
/// MUST BE OUTPUT OF A CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!!
|
||||
fn try_sign_prehashed<K: Borrow<C::Scalar> + Invert<Output = C::Scalar>>(
|
||||
&self,
|
||||
ephemeral_scalar: &K,
|
||||
hashed_msg: &ScalarBytes<C>,
|
||||
|
||||
@@ -33,6 +33,10 @@ pub mod dev;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
|
||||
pub mod hazmat;
|
||||
|
||||
#[cfg(feature = "signer")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "signer")))]
|
||||
pub mod signer;
|
||||
|
||||
// Re-export the `elliptic-curve` crate (and select types)
|
||||
pub use elliptic_curve::{
|
||||
self, generic_array,
|
||||
@@ -43,6 +47,9 @@ pub use elliptic_curve::{
|
||||
// Re-export the `signature` crate (and select types)
|
||||
pub use signature::{self, Error};
|
||||
|
||||
#[cfg(feature = "signer")]
|
||||
pub use signer::Signer;
|
||||
|
||||
use core::{
|
||||
convert::TryFrom,
|
||||
fmt::{self, Debug},
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
//! ECDSA signer. Generic over elliptic curves.
|
||||
//!
|
||||
//! Requires an [`elliptic_curve::Arithmetic`] impl on the curve, and a
|
||||
//! [`SignPrimitive`] impl on its associated `Scalar` type.
|
||||
|
||||
// TODO(tarcieri): RFC 6979; support for hardware crypto accelerators
|
||||
|
||||
use crate::{
|
||||
hazmat::{DigestPrimitive, SignPrimitive},
|
||||
Error, Signature, SignatureSize,
|
||||
};
|
||||
use elliptic_curve::{
|
||||
generic_array::ArrayLength,
|
||||
ops::Invert,
|
||||
secret_key::{FromSecretKey, SecretKey},
|
||||
weierstrass::Curve,
|
||||
zeroize::{Zeroize, Zeroizing},
|
||||
Arithmetic,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use {
|
||||
elliptic_curve::Generate,
|
||||
signature::{
|
||||
digest::Digest,
|
||||
rand_core::{CryptoRng, RngCore},
|
||||
RandomizedDigestSigner, RandomizedSigner,
|
||||
},
|
||||
};
|
||||
|
||||
/// ECDSA signer
|
||||
pub struct Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic,
|
||||
C::Scalar: Invert<Output = C::Scalar> + SignPrimitive<C> + Zeroize,
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
secret_scalar: C::Scalar,
|
||||
}
|
||||
|
||||
impl<C> Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic,
|
||||
C::Scalar: Invert<Output = C::Scalar> + SignPrimitive<C> + Zeroize,
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
/// Create a new signer
|
||||
pub fn new(secret_key: &SecretKey<C>) -> Result<Self, Error> {
|
||||
let scalar = C::Scalar::from_secret_key(secret_key);
|
||||
|
||||
if scalar.is_some().into() {
|
||||
Ok(Self {
|
||||
secret_scalar: scalar.unwrap(),
|
||||
})
|
||||
} else {
|
||||
Err(Error::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
|
||||
impl<C, D> RandomizedDigestSigner<D, Signature<C>> for Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic,
|
||||
D: Digest<OutputSize = C::ElementSize>,
|
||||
C::Scalar: Invert<Output = C::Scalar> + Generate + SignPrimitive<C> + Zeroize,
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
fn try_sign_digest_with_rng(
|
||||
&self,
|
||||
rng: impl CryptoRng + RngCore,
|
||||
digest: D,
|
||||
) -> Result<Signature<C>, Error> {
|
||||
let ephemeral_scalar = Zeroizing::new(C::Scalar::generate(rng));
|
||||
|
||||
self.secret_scalar
|
||||
.try_sign_prehashed(&*ephemeral_scalar, &digest.finalize())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
|
||||
impl<C> RandomizedSigner<Signature<C>> for Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic + DigestPrimitive,
|
||||
C::Digest: Digest<OutputSize = C::ElementSize>,
|
||||
C::Scalar: Invert<Output = C::Scalar> + Generate + SignPrimitive<C> + Zeroize,
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
fn try_sign_with_rng(
|
||||
&self,
|
||||
rng: impl CryptoRng + RngCore,
|
||||
msg: &[u8],
|
||||
) -> Result<Signature<C>, Error> {
|
||||
self.try_sign_digest_with_rng(rng, C::Digest::new().chain(msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Zeroize for Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic,
|
||||
C::Scalar: Invert<Output = C::Scalar> + SignPrimitive<C> + Zeroize,
|
||||
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
self.secret_scalar.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Drop for Signer<C>
|
||||
where
|
||||
C: Curve + Arithmetic,
|
||||
C::Scalar: Invert<Output = C::Scalar> + SignPrimitive<C> + Zeroize,
|
||||
|
||||
SignatureSize<C>: ArrayLength<u8>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user