ecdsa-0.16.9/.cargo_vcs_info.json0000644000000001430000000000100122060ustar { "git": { "sha1": "abd0175f3ae2dc4b118fa914e94c1fc4b0182ba9" }, "path_in_vcs": "ecdsa" }ecdsa-0.16.9/CHANGELOG.md000064400000000000000000000473521046102023000126240ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.16.9 (2023-11-16) ### Changed - Loosen `signature` bound to `2.0, <2.3` ([#756]) [#756]: https://github.com/RustCrypto/signatures/pull/756 ## 0.16.8 (2023-07-20) ### Added - `hazmat::{sign_prehashed, verify_prehashed}` ([#731]) ### Changed - Refactor `Signature` constructors and improve docs ([#730]) [#730]: https://github.com/RustCrypto/signatures/pull/730 [#731]: https://github.com/RustCrypto/signatures/pull/731 ## 0.16.7 (2023-05-11) ### Added - RFC5480 citation for `der::Signature` ([#710]) - support for the `SignatureBitStringEncoding` trait ([#716]) ### Changed - bump `elliptic-curve` from 0.13.3 to 0.13.4 ([#709]) - `der::Signature` citation to RFC5912 ([#711]) - make `fmt` impls more consistent ([#713]) ### Fixed - `serde` doc fixup ([#712]) [#709]: https://github.com/RustCrypto/signatures/pull/709 [#710]: https://github.com/RustCrypto/signatures/pull/710 [#711]: https://github.com/RustCrypto/signatures/pull/711 [#712]: https://github.com/RustCrypto/signatures/pull/712 [#713]: https://github.com/RustCrypto/signatures/pull/713 [#716]: https://github.com/RustCrypto/signatures/pull/716 ## 0.16.6 (2023-04-09) ### Fixed - Test macro handling of serialized field size ([#707]) [#707]: https://github.com/RustCrypto/signatures/pull/707 ## 0.16.5 (2023-04-08) ### Fixed - Use `C::FieldBytesSize` instead of `C::Uint::BYTES` ([#705]) [#705]: https://github.com/RustCrypto/signatures/pull/705 ## 0.16.4 (2023-04-05) ### Fixed - `RecoveryId` computation in `SignPrimitive` ([#702]) [#702]: https://github.com/RustCrypto/signatures/pull/702 ## 0.16.3 (2023-04-04) ### Added - RFC5758 OID support ([#686]) - `SignatureAlgorithmIdentifier` impls for `SigningKey`/`VerifyingKey` ([#688]) - `SignatureWithOid` ([#689], [#690]) - `AssociatedAlgorithmIdentifier` impls for `SigningKey`/`VerifyingKey` ([#698]) ### Changed - Loosen `signature` bound to `2.0, <2.2` ([#697]) [#686]: https://github.com/RustCrypto/signatures/pull/686 [#688]: https://github.com/RustCrypto/signatures/pull/688 [#689]: https://github.com/RustCrypto/signatures/pull/689 [#690]: https://github.com/RustCrypto/signatures/pull/690 [#697]: https://github.com/RustCrypto/signatures/pull/697 [#698]: https://github.com/RustCrypto/signatures/pull/698 ## 0.16.2 (2023-03-28) ### Added - Handle the reduced R.x case in public key recovery ([#680]) - `Signature::{from_bytes, from_slice}` methods ([#684]) [#680]: https://github.com/RustCrypto/signatures/pull/680 [#684]: https://github.com/RustCrypto/signatures/pull/684 ## 0.16.1 (2023-03-09) ### Added - `VerifyingKey::to_sec1_bytes` + more conversions ([#675]) [#675]: https://github.com/RustCrypto/signatures/pull/675 ## 0.16.0 (2023-03-01) ### Added - `Decode` and `Encode` impls for `der::Signature` ([#666]) ### Changed - Use `Scalar::invert_vartime` for faster verification ([#651]) - Bump `serdect` dependency to 0.2 ([#657]) - Bump `elliptic-curve` dependency to v0.13; MSRV 1.65 ([#660], [#663]) - Bump `rfc6979` dependency to v0.4 ([#662]) [#651]: https://github.com/RustCrypto/signatures/pull/651 [#657]: https://github.com/RustCrypto/signatures/pull/657 [#660]: https://github.com/RustCrypto/signatures/pull/660 [#662]: https://github.com/RustCrypto/signatures/pull/662 [#666]: https://github.com/RustCrypto/signatures/pull/666 ## 0.15.1 (2023-01-23) ### Added - `SigningKey::*_recoverable` methods ([#635]) [#635]: https://github.com/RustCrypto/signatures/pull/635 ## 0.15.0 (2023-01-15) ### Added - `DigestPrimitive::Digest` now has bounds that work with RFC6979 ([#568]) - `*Signer`/`*Verifier` impls for `der::Signature` ([#569]) - `VerifyingKey` recovery support ([#576]) - Trial recovery support ([#580]) ### Changed - Signature now internally structured with `r` and `s` components ([#565]) - `SigningKey::verifying_key` now returns a reference ([#567]) - Refactor `prehash_to_field_bytes` to `bits2field` free function ([#574]) - Rename `sign` feature to `signing` ([#610]) - Rename `verify` feature to `verifying` features ([#610]) - Bump `signature` crate dependency to v2.0 ([#614]) [#565]: https://github.com/RustCrypto/signatures/pull/565 [#567]: https://github.com/RustCrypto/signatures/pull/567 [#574]: https://github.com/RustCrypto/signatures/pull/574 [#580]: https://github.com/RustCrypto/signatures/pull/580 [#568]: https://github.com/RustCrypto/signatures/pull/568 [#569]: https://github.com/RustCrypto/signatures/pull/569 [#576]: https://github.com/RustCrypto/signatures/pull/576 [#580]: https://github.com/RustCrypto/signatures/pull/580 [#610]: https://github.com/RustCrypto/signatures/pull/610 [#614]: https://github.com/RustCrypto/signatures/pull/614 ## 0.14.8 (2022-09-27) ### Added - Impl `From` for `SecretKey` ([#548]) ### Fixed - Prehash must receive zero-pads on left ([#547]) [#547]: https://github.com/RustCrypto/signatures/pull/547 [#548]: https://github.com/RustCrypto/signatures/pull/548 ## 0.14.7 (2022-09-15) ### Changed - Relax `Keypair` bounds ([#539]) [#539]: https://github.com/RustCrypto/signatures/pull/539 ## 0.14.6 (2022-09-12) ### Added - Impl `signature::hazmat::{PrehashSigner, PrehashVerifier}` ([#534]) - Impl `signature::Keypair` for `SigningKey` ([#535]) [#534]: https://github.com/RustCrypto/signatures/pull/534 [#535]: https://github.com/RustCrypto/signatures/pull/535 ## 0.14.5 (2022-09-06) ### Added - Impl `EncodePrivateKey` for `SigningKey` ([#523]) - `SigningKey::as_nonzero_scalar` ([#528]) - `VerifyingKey::as_affine` ([#528]) - `RecoveryId::from_byte` ([#531]) ### Changed - Make `RecoveryId` methods `const fn` ([#529]) [#523]: https://github.com/RustCrypto/signatures/pull/523 [#528]: https://github.com/RustCrypto/signatures/pull/528 [#529]: https://github.com/RustCrypto/signatures/pull/529 [#531]: https://github.com/RustCrypto/signatures/pull/531 ## 0.14.4 (2022-08-15) ### Added - Impl `EncodePublicKey` for `VerifyingKey` ([#505]) - ZeroizeOnDrop marker for SigningKey ([#509]) ### Changed - Restrict `signature` version to v1.5-v1.6 ([#508], [#512]) [#505]: https://github.com/RustCrypto/signatures/pull/505 [#508]: https://github.com/RustCrypto/signatures/pull/508 [#509]: https://github.com/RustCrypto/signatures/pull/509 [#512]: https://github.com/RustCrypto/signatures/pull/512 ## 0.14.3 (2022-06-26) [YANKED] ### Changed - Simplified digest trait bounds ([#499]) - Bump `rfc6979` dependency to v0.3 ([#500]) [#499]: https://github.com/RustCrypto/signatures/pull/499 [#500]: https://github.com/RustCrypto/signatures/pull/500 ## 0.14.2 (2022-06-17) [YANKED] ### Added - Security warning in README.md ([#486]) ### Changed - Use `serdect` for `Signature` types ([#497]) [#486]: https://github.com/RustCrypto/signatures/pull/486 [#497]: https://github.com/RustCrypto/signatures/pull/497 ## 0.14.1 (2022-05-09) [YANKED] ### Added - `SignPrimitive::try_sign_digest_rfc6979` ([#475]) - `VerifyPrimitive::verify_digest` ([#475]) [#475]: https://github.com/RustCrypto/signatures/pull/475 ## 0.14.0 (2022-05-09) [YANKED] ### Added - `VerifyingKey::from_affine` ([#452]) ### Changed - Bump `digest` dependency to v0.10 ([#433]) - `SignPrimitive` and `VerifyPrimitive` to accept `FieldBytes` rather than `Scalar` ([#460]) - Replace `hazmat::rfc6979_generate_k` with `SignPrimitive::try_sign_prehashed_rfc6979` ([#460]) - Bump `der` dependency to v0.6 ([#468]) - Bump `elliptic-curve` dependency to v0.12 ([#468]) - Bump `rfc6979` dependency to v0.2 ([#470]) [#433]: https://github.com/RustCrypto/signatures/pull/433 [#452]: https://github.com/RustCrypto/signatures/pull/452 [#460]: https://github.com/RustCrypto/signatures/pull/460 [#468]: https://github.com/RustCrypto/signatures/pull/468 [#470]: https://github.com/RustCrypto/signatures/pull/470 ## 0.13.4 (2022-01-06) ### Added - `Signature::to_vec` ([#428]) [#428]: https://github.com/RustCrypto/signatures/pull/428 ## 0.13.3 (2021-12-04) ### Changed - Use revised `LinearCombination` trait ([#419]) [#419]: https://github.com/RustCrypto/signatures/pull/419 ## 0.13.2 (2021-12-04) [YANKED] ### Changed - Use `LinearCombination` trait ([#417]) [#417]: https://github.com/RustCrypto/signatures/pull/417 ## 0.13.1 (2021-12-03) [YANKED] ### Added - `hazmat::rfc6979_generate_k` function ([#414]) [#414]: https://github.com/RustCrypto/signatures/pull/414 ## 0.13.0 (2021-11-21) [YANKED] ### Added - `RecoveryId` type ([#392]) - Default generic impl of `SignPrimitive::try_sign_prehashed` ([#396]) - Default generic impl of `VerifyPrimitive::verify_prehashed` ([#397]) - `serde` support ([#406]) ### Changed - Make `Signature::normalize_s` non-mutating ([#355]) - Switch from `ScalarBytes` to `ScalarCore` ([#356]) - Use `PrimeCurve` trait ([#357]) - Replace `FromDigest` trait with `Reduce` ([#372]) - 2021 edition upgrade; MSRV 1.56 ([#384]) - Allow `signature` v1.4 as a dependency ([#385]) - Bump `der` dependency to v0.5 ([#408]) - Bump `elliptic-curve` dependency to v0.11 ([#408]) - Split out `rfc6979` crate ([#409]) ### Removed - `NormalizeLow` trait ([#393]) - `RecoverableSignPrimitive` ([#394]) [#355]: https://github.com/RustCrypto/signatures/pull/355 [#356]: https://github.com/RustCrypto/signatures/pull/356 [#357]: https://github.com/RustCrypto/signatures/pull/357 [#372]: https://github.com/RustCrypto/signatures/pull/372 [#384]: https://github.com/RustCrypto/signatures/pull/384 [#385]: https://github.com/RustCrypto/signatures/pull/385 [#392]: https://github.com/RustCrypto/signatures/pull/392 [#393]: https://github.com/RustCrypto/signatures/pull/393 [#394]: https://github.com/RustCrypto/signatures/pull/394 [#396]: https://github.com/RustCrypto/signatures/pull/396 [#397]: https://github.com/RustCrypto/signatures/pull/397 [#406]: https://github.com/RustCrypto/signatures/pull/406 [#408]: https://github.com/RustCrypto/signatures/pull/408 [#409]: https://github.com/RustCrypto/signatures/pull/409 ## 0.12.4 (2021-08-12) ### Added - Impl `Clone`, `Debug`, `*Eq` for `SigningKey` ([#345]) [#345]: https://github.com/RustCrypto/signatures/pull/345 ## 0.12.3 (2021-06-17) ### Added - Impl `TryFrom<&[u8]>` for `Verifying` ([#329]) - Impl `TryFrom<&[u8]>` for `SigningKey` ([#330]) ### Changed - Use `signature::Result` alias ([#331]) [#329]: https://github.com/RustCrypto/signatures/pull/329 [#330]: https://github.com/RustCrypto/signatures/pull/330 [#331]: https://github.com/RustCrypto/signatures/pull/331 ## 0.12.2 (2021-06-18) ### Added - Zeroization on drop for `SigningKey` ([#321]) [#321]: https://github.com/RustCrypto/signatures/pull/321 ## 0.12.1 (2021-06-09) ### Added - Explicit `Copy` bounds on `VerifyingKey` ([#318]) [#318]: https://github.com/RustCrypto/signatures/pull/318 ## 0.12.0 (2021-06-07) ### Changed - Bump `der` crate to v0.4 ([#302], [#315]) - Bump `elliptic-curve` crate dependency to v0.10 ([#315]) - MSRV 1.51+ ([#302], [#315]) ### Removed - Bounds now expressed via `*Arithmetic` traits ([#303], [#312]) [#302]: https://github.com/RustCrypto/signatures/pull/302 [#303]: https://github.com/RustCrypto/signatures/pull/303 [#315]: https://github.com/RustCrypto/signatures/pull/315 ## 0.11.1 (2021-05-24) ### Added - `Ord` and `PartialOrd` impls on VerifyingKey ([#298], [#299]) ### Changed - Bump `elliptic-curve` dependency to v0.9.12 ([#299]) [#298]: https://github.com/RustCrypto/signatures/pull/298 [#299]: https://github.com/RustCrypto/signatures/pull/299 ## 0.11.0 (2021-04-29) ### Added - `FromDigest` trait ([#238], [#244]) - Wycheproof test vector support ([#260]) ### Changed - Use `der` crate for decoding/encoding signatures ([#226], [#267]) - Support `HmacDrbg` with variable output size ([#243]) - Bump `base64ct` and `pkcs8`; MSRV 1.47+ ([#262]) - Flatten and simplify public API ([#268]) - Use `verifying_key` name consistently ([#273]) - Bound curve implementations on Order trait ([#280]) - Bump `elliptic-curve` to v0.9.10+; use `ScalarBytes` ([#284]) - Bump `hmac` crate dependency to v0.11 ([#287]) ### Removed - `FieldBytes` bounds ([#227]) - `CheckSignatureBytes` trait ([#281]) [#226]: https://github.com/RustCrypto/signatures/pull/226 [#227]: https://github.com/RustCrypto/signatures/pull/227 [#238]: https://github.com/RustCrypto/signatures/pull/238 [#243]: https://github.com/RustCrypto/signatures/pull/243 [#244]: https://github.com/RustCrypto/signatures/pull/244 [#260]: https://github.com/RustCrypto/signatures/pull/260 [#262]: https://github.com/RustCrypto/signatures/pull/262 [#267]: https://github.com/RustCrypto/signatures/pull/267 [#268]: https://github.com/RustCrypto/signatures/pull/268 [#273]: https://github.com/RustCrypto/signatures/pull/273 [#280]: https://github.com/RustCrypto/signatures/pull/280 [#281]: https://github.com/RustCrypto/signatures/pull/281 [#284]: https://github.com/RustCrypto/signatures/pull/284 [#287]: https://github.com/RustCrypto/signatures/pull/287 ## 0.10.2 (2020-12-22) ### Changed - Bump `elliptic-curve` crate to v0.8.3 ([#218]) - Use the `dev` module from the `elliptic-curve` crate ([#218]) [#218]: https://github.com/RustCrypto/signatures/pull/218 ## 0.10.1 (2020-12-16) [YANKED] ### Fixed - Trigger docs.rs rebuild with nightly bugfix ([RustCrypto/traits#412]) [RustCrypto/traits#412]: https://github.com/RustCrypto/traits/pull/412 ## 0.10.0 (2020-12-16) [YANKED] ### Changed - Bump `elliptic-curve` dependency to v0.8 ([#215]) [#215]: https://github.com/RustCrypto/signatures/pull/215 ## 0.9.0 (2020-12-06) ### Added - PKCS#8 support ([#203]) ### Changed - Bump `elliptic-curve` crate dependency to v0.7; MSRV 1.46+ ([#204]) - Rename `VerifyKey` to `VerifyingKey` ([#200]) - Rename `VerifyingKey::new()` to `::from_sec1_bytes()` ([#198]) - Rename `SigningKey::new()` to `::from_bytes()` ([#205]) ### Fixed - Additional validity checks on ASN.1 DER-encoded signatures ([#192]) [#205]: https://github.com/RustCrypto/signatures/pull/205 [#204]: https://github.com/RustCrypto/signatures/pull/204 [#203]: https://github.com/RustCrypto/signatures/pull/203 [#200]: https://github.com/RustCrypto/signatures/pull/200 [#198]: https://github.com/RustCrypto/signatures/pull/198 [#192]: https://github.com/RustCrypto/signatures/pull/192 ## 0.8.5 (2020-10-09) ### Fixed - Bug in default impl of CheckSignatureBytes ([#184]) [#184]: https://github.com/RustCrypto/signatures/pull/184 ## 0.8.4 (2020-10-08) ### Fixed - Work around `nightly-2020-10-06` breakage ([#180]) [#180]: https://github.com/RustCrypto/signatures/pull/180 ## 0.8.3 (2020-09-28) ### Fixed - 32-bit builds for the `dev` feature ([#177]) [#177]: https://github.com/RustCrypto/signatures/pull/177 ## 0.8.2 (2020-09-27) ### Added - `RecoverableSignPrimitive` ([#174], [#175]) [#174]: https://github.com/RustCrypto/signatures/pull/174 [#175]: https://github.com/RustCrypto/signatures/pull/175 ## 0.8.1 (2020-09-23) ### Added - Conditional `Copy` impl on `VerifyKey` ([#171]) [#171]: https://github.com/RustCrypto/signatures/pull/171 ## 0.8.0 (2020-09-11) ### Added - `CheckSignatureBytes` trait ([#151]) - Add `Signature::r`/`::s` methods which return `NonZeroScalar`values ([#151]) - `alloc` feature ([#150]) - Impl `From<&VerifyKey>` for `EncodedPoint` ([#144]) - Serialization methods for `SigningKey`/`VerifyKey` ([#143]) - RFC6979-based deterministic signatures ([#133], [#134], [#136]) ### Changed - Bump `elliptic-curve` crate dependency to v0.6 ([#165]) - Use `ProjectiveArithmetic` trait ([#164]) - Rename `ElementBytes` to `FieldBytes` ([#160]) - Use `ff` and `group` crates to v0.8 ([#156]) - MSRV 1.44+ ([#156]) - Remove `rand` feature; make `rand_core` a hard dependency ([#154]) - Use `impl Into` bounds on `Signature::from_scalars` ([#149]) - Derive `Clone`, `Debug`, `Eq`, and `Ord` on `VerifyKey` ([#148]) - Renamed `{Signer, Verifier}` => `{SigningKey, VerifyKey}` ([#140]) - Use newly refactored `sec1::EncodedPoint` ([#131]) ### Removed - `Generate` trait ([#159]) - `RecoverableSignPrimitive` ([#146]) [#165]: https://github.com/RustCrypto/signatures/pull/165 [#164]: https://github.com/RustCrypto/signatures/pull/164 [#160]: https://github.com/RustCrypto/signatures/pull/160 [#159]: https://github.com/RustCrypto/signatures/pull/159 [#156]: https://github.com/RustCrypto/signatures/pull/156 [#154]: https://github.com/RustCrypto/signatures/pull/154 [#151]: https://github.com/RustCrypto/signatures/pull/151 [#150]: https://github.com/RustCrypto/signatures/pull/150 [#149]: https://github.com/RustCrypto/signatures/pull/149 [#148]: https://github.com/RustCrypto/signatures/pull/148 [#146]: https://github.com/RustCrypto/signatures/pull/146 [#144]: https://github.com/RustCrypto/signatures/pull/144 [#143]: https://github.com/RustCrypto/signatures/pull/143 [#140]: https://github.com/RustCrypto/signatures/pull/140 [#136]: https://github.com/RustCrypto/signatures/pull/136 [#134]: https://github.com/RustCrypto/signatures/pull/134 [#133]: https://github.com/RustCrypto/signatures/pull/133 [#131]: https://github.com/RustCrypto/signatures/pull/131 ## 0.7.2 (2020-08-11) ### Added - Conditional `PrehashSignature` impl for `asn1::Signature` ([#128]) [#128]: https://github.com/RustCrypto/signatures/pull/128 ## 0.7.1 (2020-08-10) ### Changed - Use `all-features = true` on docs.rs ([#126]) [#126]: https://github.com/RustCrypto/signatures/pull/126 ## 0.7.0 (2020-08-10) ### Added - `hazmat` traits: `SignPrimitive`, `RecoverableSignPrimitive`, `VerifyPrimitive`, `DigestPrimitive` ([#96], [#99], [#107], [#111]) - `dev` module ([#103]) - `NormalizeLow` trait ([#115], [#118], [#119]) - `Copy` impl on `Signature` ([#117]) - `RecoverableSignPrimitive` ([#120]) ### Changed - Bumped `elliptic-curve` crate to v0.5 release ([#123]) - Renamed `FixedSignature` to `ecdsa::Signature` ([#98]) - Renamed `Asn1Signature` to `ecdsa::asn1::Signature` ([#98], [#102]) ### Removed - Curve-specific types - migrated to `k256`, `p256`, `p384` crates ([#96]) [#96]: https://github.com/RustCrypto/signatures/pull/96 [#98]: https://github.com/RustCrypto/signatures/pull/98 [#99]: https://github.com/RustCrypto/signatures/pull/99 [#102]: https://github.com/RustCrypto/signatures/pull/102 [#103]: https://github.com/RustCrypto/signatures/pull/103 [#107]: https://github.com/RustCrypto/signatures/pull/107 [#111]: https://github.com/RustCrypto/signatures/pull/111 [#115]: https://github.com/RustCrypto/signatures/pull/115 [#117]: https://github.com/RustCrypto/signatures/pull/117 [#118]: https://github.com/RustCrypto/signatures/pull/118 [#119]: https://github.com/RustCrypto/signatures/pull/119 [#120]: https://github.com/RustCrypto/signatures/pull/120 [#123]: https://github.com/RustCrypto/signatures/pull/123 ## 0.6.1 (2020-06-29) ### Added - `doc_cfg` attributes for https://docs.rs ([#91]) - `ecdsa::curve::secp256k1::RecoverableSignature` ([#90]) [#91]: https://github.com/RustCrypto/signatures/pull/91 [#90]: https://github.com/RustCrypto/signatures/pull/90 ## 0.6.0 (2020-06-09) ### Changed - Upgrade to `signature` ~1.1.0; `sha` v0.9 ([#87]) - Bump all elliptic curve crates; MSRV 1.41+ ([#86]) [#87]: https://github.com/RustCrypto/signatures/pull/87 [#86]: https://github.com/RustCrypto/signatures/pull/86 ## 0.5.0 (2020-04-18) ### Changed - Upgrade `signature` crate to v1.0 final release ([#80]) [#80]: https://github.com/RustCrypto/signatures/pull/80 ## 0.4.0 (2020-01-07) ### Changed - Upgrade `elliptic-curve` crate to v0.3.0; make curves cargo features ([#68]) [#68]: https://github.com/RustCrypto/signatures/pull/68 ## 0.3.0 (2019-12-11) ### Changed - Upgrade `elliptic-curve` crate to v0.2.0; MSRV 1.37+ ([#65]) [#65]: https://github.com/RustCrypto/signatures/pull/65 ## 0.2.1 (2019-12-06) ### Added - Re-export `PublicKey` and `SecretKey` from the `elliptic-curve` crate ([#61]) [#61]: https://github.com/RustCrypto/signatures/pull/61 ## 0.2.0 (2019-12-06) ### Changed - Use curve types from the `elliptic-curve` crate ([#58]) [#58]: https://github.com/RustCrypto/signatures/pull/58 ## 0.1.0 (2019-10-29) - Initial release ecdsa-0.16.9/Cargo.toml0000644000000053350000000000100102140ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.65" name = "ecdsa" version = "0.16.9" authors = ["RustCrypto Developers"] description = """ Pure Rust implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA) as specified in FIPS 186-4 (Digital Signature Standard), providing RFC6979 deterministic signatures as well as support for added entropy """ readme = "README.md" keywords = [ "crypto", "ecc", "nist", "secp256k1", "signature", ] categories = [ "cryptography", "no-std", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/RustCrypto/signatures/tree/master/ecdsa" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.der] version = "0.7" optional = true [dependencies.digest] version = "0.10.7" features = ["oid"] optional = true default-features = false [dependencies.elliptic-curve] version = "0.13.6" features = [ "digest", "sec1", ] default-features = false [dependencies.rfc6979] version = "0.4" optional = true [dependencies.serdect] version = "0.2" features = ["alloc"] optional = true default-features = false [dependencies.sha2] version = "0.10" features = ["oid"] optional = true default-features = false [dependencies.signature] version = "2.0, <2.3" features = ["rand_core"] default-features = false [dependencies.spki] version = "0.7.2" optional = true default-features = false [dev-dependencies.elliptic-curve] version = "0.13" features = ["dev"] default-features = false [dev-dependencies.hex-literal] version = "0.4" [dev-dependencies.sha2] version = "0.10" default-features = false [features] alloc = [ "elliptic-curve/alloc", "signature/alloc", "spki/alloc", ] arithmetic = ["elliptic-curve/arithmetic"] default = ["digest"] dev = [ "arithmetic", "digest", "elliptic-curve/dev", "hazmat", ] digest = [ "dep:digest", "signature/digest", ] hazmat = [] pem = [ "elliptic-curve/pem", "pkcs8", ] pkcs8 = [ "digest", "elliptic-curve/pkcs8", "der", ] serde = [ "elliptic-curve/serde", "serdect", ] signing = [ "arithmetic", "digest", "hazmat", "rfc6979", ] std = [ "alloc", "elliptic-curve/std", "signature/std", ] verifying = [ "arithmetic", "digest", "hazmat", ] ecdsa-0.16.9/Cargo.toml.orig000064400000000000000000000040561046102023000136740ustar 00000000000000[package] name = "ecdsa" version = "0.16.9" description = """ Pure Rust implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA) as specified in FIPS 186-4 (Digital Signature Standard), providing RFC6979 deterministic signatures as well as support for added entropy """ authors = ["RustCrypto Developers"] license = "Apache-2.0 OR MIT" repository = "https://github.com/RustCrypto/signatures/tree/master/ecdsa" readme = "README.md" categories = ["cryptography", "no-std"] keywords = ["crypto", "ecc", "nist", "secp256k1", "signature"] edition = "2021" rust-version = "1.65" [dependencies] elliptic-curve = { version = "0.13.6", default-features = false, features = ["digest", "sec1"] } signature = { version = "2.0, <2.3", default-features = false, features = ["rand_core"] } # optional dependencies der = { version = "0.7", optional = true } digest = { version = "0.10.7", optional = true, default-features = false, features = ["oid"] } rfc6979 = { version = "0.4", optional = true, path = "../rfc6979" } serdect = { version = "0.2", optional = true, default-features = false, features = ["alloc"] } sha2 = { version = "0.10", optional = true, default-features = false, features = ["oid"] } spki = { version = "0.7.2", optional = true, default-features = false } [dev-dependencies] elliptic-curve = { version = "0.13", default-features = false, features = ["dev"] } hex-literal = "0.4" sha2 = { version = "0.10", default-features = false } [features] default = ["digest"] alloc = ["elliptic-curve/alloc", "signature/alloc", "spki/alloc"] std = ["alloc", "elliptic-curve/std", "signature/std"] arithmetic = ["elliptic-curve/arithmetic"] dev = ["arithmetic", "digest", "elliptic-curve/dev", "hazmat"] digest = ["dep:digest", "signature/digest"] hazmat = [] pkcs8 = ["digest", "elliptic-curve/pkcs8", "der"] pem = ["elliptic-curve/pem", "pkcs8"] serde = ["elliptic-curve/serde", "serdect"] signing = ["arithmetic", "digest", "hazmat", "rfc6979"] verifying = ["arithmetic", "digest", "hazmat"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] ecdsa-0.16.9/LICENSE-APACHE000064400000000000000000000251361046102023000127330ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018-2022 RustCrypto Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ecdsa-0.16.9/LICENSE-MIT000064400000000000000000000020561046102023000124370ustar 00000000000000Copyright (c) 2018-2022 RustCrypto Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ecdsa-0.16.9/README.md000064400000000000000000000063361046102023000122670ustar 00000000000000# [RustCrypto]: ECDSA [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] [![Build Status][build-image]][build-link] ![Apache2/MIT licensed][license-image] ![MSRV][rustc-image] [![Project Chat][chat-image]][chat-link] [Elliptic Curve Digital Signature Algorithm (ECDSA)][1] as specified in [FIPS 186-4][2] (Digital Signature Standard). [Documentation][docs-link] ## About This crate provides generic ECDSA support which can be used in the following ways: - Generic implementation of ECDSA usable with the following crates: - [`k256`] (secp256k1) - [`p256`] (NIST P-256) - [`p384`] (NIST P-384) - Other crates which provide their own complete implementations of ECDSA can also leverage the types from this crate to export ECDSA functionality in a generic, interoperable way by leveraging [`ecdsa::Signature`] with the [`signature::Signer`] and [`signature::Verifier`] traits. ## ⚠️ Security Warning The ECDSA implementation contained in this crate has never been independently audited for security! This crate contains a generic implementation of ECDSA which must be instantiated using a separate crate providing a concrete implementation of arithmetic for a particular curve. It's possible timing variability can exist in concrete curve implementations, and thus this crate's security can only be properly assessed for a specific elliptic curve. USE AT YOUR OWN RISK! ## Minimum Supported Rust Version This crate requires **Rust 1.65** at a minimum. We may change the MSRV in the future, but it will be accompanied by a minor version bump. ## License All crates licensed under either of - [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - [MIT license](http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [//]: # (badges) [crate-image]: https://buildstats.info/crate/ecdsa [crate-link]: https://crates.io/crates/ecdsa [docs-image]: https://docs.rs/ecdsa/badge.svg [docs-link]: https://docs.rs/ecdsa/ [build-image]: https://github.com/RustCrypto/signatures/actions/workflows/ecdsa.yml/badge.svg [build-link]: https://github.com/RustCrypto/signatures/actions/workflows/ecdsa.yml [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260048-signatures [//]: # (links) [RustCrypto]: https://github.com/RustCrypto [//]: # (footnotes) [1]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm [2]: https://csrc.nist.gov/publications/detail/fips/186/4/final [//]: # (docs.rs definitions) [`ecdsa::Signature`]: https://docs.rs/ecdsa/latest/ecdsa/struct.Signature.html [`k256`]: https://docs.rs/k256 [`p256`]: https://docs.rs/p256 [`p384`]: https://docs.rs/p384 [`signature::Signer`]: https://docs.rs/signature/latest/signature/trait.Signer.html [`signature::Verifier`]: https://docs.rs/signature/latest/signature/trait.Verifier.html ecdsa-0.16.9/src/der.rs000064400000000000000000000327161046102023000127200ustar 00000000000000//! Support for ASN.1 DER-encoded ECDSA signatures as specified in //! [RFC5912 Appendix A]. //! //! [RFC5912 Appendix A]: https://www.rfc-editor.org/rfc/rfc5912#appendix-A use crate::{Error, Result}; use core::{ fmt::{self, Debug}, ops::{Add, Range}, }; use der::{asn1::UintRef, Decode, Encode, FixedTag, Length, Reader, Tag, Writer}; use elliptic_curve::{ consts::U9, generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, FieldBytesSize, PrimeCurve, }; #[cfg(feature = "alloc")] use { alloc::{boxed::Box, vec::Vec}, signature::SignatureEncoding, spki::{der::asn1::BitString, SignatureBitStringEncoding}, }; #[cfg(feature = "serde")] use serdect::serde::{de, ser, Deserialize, Serialize}; /// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve: /// 9-bytes. /// /// Includes 3-byte ASN.1 DER header: /// /// - 1-byte: ASN.1 `SEQUENCE` tag (0x30) /// - 2-byte: length /// /// ...followed by two ASN.1 `INTEGER` values, which each have a header whose /// maximum length is the following: /// /// - 1-byte: ASN.1 `INTEGER` tag (0x02) /// - 1-byte: length /// - 1-byte: zero to indicate value is positive (`INTEGER` is signed) pub type MaxOverhead = U9; /// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve. pub type MaxSize = < as Add>::Output as Add>::Output; /// Byte array containing a serialized ASN.1 signature type SignatureBytes = GenericArray>; /// ASN.1 DER-encoded signature as specified in [RFC5912 Appendix A]: /// /// ```text /// ECDSA-Sig-Value ::= SEQUENCE { /// r INTEGER, /// s INTEGER /// } /// ``` /// /// [RFC5912 Appendix A]: https://www.rfc-editor.org/rfc/rfc5912#appendix-A pub struct Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { /// ASN.1 DER-encoded signature data bytes: SignatureBytes, /// Range of the `r` value within the signature r_range: Range, /// Range of the `s` value within the signature s_range: Range, } #[allow(clippy::len_without_is_empty)] impl Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { /// Parse signature from DER-encoded bytes. pub fn from_bytes(input: &[u8]) -> Result { let (r, s) = decode_der(input).map_err(|_| Error::new())?; if r.as_bytes().len() > C::FieldBytesSize::USIZE || s.as_bytes().len() > C::FieldBytesSize::USIZE { return Err(Error::new()); } let r_range = find_scalar_range(input, r.as_bytes())?; let s_range = find_scalar_range(input, s.as_bytes())?; if s_range.end != input.len() { return Err(Error::new()); } let mut bytes = SignatureBytes::::default(); bytes[..s_range.end].copy_from_slice(input); Ok(Signature { bytes, r_range, s_range, }) } /// Create an ASN.1 DER encoded signature from big endian `r` and `s` scalar /// components. pub(crate) fn from_components(r: &[u8], s: &[u8]) -> der::Result { let r = UintRef::new(r)?; let s = UintRef::new(s)?; let mut bytes = SignatureBytes::::default(); let mut writer = der::SliceWriter::new(&mut bytes); writer.sequence((r.encoded_len()? + s.encoded_len()?)?, |seq| { seq.encode(&r)?; seq.encode(&s) })?; writer .finish()? .try_into() .map_err(|_| der::Tag::Sequence.value_error()) } /// Borrow this signature as a byte slice pub fn as_bytes(&self) -> &[u8] { &self.bytes.as_slice()[..self.len()] } /// Serialize this signature as a boxed byte slice #[cfg(feature = "alloc")] pub fn to_bytes(&self) -> Box<[u8]> { self.as_bytes().to_vec().into_boxed_slice() } /// Get the length of the signature in bytes pub fn len(&self) -> usize { self.s_range.end } /// Get the `r` component of the signature (leading zeros removed) pub(crate) fn r(&self) -> &[u8] { &self.bytes[self.r_range.clone()] } /// Get the `s` component of the signature (leading zeros removed) pub(crate) fn s(&self) -> &[u8] { &self.bytes[self.s_range.clone()] } } impl AsRef<[u8]> for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl Clone for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn clone(&self) -> Self { Self { bytes: self.bytes.clone(), r_range: self.r_range.clone(), s_range: self.s_range.clone(), } } } impl Debug for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ecdsa::der::Signature<{:?}>(", C::default())?; for &byte in self.as_ref() { write!(f, "{:02X}", byte)?; } write!(f, ")") } } impl<'a, C> Decode<'a> for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn decode>(reader: &mut R) -> der::Result { let header = reader.peek_header()?; header.tag.assert_eq(Tag::Sequence)?; let mut buf = SignatureBytes::::default(); let len = (header.encoded_len()? + header.length)?; let slice = buf .get_mut(..usize::try_from(len)?) .ok_or_else(|| reader.error(Tag::Sequence.length_error().kind()))?; reader.read_into(slice)?; Self::from_bytes(slice).map_err(|_| Tag::Integer.value_error()) } } impl Encode for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn encoded_len(&self) -> der::Result { Length::try_from(self.len()) } fn encode(&self, writer: &mut impl Writer) -> der::Result<()> { writer.write(self.as_bytes()) } } impl FixedTag for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { const TAG: Tag = Tag::Sequence; } impl From> for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn from(sig: crate::Signature) -> Signature { sig.to_der() } } impl TryFrom<&[u8]> for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { type Error = Error; fn try_from(input: &[u8]) -> Result { Self::from_bytes(input) } } impl TryFrom> for crate::Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { type Error = Error; fn try_from(sig: Signature) -> Result> { let mut bytes = super::SignatureBytes::::default(); let r_begin = C::FieldBytesSize::USIZE.saturating_sub(sig.r().len()); let s_begin = bytes.len().saturating_sub(sig.s().len()); bytes[r_begin..C::FieldBytesSize::USIZE].copy_from_slice(sig.r()); bytes[s_begin..].copy_from_slice(sig.s()); Self::try_from(bytes.as_slice()) } } #[cfg(feature = "alloc")] impl From> for Box<[u8]> where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn from(signature: Signature) -> Box<[u8]> { signature.to_vec().into_boxed_slice() } } #[cfg(feature = "alloc")] impl SignatureEncoding for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { type Repr = Box<[u8]>; fn to_vec(&self) -> Vec { self.as_bytes().into() } } #[cfg(feature = "alloc")] impl SignatureBitStringEncoding for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn to_bitstring(&self) -> der::Result { BitString::new(0, self.to_vec()) } } #[cfg(feature = "serde")] impl Serialize for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer) } } #[cfg(feature = "serde")] impl<'de, C> Deserialize<'de> for Signature where C: PrimeCurve, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { let mut buf = SignatureBytes::::default(); let slice = serdect::slice::deserialize_hex_or_bin(&mut buf, deserializer)?; Self::try_from(slice).map_err(de::Error::custom) } } /// Decode the `r` and `s` components of a DER-encoded ECDSA signature. fn decode_der(der_bytes: &[u8]) -> der::Result<(UintRef<'_>, UintRef<'_>)> { let mut reader = der::SliceReader::new(der_bytes)?; let header = der::Header::decode(&mut reader)?; header.tag.assert_eq(der::Tag::Sequence)?; let ret = reader.read_nested(header.length, |reader| { let r = UintRef::decode(reader)?; let s = UintRef::decode(reader)?; Ok((r, s)) })?; reader.finish(ret) } /// Locate the range within a slice at which a particular subslice is located fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result> { let outer_start = outer.as_ptr() as usize; let inner_start = inner.as_ptr() as usize; let start = inner_start .checked_sub(outer_start) .ok_or_else(Error::new)?; let end = start.checked_add(inner.len()).ok_or_else(Error::new)?; Ok(Range { start, end }) } #[cfg(all(feature = "digest", feature = "hazmat"))] impl signature::PrehashSignature for Signature where C: PrimeCurve + crate::hazmat::DigestPrimitive, MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { type Digest = C::Digest; } #[cfg(all(test, feature = "arithmetic"))] mod tests { use elliptic_curve::dev::MockCurve; type Signature = crate::Signature; const EXAMPLE_SIGNATURE: [u8; 64] = [ 0xf3, 0xac, 0x80, 0x61, 0xb5, 0x14, 0x79, 0x5b, 0x88, 0x43, 0xe3, 0xd6, 0x62, 0x95, 0x27, 0xed, 0x2a, 0xfd, 0x6b, 0x1f, 0x6a, 0x55, 0x5a, 0x7a, 0xca, 0xbb, 0x5e, 0x6f, 0x79, 0xc8, 0xc2, 0xac, 0x8b, 0xf7, 0x78, 0x19, 0xca, 0x5, 0xa6, 0xb2, 0x78, 0x6c, 0x76, 0x26, 0x2b, 0xf7, 0x37, 0x1c, 0xef, 0x97, 0xb2, 0x18, 0xe9, 0x6f, 0x17, 0x5a, 0x3c, 0xcd, 0xda, 0x2a, 0xcc, 0x5, 0x89, 0x3, ]; #[test] fn test_fixed_to_asn1_signature_roundtrip() { let signature1 = Signature::try_from(EXAMPLE_SIGNATURE.as_ref()).unwrap(); // Convert to ASN.1 DER and back let asn1_signature = signature1.to_der(); let signature2 = Signature::from_der(asn1_signature.as_ref()).unwrap(); assert_eq!(signature1, signature2); } #[test] fn test_asn1_too_short_signature() { assert!(Signature::from_der(&[]).is_err()); assert!(Signature::from_der(&[der::Tag::Sequence.into()]).is_err()); assert!(Signature::from_der(&[der::Tag::Sequence.into(), 0x00]).is_err()); assert!(Signature::from_der(&[ der::Tag::Sequence.into(), 0x03, der::Tag::Integer.into(), 0x01, 0x01 ]) .is_err()); } #[test] fn test_asn1_non_der_signature() { // A minimal 8-byte ASN.1 signature parses OK. assert!(Signature::from_der(&[ der::Tag::Sequence.into(), 0x06, // length of below der::Tag::Integer.into(), 0x01, // length of value 0x01, // value=1 der::Tag::Integer.into(), 0x01, // length of value 0x01, // value=1 ]) .is_ok()); // But length fields that are not minimally encoded should be rejected, as they are not // valid DER, cf. // https://github.com/google/wycheproof/blob/2196000605e4/testvectors/ecdsa_secp256k1_sha256_test.json#L57-L66 assert!(Signature::from_der(&[ der::Tag::Sequence.into(), 0x81, // extended length: 1 length byte to come 0x06, // length of below der::Tag::Integer.into(), 0x01, // length of value 0x01, // value=1 der::Tag::Integer.into(), 0x01, // length of value 0x01, // value=1 ]) .is_err()); } } ecdsa-0.16.9/src/dev.rs000064400000000000000000000201271046102023000127150ustar 00000000000000//! Development-related functionality. // TODO(tarcieri): implement full set of tests from ECDSA2VS // /// ECDSA test vector pub struct TestVector { /// Private scalar pub d: &'static [u8], /// Public key x-coordinate (`Qx`) pub q_x: &'static [u8], /// Public key y-coordinate (`Qy`) pub q_y: &'static [u8], /// Ephemeral scalar (a.k.a. nonce) pub k: &'static [u8], /// Message digest (prehashed) pub m: &'static [u8], /// Signature `r` component pub r: &'static [u8], /// Signature `s` component pub s: &'static [u8], } /// Define ECDSA signing test. #[macro_export] macro_rules! new_signing_test { ($curve:path, $vectors:expr) => { use $crate::{ elliptic_curve::{ bigint::Encoding, generic_array::{typenum::Unsigned, GenericArray}, group::ff::PrimeField, Curve, CurveArithmetic, Scalar, }, hazmat::SignPrimitive, }; fn decode_scalar(bytes: &[u8]) -> Option> { if bytes.len() == <$curve as Curve>::FieldBytesSize::USIZE { Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(bytes)).into() } else { None } } #[test] fn ecdsa_signing() { for vector in $vectors { let d = decode_scalar(vector.d).expect("invalid vector.d"); let k = decode_scalar(vector.k).expect("invalid vector.m"); assert_eq!( <$curve as Curve>::FieldBytesSize::USIZE, vector.m.len(), "invalid vector.m (must be field-sized digest)" ); let z = GenericArray::clone_from_slice(vector.m); let sig = d.try_sign_prehashed(k, &z).expect("ECDSA sign failed").0; assert_eq!(vector.r, sig.r().to_bytes().as_slice()); assert_eq!(vector.s, sig.s().to_bytes().as_slice()); } } }; } /// Define ECDSA verification test. #[macro_export] macro_rules! new_verification_test { ($curve:path, $vectors:expr) => { use $crate::{ elliptic_curve::{ generic_array::GenericArray, group::ff::PrimeField, sec1::{EncodedPoint, FromEncodedPoint}, AffinePoint, CurveArithmetic, Scalar, }, hazmat::VerifyPrimitive, Signature, }; #[test] fn ecdsa_verify_success() { for vector in $vectors { let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( GenericArray::from_slice(vector.q_x), GenericArray::from_slice(vector.q_y), false, ); let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); let z = GenericArray::clone_from_slice(vector.m); let sig = Signature::from_scalars( GenericArray::clone_from_slice(vector.r), GenericArray::clone_from_slice(vector.s), ) .unwrap(); let result = q.verify_prehashed(&z, &sig); assert!(result.is_ok()); } } #[test] fn ecdsa_verify_invalid_s() { for vector in $vectors { let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( GenericArray::from_slice(vector.q_x), GenericArray::from_slice(vector.q_y), false, ); let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); let z = GenericArray::clone_from_slice(vector.m); // Flip a bit in `s` let mut s_tweaked = GenericArray::clone_from_slice(vector.s); s_tweaked[0] ^= 1; let sig = Signature::from_scalars(GenericArray::clone_from_slice(vector.r), s_tweaked) .unwrap(); let result = q.verify_prehashed(&z, &sig); assert!(result.is_err()); } } // TODO(tarcieri): test invalid Q, invalid r, invalid m }; } /// Define a Wycheproof verification test. #[macro_export] macro_rules! new_wycheproof_test { ($name:ident, $test_name: expr, $curve:path) => { use $crate::{ elliptic_curve::{bigint::Integer, sec1::EncodedPoint}, signature::Verifier, Signature, }; #[test] fn $name() { use blobby::Blob5Iterator; use elliptic_curve::{bigint::Encoding as _, generic_array::typenum::Unsigned}; // Build a field element but allow for too-short input (left pad with zeros) // or too-long input (check excess leftmost bytes are zeros). fn element_from_padded_slice( data: &[u8], ) -> elliptic_curve::FieldBytes { let point_len = C::FieldBytesSize::USIZE; if data.len() >= point_len { let offset = data.len() - point_len; for v in data.iter().take(offset) { assert_eq!(*v, 0, "EcdsaVerifier: point too large"); } elliptic_curve::FieldBytes::::clone_from_slice(&data[offset..]) } else { // Provided slice is too short and needs to be padded with zeros // on the left. Build a combined exact iterator to do this. let iter = core::iter::repeat(0) .take(point_len - data.len()) .chain(data.iter().cloned()); elliptic_curve::FieldBytes::::from_exact_iter(iter).unwrap() } } fn run_test( wx: &[u8], wy: &[u8], msg: &[u8], sig: &[u8], pass: bool, ) -> Option<&'static str> { let x = element_from_padded_slice::<$curve>(wx); let y = element_from_padded_slice::<$curve>(wy); let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( &x, &y, /* compress= */ false, ); let verifying_key = $crate::VerifyingKey::<$curve>::from_encoded_point(&q_encoded).unwrap(); let sig = match Signature::from_der(sig) { Ok(s) => s, Err(_) if !pass => return None, Err(_) => return Some("failed to parse signature ASN.1"), }; match verifying_key.verify(msg, &sig) { Ok(_) if pass => None, Ok(_) => Some("signature verify unexpectedly succeeded"), Err(_) if !pass => None, Err(_) => Some("signature verify failed"), } } let data = include_bytes!(concat!("test_vectors/data/", $test_name, ".blb")); for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() { let [wx, wy, msg, sig, status] = row.unwrap(); let pass = match status[0] { 0 => false, 1 => true, _ => panic!("invalid value for pass flag"), }; if let Some(desc) = run_test(wx, wy, msg, sig, pass) { panic!( "\n\ Failed test №{}: {}\n\ wx:\t{:?}\n\ wy:\t{:?}\n\ msg:\t{:?}\n\ sig:\t{:?}\n\ pass:\t{}\n", i, desc, wx, wy, msg, sig, pass, ); } } } }; } ecdsa-0.16.9/src/hazmat.rs000064400000000000000000000260211046102023000134220ustar 00000000000000//! Low-level ECDSA primitives. //! //! # ⚠️ Warning: Hazmat! //! //! YOU PROBABLY DON'T WANT TO USE THESE! //! //! These primitives are easy-to-misuse low-level interfaces. //! //! If you are an end user / non-expert in cryptography, do not use these! //! Failure to use them correctly can lead to catastrophic failures including //! FULL PRIVATE KEY RECOVERY! use crate::{Error, Result}; use core::cmp; use elliptic_curve::{generic_array::typenum::Unsigned, FieldBytes, PrimeCurve}; #[cfg(feature = "arithmetic")] use { crate::{RecoveryId, SignatureSize}, elliptic_curve::{ ff::{Field, PrimeField}, group::{Curve as _, Group}, ops::{Invert, LinearCombination, MulByGenerator, Reduce}, point::AffineCoordinates, scalar::IsHigh, subtle::CtOption, CurveArithmetic, ProjectivePoint, Scalar, }, }; #[cfg(feature = "digest")] use { elliptic_curve::FieldBytesSize, signature::{ digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset}, PrehashSignature, }, }; #[cfg(feature = "rfc6979")] use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive}; #[cfg(any(feature = "arithmetic", feature = "digest"))] use crate::{elliptic_curve::generic_array::ArrayLength, Signature}; /// Try to sign the given prehashed message using ECDSA. /// /// This trait is intended to be implemented on a type with access to the /// secret scalar via `&self`, such as particular curve's `Scalar` type. #[cfg(feature = "arithmetic")] pub trait SignPrimitive: AsRef + Into> + IsHigh + PrimeField> + Reduce> + Sized where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { /// Try to sign the prehashed message. /// /// Accepts the following arguments: /// /// - `k`: ephemeral scalar value. MUST BE UNIFORMLY RANDOM!!! /// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY /// SECURE DIGEST ALGORITHM!!! /// /// # Returns /// /// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`] /// which can be used to recover the verifying key for a given signature. fn try_sign_prehashed( &self, k: K, z: &FieldBytes, ) -> Result<(Signature, Option)> where K: AsRef + Invert>, { sign_prehashed(self, k, z).map(|(sig, recid)| (sig, (Some(recid)))) } /// Try to sign the given message digest deterministically using the method /// described in [RFC6979] for computing ECDSA ephemeral scalar `k`. /// /// Accepts the following parameters: /// - `z`: message digest to be signed. /// - `ad`: optional additional data, e.g. added entropy from an RNG /// /// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979 #[cfg(feature = "rfc6979")] fn try_sign_prehashed_rfc6979( &self, z: &FieldBytes, ad: &[u8], ) -> Result<(Signature, Option)> where Self: From> + Invert>, D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, { let k = Scalar::::from_repr(rfc6979::generate_k::( &self.to_repr(), &C::ORDER.encode_field_bytes(), z, ad, )) .unwrap(); self.try_sign_prehashed::(k, z) } } /// Verify the given prehashed message using ECDSA. /// /// This trait is intended to be implemented on type which can access /// the affine point represeting the public key via `&self`, such as a /// particular curve's `AffinePoint` type. #[cfg(feature = "arithmetic")] pub trait VerifyPrimitive: AffineCoordinates> + Copy + Sized where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { /// Verify the prehashed message against the provided ECDSA signature. /// /// Accepts the following arguments: /// /// - `z`: message digest to be verified. MUST BE OUTPUT OF A /// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!! /// - `sig`: signature to be verified against the key and message fn verify_prehashed(&self, z: &FieldBytes, sig: &Signature) -> Result<()> { verify_prehashed(&ProjectivePoint::::from(*self), z, sig) } /// Verify message digest against the provided signature. #[cfg(feature = "digest")] fn verify_digest(&self, msg_digest: D, sig: &Signature) -> Result<()> where D: FixedOutput>, { self.verify_prehashed(&msg_digest.finalize_fixed(), sig) } } /// Bind a preferred [`Digest`] algorithm to an elliptic curve type. /// /// Generally there is a preferred variety of the SHA-2 family used with ECDSA /// for a particular elliptic curve. /// /// This trait can be used to specify it, and with it receive a blanket impl of /// [`PrehashSignature`], used by [`signature_derive`][1]) for the [`Signature`] /// type for a particular elliptic curve. /// /// [1]: https://github.com/RustCrypto/traits/tree/master/signature/derive #[cfg(feature = "digest")] pub trait DigestPrimitive: PrimeCurve { /// Preferred digest to use when computing ECDSA signatures for this /// elliptic curve. This is typically a member of the SHA-2 family. type Digest: BlockSizeUser + Digest + FixedOutput> + FixedOutputReset; } #[cfg(feature = "digest")] impl PrehashSignature for Signature where C: DigestPrimitive, as core::ops::Add>::Output: ArrayLength, { type Digest = C::Digest; } /// Partial implementation of the `bits2int` function as defined in /// [RFC6979 § 2.3.2] as well as [SEC1] § 2.3.8. /// /// This is used to convert a message digest whose size may be smaller or /// larger than the size of the curve's scalar field into a serialized /// (unreduced) field element. /// /// [RFC6979 § 2.3.2]: https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2 /// [SEC1]: https://www.secg.org/sec1-v2.pdf pub fn bits2field(bits: &[u8]) -> Result> { // Minimum allowed bits size is half the field size if bits.len() < C::FieldBytesSize::USIZE / 2 { return Err(Error::new()); } let mut field_bytes = FieldBytes::::default(); match bits.len().cmp(&C::FieldBytesSize::USIZE) { cmp::Ordering::Equal => field_bytes.copy_from_slice(bits), cmp::Ordering::Less => { // If bits is smaller than the field size, pad with zeroes on the left field_bytes[(C::FieldBytesSize::USIZE - bits.len())..].copy_from_slice(bits); } cmp::Ordering::Greater => { // If bits is larger than the field size, truncate field_bytes.copy_from_slice(&bits[..C::FieldBytesSize::USIZE]); } } Ok(field_bytes) } /// Sign a prehashed message digest using the provided secret scalar and /// ephemeral scalar, returning an ECDSA signature. /// /// Accepts the following arguments: /// /// - `d`: signing key. MUST BE UNIFORMLY RANDOM!!! /// - `k`: ephemeral scalar value. MUST BE UNIFORMLY RANDOM!!! /// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY /// SECURE DIGEST ALGORITHM!!! /// /// # Returns /// /// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`] /// which can be used to recover the verifying key for a given signature. #[cfg(feature = "arithmetic")] #[allow(non_snake_case)] pub fn sign_prehashed( d: &Scalar, k: K, z: &FieldBytes, ) -> Result<(Signature, RecoveryId)> where C: PrimeCurve + CurveArithmetic, K: AsRef> + Invert>>, SignatureSize: ArrayLength, { // TODO(tarcieri): use `NonZeroScalar` for `k`. if k.as_ref().is_zero().into() { return Err(Error::new()); } let z = as Reduce>::reduce_bytes(z); // Compute scalar inversion of 𝑘 let k_inv = Option::>::from(k.invert()).ok_or_else(Error::new)?; // Compute 𝑹 = 𝑘×𝑮 let R = ProjectivePoint::::mul_by_generator(k.as_ref()).to_affine(); // Lift x-coordinate of 𝑹 (element of base field) into a serialized big // integer, then reduce it into an element of the scalar field let r = Scalar::::reduce_bytes(&R.x()); let x_is_reduced = r.to_repr() != R.x(); // Compute 𝒔 as a signature over 𝒓 and 𝒛. let s = k_inv * (z + (r * d)); // NOTE: `Signature::from_scalars` checks that both `r` and `s` are non-zero. let signature = Signature::from_scalars(r, s)?; let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced); Ok((signature, recovery_id)) } /// Verify the prehashed message against the provided ECDSA signature. /// /// Accepts the following arguments: /// /// - `q`: public key with which to verify the signature. /// - `z`: message digest to be verified. MUST BE OUTPUT OF A /// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!! /// - `sig`: signature to be verified against the key and message. #[cfg(feature = "arithmetic")] pub fn verify_prehashed( q: &ProjectivePoint, z: &FieldBytes, sig: &Signature, ) -> Result<()> where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { let z = Scalar::::reduce_bytes(z); let (r, s) = sig.split_scalars(); let s_inv = *s.invert_vartime(); let u1 = z * s_inv; let u2 = *r * s_inv; let x = ProjectivePoint::::lincomb(&ProjectivePoint::::generator(), &u1, q, &u2) .to_affine() .x(); if *r == Scalar::::reduce_bytes(&x) { Ok(()) } else { Err(Error::new()) } } #[cfg(test)] mod tests { use super::bits2field; use elliptic_curve::dev::MockCurve; use hex_literal::hex; #[test] fn bits2field_too_small() { assert!(bits2field::(b"").is_err()); } #[test] fn bits2field_size_less() { let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); let field_bytes = bits2field::(&prehash).unwrap(); assert_eq!( field_bytes.as_slice(), &hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") ); } #[test] fn bits2field_size_eq() { let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); let field_bytes = bits2field::(&prehash).unwrap(); assert_eq!(field_bytes.as_slice(), &prehash); } #[test] fn bits2field_size_greater() { let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); let field_bytes = bits2field::(&prehash).unwrap(); assert_eq!( field_bytes.as_slice(), &hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") ); } } ecdsa-0.16.9/src/lib.rs000064400000000000000000000523101046102023000127040ustar 00000000000000#![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] #![forbid(unsafe_code)] #![warn( clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::checked_conversions, clippy::implicit_saturating_sub, clippy::panic, clippy::panic_in_result_fn, clippy::unwrap_used, missing_docs, rust_2018_idioms, unused_lifetimes, unused_qualifications )] //! ## `serde` support //! //! When the `serde` feature of this crate is enabled, `Serialize` and //! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`] //! types. //! //! Please see type-specific documentation for more information. //! //! ## Interop //! //! Any crates which provide an implementation of ECDSA for a particular //! elliptic curve can leverage the types from this crate, along with the //! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in //! a generic, interoperable way by leveraging the [`Signature`] type with in //! conjunction with the [`signature::Signer`] and [`signature::Verifier`] //! traits. //! //! For example, the [`ring-compat`] crate implements the [`signature::Signer`] //! and [`signature::Verifier`] traits in conjunction with the //! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to //! wrap the ECDSA implementations from [*ring*] in a generic, interoperable //! API. //! //! [`k256`]: https://docs.rs/k256 //! [`p256`]: https://docs.rs/p256 //! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html //! [`p384`]: https://docs.rs/p384 //! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html //! [`ring-compat`]: https://docs.rs/ring-compat //! [*ring*]: https://docs.rs/ring #[cfg(feature = "alloc")] extern crate alloc; mod normalized; mod recovery; #[cfg(feature = "der")] pub mod der; #[cfg(feature = "dev")] pub mod dev; #[cfg(feature = "hazmat")] pub mod hazmat; #[cfg(feature = "signing")] mod signing; #[cfg(feature = "verifying")] mod verifying; pub use crate::{normalized::NormalizedSignature, recovery::RecoveryId}; // Re-export the `elliptic-curve` crate (and select types) pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve}; // Re-export the `signature` crate (and select types) pub use signature::{self, Error, Result, SignatureEncoding}; #[cfg(feature = "signing")] pub use crate::signing::SigningKey; #[cfg(feature = "verifying")] pub use crate::verifying::VerifyingKey; use core::{fmt, ops::Add}; use elliptic_curve::{ generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, FieldBytes, FieldBytesSize, ScalarPrimitive, }; #[cfg(feature = "alloc")] use alloc::vec::Vec; #[cfg(feature = "arithmetic")] use { core::str, elliptic_curve::{scalar::IsHigh, CurveArithmetic, NonZeroScalar}, }; #[cfg(feature = "digest")] use digest::{ const_oid::{AssociatedOid, ObjectIdentifier}, Digest, }; #[cfg(feature = "pkcs8")] use elliptic_curve::pkcs8::spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, }; #[cfg(feature = "serde")] use serdect::serde::{de, ser, Deserialize, Serialize}; #[cfg(all(feature = "alloc", feature = "pkcs8"))] use elliptic_curve::pkcs8::spki::{ self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier, }; /// OID for ECDSA with SHA-224 digests. /// /// ```text /// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 1 } /// ``` // TODO(tarcieri): use `ObjectIdentifier::push_arc` when const unwrap is stable #[cfg(feature = "digest")] pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1"); /// OID for ECDSA with SHA-256 digests. /// /// ```text /// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2"); /// OID for ECDSA with SHA-384 digests. /// /// ```text /// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3"); /// OID for ECDSA with SHA-512 digests. /// /// ```text /// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4"); #[cfg(feature = "digest")] const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4"); #[cfg(feature = "digest")] const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"); #[cfg(feature = "digest")] const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"); #[cfg(feature = "digest")] const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"); /// Size of a fixed sized signature for the given elliptic curve. pub type SignatureSize = as Add>::Output; /// Fixed-size byte array containing an ECDSA signature pub type SignatureBytes = GenericArray>; /// ECDSA signature (fixed-size). Generic over elliptic curve types. /// /// Serialized as fixed-sized big endian scalar values with no added framing: /// /// - `r`: field element size for the given curve, big-endian /// - `s`: field element size for the given curve, big-endian /// /// Both `r` and `s` MUST be non-zero. /// /// For example, in a curve with a 256-bit modulus like NIST P-256 or /// secp256k1, `r` and `s` will both be 32-bytes and serialized as big endian, /// resulting in a signature with a total of 64-bytes. /// /// ASN.1 DER-encoded signatures also supported via the /// [`Signature::from_der`] and [`Signature::to_der`] methods. /// /// # `serde` support /// /// When the `serde` feature of this crate is enabled, it provides support for /// serializing and deserializing ECDSA signatures using the `Serialize` and /// `Deserialize` traits. /// /// The serialization uses a hexadecimal encoding when used with /// "human readable" text formats, and a binary encoding otherwise. #[derive(Clone, Eq, PartialEq)] pub struct Signature { r: ScalarPrimitive, s: ScalarPrimitive, } impl Signature where C: PrimeCurve, SignatureSize: ArrayLength, { /// Parse a signature from fixed-width bytes, i.e. 2 * the size of /// [`FieldBytes`] for a particular curve. /// /// # Returns /// - `Ok(signature)` if the `r` and `s` components are both in the valid /// range `1..n` when serialized as concatenated big endian integers. /// - `Err(err)` if the `r` and/or `s` component of the signature is /// out-of-range when interpreted as a big endian integer. pub fn from_bytes(bytes: &SignatureBytes) -> Result { let (r_bytes, s_bytes) = bytes.split_at(C::FieldBytesSize::USIZE); let r = FieldBytes::::clone_from_slice(r_bytes); let s = FieldBytes::::clone_from_slice(s_bytes); Self::from_scalars(r, s) } /// Parse a signature from a byte slice. pub fn from_slice(slice: &[u8]) -> Result { if slice.len() == SignatureSize::::USIZE { Self::from_bytes(SignatureBytes::::from_slice(slice)) } else { Err(Error::new()) } } /// Parse a signature from ASN.1 DER. #[cfg(feature = "der")] pub fn from_der(bytes: &[u8]) -> Result where der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { der::Signature::::try_from(bytes).and_then(Self::try_from) } /// Create a [`Signature`] from the serialized `r` and `s` scalar values /// which comprise the signature. /// /// # Returns /// - `Ok(signature)` if the `r` and `s` components are both in the valid /// range `1..n` when serialized as concatenated big endian integers. /// - `Err(err)` if the `r` and/or `s` component of the signature is /// out-of-range when interpreted as a big endian integer. pub fn from_scalars(r: impl Into>, s: impl Into>) -> Result { let r = ScalarPrimitive::from_slice(&r.into()).map_err(|_| Error::new())?; let s = ScalarPrimitive::from_slice(&s.into()).map_err(|_| Error::new())?; if r.is_zero().into() || s.is_zero().into() { return Err(Error::new()); } Ok(Self { r, s }) } /// Split the signature into its `r` and `s` components, represented as bytes. pub fn split_bytes(&self) -> (FieldBytes, FieldBytes) { (self.r.to_bytes(), self.s.to_bytes()) } /// Serialize this signature as bytes. pub fn to_bytes(&self) -> SignatureBytes { let mut bytes = SignatureBytes::::default(); let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE); r_bytes.copy_from_slice(&self.r.to_bytes()); s_bytes.copy_from_slice(&self.s.to_bytes()); bytes } /// Serialize this signature as ASN.1 DER. #[cfg(feature = "der")] pub fn to_der(&self) -> der::Signature where der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { let (r, s) = self.split_bytes(); der::Signature::from_components(&r, &s).expect("DER encoding error") } /// Convert this signature into a byte vector. #[cfg(feature = "alloc")] pub fn to_vec(&self) -> Vec { self.to_bytes().to_vec() } } #[cfg(feature = "arithmetic")] impl Signature where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { /// Get the `r` component of this signature pub fn r(&self) -> NonZeroScalar { NonZeroScalar::new(self.r.into()).unwrap() } /// Get the `s` component of this signature pub fn s(&self) -> NonZeroScalar { NonZeroScalar::new(self.s.into()).unwrap() } /// Split the signature into its `r` and `s` scalars. pub fn split_scalars(&self) -> (NonZeroScalar, NonZeroScalar) { (self.r(), self.s()) } /// Normalize signature into "low S" form as described in /// [BIP 0062: Dealing with Malleability][1]. /// /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki pub fn normalize_s(&self) -> Option { let s = self.s(); if s.is_high().into() { let mut result = self.clone(); result.s = ScalarPrimitive::from(-s); Some(result) } else { None } } } impl Copy for Signature where C: PrimeCurve, SignatureSize: ArrayLength, as ArrayLength>::ArrayType: Copy, { } impl From> for SignatureBytes where C: PrimeCurve, SignatureSize: ArrayLength, { fn from(signature: Signature) -> SignatureBytes { signature.to_bytes() } } impl SignatureEncoding for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { type Repr = SignatureBytes; } impl TryFrom<&[u8]> for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { type Error = Error; fn try_from(slice: &[u8]) -> Result { Self::from_slice(slice) } } impl fmt::Debug for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ecdsa::Signature<{:?}>(", C::default())?; for byte in self.to_bytes() { write!(f, "{:02X}", byte)?; } write!(f, ")") } } impl fmt::Display for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:X}", self) } } impl fmt::LowerHex for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.to_bytes() { write!(f, "{:02x}", byte)?; } Ok(()) } } impl fmt::UpperHex for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.to_bytes() { write!(f, "{:02X}", byte)?; } Ok(()) } } #[cfg(feature = "arithmetic")] impl str::FromStr for Signature where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { type Err = Error; fn from_str(hex: &str) -> Result { if hex.as_bytes().len() != C::FieldBytesSize::USIZE * 4 { return Err(Error::new()); } // This check is mainly to ensure `hex.split_at` below won't panic if !hex .as_bytes() .iter() .all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z')) { return Err(Error::new()); } let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2); let r = r_hex .parse::>() .map_err(|_| Error::new())?; let s = s_hex .parse::>() .map_err(|_| Error::new())?; Self::from_scalars(r, s) } } /// ECDSA [`ObjectIdentifier`] which identifies the digest used by default /// with the `Signer` and `Verifier` traits. /// /// To support non-default digest algorithms, use the [`SignatureWithOid`] /// type instead. #[cfg(all(feature = "digest", feature = "hazmat"))] impl AssociatedOid for Signature where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, { const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) { Some(oid) => oid, None => panic!("no RFC5758 ECDSA OID defined for DigestPrimitive::Digest"), }; } /// ECDSA `AlgorithmIdentifier` which identifies the digest used by default /// with the `Signer` and `Verifier` traits. #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for Signature where C: PrimeCurve, Self: AssociatedOid, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { oid: Self::OID, parameters: None, }; } #[cfg(feature = "serde")] impl Serialize for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer) } } #[cfg(feature = "serde")] impl<'de, C> Deserialize<'de> for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { let mut bytes = SignatureBytes::::default(); serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?; Self::try_from(bytes.as_slice()).map_err(de::Error::custom) } } /// An extended [`Signature`] type which is parameterized by an /// `ObjectIdentifier` which identifies the ECDSA variant used by a /// particular signature. /// /// Valid `ObjectIdentifiers` are defined in [RFC5758 § 3.2]: /// /// - SHA-224: [`ECDSA_SHA224_OID`] (1.2.840.10045.4.3.1) /// - SHA-256: [`ECDSA_SHA256_OID`] (1.2.840.10045.4.3.2) /// - SHA-384: [`ECDSA_SHA384_OID`] (1.2.840.10045.4.3.3) /// - SHA-512: [`ECDSA_SHA512_OID`] (1.2.840.10045.4.3.4) /// /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 #[cfg(feature = "digest")] #[derive(Clone, Eq, PartialEq)] pub struct SignatureWithOid { /// Inner signature type. signature: Signature, /// OID which identifies the ECDSA variant used. /// /// MUST be one of the ECDSA algorithm variants as defined in RFC5758. /// /// These OIDs begin with `1.2.840.10045.4`. oid: ObjectIdentifier, } #[cfg(feature = "digest")] impl SignatureWithOid where C: PrimeCurve, { /// Create a new signature with an explicitly provided OID. /// /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for /// ECDSA variants. /// /// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 pub fn new(signature: Signature, oid: ObjectIdentifier) -> Result { // TODO(tarcieri): use `ObjectIdentifier::starts_with` for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3") .arcs() .zip(oid.arcs()) { if arc1 != arc2 { return Err(Error::new()); } } Ok(Self { signature, oid }) } /// Create a new signature, determining the OID from the given digest. /// /// Supports SHA-2 family digests as enumerated in [RFC5758 § 3.2], i.e. /// SHA-224, SHA-256, SHA-384, or SHA-512. /// /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 pub fn new_with_digest(signature: Signature) -> Result where D: AssociatedOid + Digest, { let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?; Ok(Self { signature, oid }) } /// Parse a signature from fixed-with bytes. pub fn from_bytes_with_digest(bytes: &SignatureBytes) -> Result where D: AssociatedOid + Digest, SignatureSize: ArrayLength, { Self::new_with_digest::(Signature::::from_bytes(bytes)?) } /// Parse a signature from a byte slice. pub fn from_slice_with_digest(slice: &[u8]) -> Result where D: AssociatedOid + Digest, SignatureSize: ArrayLength, { Self::new_with_digest::(Signature::::from_slice(slice)?) } /// Get the fixed-width ECDSA signature. pub fn signature(&self) -> &Signature { &self.signature } /// Get the ECDSA OID for this signature. pub fn oid(&self) -> ObjectIdentifier { self.oid } /// Serialize this signature as bytes. pub fn to_bytes(&self) -> SignatureBytes where SignatureSize: ArrayLength, { self.signature.to_bytes() } } #[cfg(feature = "digest")] impl Copy for SignatureWithOid where C: PrimeCurve, SignatureSize: ArrayLength, as ArrayLength>::ArrayType: Copy, { } #[cfg(feature = "digest")] impl From> for Signature where C: PrimeCurve, { fn from(sig: SignatureWithOid) -> Signature { sig.signature } } #[cfg(feature = "digest")] impl From> for SignatureBytes where C: PrimeCurve, SignatureSize: ArrayLength, { fn from(signature: SignatureWithOid) -> SignatureBytes { signature.to_bytes() } } /// NOTE: this implementation assumes the default digest for the given elliptic /// curve as defined by [`hazmat::DigestPrimitive`]. /// /// When working with alternative digests, you will need to use e.g. /// [`SignatureWithOid::new_with_digest`]. #[cfg(all(feature = "digest", feature = "hazmat"))] impl SignatureEncoding for SignatureWithOid where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, SignatureSize: ArrayLength, { type Repr = SignatureBytes; } /// NOTE: this implementation assumes the default digest for the given elliptic /// curve as defined by [`hazmat::DigestPrimitive`]. /// /// When working with alternative digests, you will need to use e.g. /// [`SignatureWithOid::new_with_digest`]. #[cfg(all(feature = "digest", feature = "hazmat"))] impl TryFrom<&[u8]> for SignatureWithOid where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, SignatureSize: ArrayLength, { type Error = Error; fn try_from(slice: &[u8]) -> Result { Self::new(Signature::::from_slice(slice)?, C::Digest::OID) } } #[cfg(all(feature = "alloc", feature = "pkcs8"))] impl DynAssociatedAlgorithmIdentifier for SignatureWithOid where C: PrimeCurve, { fn algorithm_identifier(&self) -> spki::Result { Ok(AlgorithmIdentifierOwned { oid: self.oid, parameters: None, }) } } /// Get the ECDSA OID for a given digest OID. #[cfg(feature = "digest")] const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option { match digest_oid { SHA224_OID => Some(ECDSA_SHA224_OID), SHA256_OID => Some(ECDSA_SHA256_OID), SHA384_OID => Some(ECDSA_SHA384_OID), SHA512_OID => Some(ECDSA_SHA512_OID), _ => None, } } ecdsa-0.16.9/src/normalized.rs000064400000000000000000000004511046102023000143010ustar 00000000000000//! Support for ECDSA signatures with low-S normalization. use crate::Signature; use elliptic_curve::PrimeCurve; /// ECDSA signature with low-S normalization applied. #[derive(Clone, Eq, PartialEq)] #[repr(transparent)] pub struct NormalizedSignature { inner: Signature, } ecdsa-0.16.9/src/recovery.rs000064400000000000000000000270111046102023000137740ustar 00000000000000//! Public key recovery support. use crate::{Error, Result}; #[cfg(feature = "signing")] use { crate::{hazmat::SignPrimitive, SigningKey}, elliptic_curve::subtle::CtOption, signature::{hazmat::PrehashSigner, DigestSigner, Signer}, }; #[cfg(feature = "verifying")] use { crate::{hazmat::VerifyPrimitive, VerifyingKey}, elliptic_curve::{ bigint::CheckedAdd, ops::{LinearCombination, Reduce}, point::DecompressPoint, sec1::{self, FromEncodedPoint, ToEncodedPoint}, AffinePoint, FieldBytesEncoding, FieldBytesSize, Group, PrimeField, ProjectivePoint, }, signature::hazmat::PrehashVerifier, }; #[cfg(any(feature = "signing", feature = "verifying"))] use { crate::{ hazmat::{bits2field, DigestPrimitive}, Signature, SignatureSize, }, elliptic_curve::{ generic_array::ArrayLength, ops::Invert, CurveArithmetic, PrimeCurve, Scalar, }, signature::digest::Digest, }; /// Recovery IDs, a.k.a. "recid". /// /// This is an integer value `0`, `1`, `2`, or `3` included along with a /// signature which is used during the recovery process to select the correct /// public key from the signature. /// /// It consists of two bits of information: /// /// - low bit (0/1): was the y-coordinate of the affine point resulting from /// the fixed-base multiplication 𝑘×𝑮 odd? This part of the algorithm /// functions similar to point decompression. /// - hi bit (3/4): did the affine x-coordinate of 𝑘×𝑮 overflow the order of /// the scalar field, requiring a reduction when computing `r`? #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct RecoveryId(u8); impl RecoveryId { /// Maximum supported value for the recovery ID (inclusive). pub const MAX: u8 = 3; /// Create a new [`RecoveryId`] from the following 1-bit arguments: /// /// - `is_y_odd`: is the affine y-coordinate of 𝑘×𝑮 odd? /// - `is_x_reduced`: did the affine x-coordinate of 𝑘×𝑮 overflow the curve order? pub const fn new(is_y_odd: bool, is_x_reduced: bool) -> Self { Self((is_x_reduced as u8) << 1 | (is_y_odd as u8)) } /// Did the affine x-coordinate of 𝑘×𝑮 overflow the curve order? pub const fn is_x_reduced(self) -> bool { (self.0 & 0b10) != 0 } /// Is the affine y-coordinate of 𝑘×𝑮 odd? pub const fn is_y_odd(self) -> bool { (self.0 & 1) != 0 } /// Convert a `u8` into a [`RecoveryId`]. pub const fn from_byte(byte: u8) -> Option { if byte <= Self::MAX { Some(Self(byte)) } else { None } } /// Convert this [`RecoveryId`] into a `u8`. pub const fn to_byte(self) -> u8 { self.0 } } #[cfg(feature = "verifying")] impl RecoveryId { /// Given a public key, message, and signature, use trial recovery /// to determine if a suitable recovery ID exists, or return an error /// otherwise. pub fn trial_recovery_from_msg( verifying_key: &VerifyingKey, msg: &[u8], signature: &Signature, ) -> Result where C: DigestPrimitive + PrimeCurve + CurveArithmetic, AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArrayLength, { Self::trial_recovery_from_digest(verifying_key, C::Digest::new_with_prefix(msg), signature) } /// Given a public key, message digest, and signature, use trial recovery /// to determine if a suitable recovery ID exists, or return an error /// otherwise. pub fn trial_recovery_from_digest( verifying_key: &VerifyingKey, digest: D, signature: &Signature, ) -> Result where C: PrimeCurve + CurveArithmetic, D: Digest, AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArrayLength, { Self::trial_recovery_from_prehash(verifying_key, &digest.finalize(), signature) } /// Given a public key, message digest, and signature, use trial recovery /// to determine if a suitable recovery ID exists, or return an error /// otherwise. pub fn trial_recovery_from_prehash( verifying_key: &VerifyingKey, prehash: &[u8], signature: &Signature, ) -> Result where C: PrimeCurve + CurveArithmetic, AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArrayLength, { for id in 0..=Self::MAX { let recovery_id = RecoveryId(id); if let Ok(vk) = VerifyingKey::recover_from_prehash(prehash, signature, recovery_id) { if verifying_key == &vk { return Ok(recovery_id); } } } Err(Error::new()) } } impl TryFrom for RecoveryId { type Error = Error; fn try_from(byte: u8) -> Result { Self::from_byte(byte).ok_or_else(Error::new) } } impl From for u8 { fn from(id: RecoveryId) -> u8 { id.0 } } #[cfg(feature = "signing")] impl SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { /// Sign the given message prehash, returning a signature and recovery ID. pub fn sign_prehash_recoverable(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { let z = bits2field::(prehash)?; let (sig, recid) = self .as_nonzero_scalar() .try_sign_prehashed_rfc6979::(&z, &[])?; Ok((sig, recid.ok_or_else(Error::new)?)) } /// Sign the given message digest, returning a signature and recovery ID. pub fn sign_digest_recoverable(&self, msg_digest: D) -> Result<(Signature, RecoveryId)> where D: Digest, { self.sign_prehash_recoverable(&msg_digest.finalize()) } /// Sign the given message, hashing it with the curve's default digest /// function, and returning a signature and recovery ID. pub fn sign_recoverable(&self, msg: &[u8]) -> Result<(Signature, RecoveryId)> { self.sign_digest_recoverable(C::Digest::new_with_prefix(msg)) } } #[cfg(feature = "signing")] impl DigestSigner, RecoveryId)> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest(&self, msg_digest: D) -> Result<(Signature, RecoveryId)> { self.sign_digest_recoverable(msg_digest) } } #[cfg(feature = "signing")] impl PrehashSigner<(Signature, RecoveryId)> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { self.sign_prehash_recoverable(prehash) } } #[cfg(feature = "signing")] impl Signer<(Signature, RecoveryId)> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result<(Signature, RecoveryId)> { self.sign_recoverable(msg) } } #[cfg(feature = "verifying")] impl VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArrayLength, { /// Recover a [`VerifyingKey`] from the given message, signature, and /// [`RecoveryId`]. /// /// The message is first hashed using this curve's [`DigestPrimitive`]. pub fn recover_from_msg( msg: &[u8], signature: &Signature, recovery_id: RecoveryId, ) -> Result where C: DigestPrimitive, { Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id) } /// Recover a [`VerifyingKey`] from the given message [`Digest`], /// signature, and [`RecoveryId`]. pub fn recover_from_digest( msg_digest: D, signature: &Signature, recovery_id: RecoveryId, ) -> Result where D: Digest, { Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id) } /// Recover a [`VerifyingKey`] from the given `prehash` of a message, the /// signature over that prehashed message, and a [`RecoveryId`]. #[allow(non_snake_case)] pub fn recover_from_prehash( prehash: &[u8], signature: &Signature, recovery_id: RecoveryId, ) -> Result { let (r, s) = signature.split_scalars(); let z = as Reduce>::reduce_bytes(&bits2field::(prehash)?); let mut r_bytes = r.to_repr(); if recovery_id.is_x_reduced() { match Option::::from( C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER), ) { Some(restored) => r_bytes = restored.encode_field_bytes(), // No reduction should happen here if r was reduced None => return Err(Error::new()), }; } let R = AffinePoint::::decompress(&r_bytes, u8::from(recovery_id.is_y_odd()).into()); if R.is_none().into() { return Err(Error::new()); } let R = ProjectivePoint::::from(R.unwrap()); let r_inv = *r.invert(); let u1 = -(r_inv * z); let u2 = r_inv * *s; let pk = ProjectivePoint::::lincomb(&ProjectivePoint::::generator(), &u1, &R, &u2); let vk = Self::from_affine(pk.into())?; // Ensure signature verifies with the recovered key vk.verify_prehash(prehash, signature)?; Ok(vk) } } #[cfg(test)] mod tests { use super::RecoveryId; #[test] fn new() { assert_eq!(RecoveryId::new(false, false).to_byte(), 0); assert_eq!(RecoveryId::new(true, false).to_byte(), 1); assert_eq!(RecoveryId::new(false, true).to_byte(), 2); assert_eq!(RecoveryId::new(true, true).to_byte(), 3); } #[test] fn try_from() { for n in 0u8..=3 { assert_eq!(RecoveryId::try_from(n).unwrap().to_byte(), n); } for n in 4u8..=255 { assert!(RecoveryId::try_from(n).is_err()); } } #[test] fn is_x_reduced() { assert_eq!(RecoveryId::try_from(0).unwrap().is_x_reduced(), false); assert_eq!(RecoveryId::try_from(1).unwrap().is_x_reduced(), false); assert_eq!(RecoveryId::try_from(2).unwrap().is_x_reduced(), true); assert_eq!(RecoveryId::try_from(3).unwrap().is_x_reduced(), true); } #[test] fn is_y_odd() { assert_eq!(RecoveryId::try_from(0).unwrap().is_y_odd(), false); assert_eq!(RecoveryId::try_from(1).unwrap().is_y_odd(), true); assert_eq!(RecoveryId::try_from(2).unwrap().is_y_odd(), false); assert_eq!(RecoveryId::try_from(3).unwrap().is_y_odd(), true); } } ecdsa-0.16.9/src/signing.rs000064400000000000000000000447231046102023000136050ustar 00000000000000//! ECDSA signing: producing signatures using a [`SigningKey`]. use crate::{ ecdsa_oid_for_digest, hazmat::{bits2field, DigestPrimitive, SignPrimitive}, Error, Result, Signature, SignatureSize, SignatureWithOid, }; use core::fmt::{self, Debug}; use digest::{const_oid::AssociatedOid, Digest, FixedOutput}; use elliptic_curve::{ generic_array::ArrayLength, group::ff::PrimeField, ops::Invert, subtle::{Choice, ConstantTimeEq, CtOption}, zeroize::{Zeroize, ZeroizeOnDrop}, CurveArithmetic, FieldBytes, FieldBytesSize, NonZeroScalar, PrimeCurve, Scalar, SecretKey, }; use signature::{ hazmat::{PrehashSigner, RandomizedPrehashSigner}, rand_core::CryptoRngCore, DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer, }; #[cfg(feature = "der")] use {crate::der, core::ops::Add}; #[cfg(feature = "pem")] use { crate::elliptic_curve::pkcs8::{DecodePrivateKey, EncodePrivateKey, SecretDocument}, core::str::FromStr, }; #[cfg(feature = "pkcs8")] use crate::elliptic_curve::{ pkcs8::{ self, der::AnyRef, spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}, ObjectIdentifier, }, sec1::{self, FromEncodedPoint, ToEncodedPoint}, AffinePoint, }; #[cfg(feature = "verifying")] use {crate::VerifyingKey, elliptic_curve::PublicKey, signature::KeypairRef}; /// ECDSA secret key used for signing. Generic over prime order elliptic curves /// (e.g. NIST P-curves) /// /// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a /// [`SignPrimitive`] impl on its associated `Scalar` type. /// /// ## Usage /// /// The [`signature`] crate defines the following traits which are the /// primary API for signing: /// /// - [`Signer`]: sign a message using this key /// - [`DigestSigner`]: sign the output of a [`Digest`] using this key /// - [`PrehashSigner`]: sign the low-level raw output bytes of a message digest /// /// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html) /// for examples of using this type with a concrete elliptic curve. #[derive(Clone)] pub struct SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { /// ECDSA signing keys are non-zero elements of a given curve's scalar field. secret_scalar: NonZeroScalar, /// Verifying key which corresponds to this signing key. #[cfg(feature = "verifying")] verifying_key: VerifyingKey, } impl SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { /// Generate a cryptographically random [`SigningKey`]. pub fn random(rng: &mut impl CryptoRngCore) -> Self { NonZeroScalar::::random(rng).into() } /// Initialize signing key from a raw scalar serialized as a byte array. pub fn from_bytes(bytes: &FieldBytes) -> Result { SecretKey::::from_bytes(bytes) .map(Into::into) .map_err(|_| Error::new()) } /// Initialize signing key from a raw scalar serialized as a byte slice. pub fn from_slice(bytes: &[u8]) -> Result { SecretKey::::from_slice(bytes) .map(Into::into) .map_err(|_| Error::new()) } /// Serialize this [`SigningKey`] as bytes pub fn to_bytes(&self) -> FieldBytes { self.secret_scalar.to_repr() } /// Borrow the secret [`NonZeroScalar`] value for this key. /// /// # ⚠️ Warning /// /// This value is key material. /// /// Please treat it with the care it deserves! pub fn as_nonzero_scalar(&self) -> &NonZeroScalar { &self.secret_scalar } /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`]. #[cfg(feature = "verifying")] pub fn verifying_key(&self) -> &VerifyingKey { &self.verifying_key } } // // `*Signer` trait impls // /// Sign message digest using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl DigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest(&self, msg_digest: D) -> Result> { self.sign_prehash(&msg_digest.finalize_fixed()) } } /// Sign message prehash using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl PrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { let z = bits2field::(prehash)?; Ok(self .secret_scalar .try_sign_prehashed_rfc6979::(&z, &[])? .0) } } /// Sign message using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { self.try_sign_digest(C::Digest::new_with_prefix(msg)) } } impl RandomizedDigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, msg_digest: D, ) -> Result> { self.sign_prehash_with_rng(rng, &msg_digest.finalize_fixed()) } } impl RandomizedPrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> Result> { let z = bits2field::(prehash)?; let mut ad = FieldBytes::::default(); rng.fill_bytes(&mut ad); Ok(self .secret_scalar .try_sign_prehashed_rfc6979::(&z, &ad)? .0) } } impl RandomizedSigner> for SigningKey where Self: RandomizedDigestSigner>, C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_with_rng(&self, rng: &mut impl CryptoRngCore, msg: &[u8]) -> Result> { self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg)) } } impl DigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: AssociatedOid + Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest(&self, msg_digest: D) -> Result> { let signature: Signature = self.try_sign_digest(msg_digest)?; let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?; SignatureWithOid::new(signature, oid) } } impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, C::Digest: AssociatedOid, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { self.try_sign_digest(C::Digest::new_with_prefix(msg)) } } #[cfg(feature = "der")] impl PrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { PrehashSigner::>::sign_prehash(self, prehash).map(Into::into) } } #[cfg(feature = "der")] impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { Signer::>::try_sign(self, msg).map(Into::into) } } #[cfg(feature = "der")] impl RandomizedDigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, msg_digest: D, ) -> Result> { RandomizedDigestSigner::>::try_sign_digest_with_rng(self, rng, msg_digest) .map(Into::into) } } #[cfg(feature = "der")] impl RandomizedPrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> Result> { RandomizedPrehashSigner::>::sign_prehash_with_rng(self, rng, prehash) .map(Into::into) } } #[cfg(feature = "der")] impl RandomizedSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, msg: &[u8], ) -> Result> { RandomizedSigner::>::try_sign_with_rng(self, rng, msg).map(Into::into) } } // // Other trait impls // #[cfg(feature = "verifying")] impl AsRef> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn as_ref(&self) -> &VerifyingKey { &self.verifying_key } } impl ConstantTimeEq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn ct_eq(&self, other: &Self) -> Choice { self.secret_scalar.ct_eq(&other.secret_scalar) } } impl Debug for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningKey").finish_non_exhaustive() } } impl Drop for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn drop(&mut self) { self.secret_scalar.zeroize(); } } /// Constant-time comparison impl Eq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { } impl PartialEq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn eq(&self, other: &SigningKey) -> bool { self.ct_eq(other).into() } } impl From> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_scalar: NonZeroScalar) -> Self { #[cfg(feature = "verifying")] let public_key = PublicKey::from_secret_scalar(&secret_scalar); Self { secret_scalar, #[cfg(feature = "verifying")] verifying_key: public_key.into(), } } } impl From> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: SecretKey) -> Self { Self::from(&secret_key) } } impl From<&SecretKey> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SecretKey) -> Self { secret_key.to_nonzero_scalar().into() } } impl From> for SecretKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(key: SigningKey) -> Self { key.secret_scalar.into() } } impl From<&SigningKey> for SecretKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SigningKey) -> Self { secret_key.secret_scalar.into() } } impl TryFrom<&[u8]> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Error = Error; fn try_from(bytes: &[u8]) -> Result { Self::from_slice(bytes) } } impl ZeroizeOnDrop for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { } #[cfg(feature = "verifying")] impl From> for VerifyingKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(signing_key: SigningKey) -> VerifyingKey { signing_key.verifying_key } } #[cfg(feature = "verifying")] impl From<&SigningKey> for VerifyingKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(signing_key: &SigningKey) -> VerifyingKey { signing_key.verifying_key } } #[cfg(feature = "verifying")] impl KeypairRef for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type VerifyingKey = VerifyingKey; } #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for SigningKey where C: AssociatedOid + CurveArithmetic + PrimeCurve, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Params = ObjectIdentifier; const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = SecretKey::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl SignatureAlgorithmIdentifier for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, Signature: AssociatedAlgorithmIdentifier>, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier = Signature::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl TryFrom> for SigningKey where C: PrimeCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { SecretKey::try_from(private_key_info).map(Into::into) } } #[cfg(feature = "pem")] impl EncodePrivateKey for SigningKey where C: AssociatedOid + PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn to_pkcs8_der(&self) -> pkcs8::Result { SecretKey::from(self.secret_scalar).to_pkcs8_der() } } #[cfg(feature = "pem")] impl FromStr for SigningKey where C: PrimeCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Err = Error; fn from_str(s: &str) -> Result { Self::from_pkcs8_pem(s).map_err(|_| Error::new()) } } ecdsa-0.16.9/src/verifying.rs000064400000000000000000000351141046102023000141430ustar 00000000000000//! ECDSA verifying: checking signatures are authentic using a [`VerifyingKey`]. use crate::{ hazmat::{bits2field, DigestPrimitive, VerifyPrimitive}, Error, Result, Signature, SignatureSize, }; use core::{cmp::Ordering, fmt::Debug}; use elliptic_curve::{ generic_array::ArrayLength, point::PointCompression, sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint}, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, }; use signature::{ digest::{Digest, FixedOutput}, hazmat::PrehashVerifier, DigestVerifier, Verifier, }; #[cfg(feature = "alloc")] use alloc::boxed::Box; #[cfg(feature = "der")] use {crate::der, core::ops::Add}; #[cfg(feature = "pem")] use { core::str::FromStr, elliptic_curve::pkcs8::{DecodePublicKey, EncodePublicKey}, }; #[cfg(feature = "pkcs8")] use elliptic_curve::pkcs8::{ self, der::AnyRef, spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}, AssociatedOid, ObjectIdentifier, }; #[cfg(feature = "sha2")] use { crate::{ SignatureWithOid, ECDSA_SHA224_OID, ECDSA_SHA256_OID, ECDSA_SHA384_OID, ECDSA_SHA512_OID, }, sha2::{Sha224, Sha256, Sha384, Sha512}, }; #[cfg(all(feature = "pem", feature = "serde"))] use serdect::serde::{de, ser, Deserialize, Serialize}; /// ECDSA public key used for verifying signatures. Generic over prime order /// elliptic curves (e.g. NIST P-curves) /// /// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a /// [`VerifyPrimitive`] impl on its associated `AffinePoint` type. /// /// ## Usage /// /// The [`signature`] crate defines the following traits which are the /// primary API for verifying: /// /// - [`Verifier`]: verify a message against a provided key and signature /// - [`DigestVerifier`]: verify a message [`Digest`] against a provided key and signature /// - [`PrehashVerifier`]: verify the low-level raw output bytes of a message digest /// /// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html) /// for examples of using this type with a concrete elliptic curve. /// /// # `serde` support /// /// When the `serde` feature of this crate is enabled, it provides support for /// serializing and deserializing ECDSA signatures using the `Serialize` and /// `Deserialize` traits. /// /// The serialization leverages the encoding used by the [`PublicKey`] type, /// which is a binary-oriented ASN.1 DER encoding. #[derive(Clone, Debug)] pub struct VerifyingKey where C: PrimeCurve + CurveArithmetic, { pub(crate) inner: PublicKey, } impl VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { /// Initialize [`VerifyingKey`] from a SEC1-encoded public key. pub fn from_sec1_bytes(bytes: &[u8]) -> Result { PublicKey::from_sec1_bytes(bytes) .map(|pk| Self { inner: pk }) .map_err(|_| Error::new()) } /// Initialize [`VerifyingKey`] from an affine point. /// /// Returns an [`Error`] if the given affine point is the additive identity /// (a.k.a. point at infinity). pub fn from_affine(affine: AffinePoint) -> Result { Ok(Self { inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?, }) } /// Initialize [`VerifyingKey`] from an [`EncodedPoint`]. pub fn from_encoded_point(public_key: &EncodedPoint) -> Result { Option::from(PublicKey::::from_encoded_point(public_key)) .map(|public_key| Self { inner: public_key }) .ok_or_else(Error::new) } /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally /// applying point compression. pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint { self.inner.to_encoded_point(compress) } /// Convert this [`VerifyingKey`] into the /// `Elliptic-Curve-Point-to-Octet-String` encoding described in /// SEC 1: Elliptic Curve Cryptography (Version 2.0) section 2.3.3 /// (page 10). /// /// #[cfg(feature = "alloc")] pub fn to_sec1_bytes(&self) -> Box<[u8]> where C: PointCompression, { self.inner.to_sec1_bytes() } /// Borrow the inner [`AffinePoint`] for this public key. pub fn as_affine(&self) -> &AffinePoint { self.inner.as_affine() } } // // `*Verifier` trait impls // impl DigestVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, D: Digest + FixedOutput>, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify_digest(&self, msg_digest: D, signature: &Signature) -> Result<()> { self.inner.as_affine().verify_digest(msg_digest, signature) } } impl PrehashVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> { let field = bits2field::(prehash)?; self.inner.as_affine().verify_prehashed(&field, signature) } } impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify(&self, msg: &[u8], signature: &Signature) -> Result<()> { self.verify_digest(C::Digest::new_with_prefix(msg), signature) } } #[cfg(feature = "sha2")] impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify(&self, msg: &[u8], sig: &SignatureWithOid) -> Result<()> { match sig.oid() { ECDSA_SHA224_OID => self.verify_prehash(&Sha224::digest(msg), sig.signature()), ECDSA_SHA256_OID => self.verify_prehash(&Sha256::digest(msg), sig.signature()), ECDSA_SHA384_OID => self.verify_prehash(&Sha384::digest(msg), sig.signature()), ECDSA_SHA512_OID => self.verify_prehash(&Sha512::digest(msg), sig.signature()), _ => Err(Error::new()), } } } #[cfg(feature = "der")] impl DigestVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, D: Digest + FixedOutput>, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify_digest(&self, msg_digest: D, signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; DigestVerifier::>::verify_digest(self, msg_digest, &signature) } } #[cfg(feature = "der")] impl PrehashVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify_prehash(&self, prehash: &[u8], signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; PrehashVerifier::>::verify_prehash(self, prehash, &signature) } } #[cfg(feature = "der")] impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify(&self, msg: &[u8], signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; Verifier::>::verify(self, msg, &signature) } } // // Other trait impls // impl AsRef> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn as_ref(&self) -> &AffinePoint { self.as_affine() } } impl Copy for VerifyingKey where C: PrimeCurve + CurveArithmetic {} impl From> for CompressedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: VerifyingKey) -> CompressedPoint { verifying_key.inner.into() } } impl From<&VerifyingKey> for CompressedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: &VerifyingKey) -> CompressedPoint { verifying_key.inner.into() } } impl From> for EncodedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: VerifyingKey) -> EncodedPoint { verifying_key.inner.into() } } impl From<&VerifyingKey> for EncodedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: &VerifyingKey) -> EncodedPoint { verifying_key.inner.into() } } impl Eq for VerifyingKey where C: PrimeCurve + CurveArithmetic {} impl PartialEq for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) } } impl From> for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn from(public_key: PublicKey) -> VerifyingKey { VerifyingKey { inner: public_key } } } impl From<&PublicKey> for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn from(public_key: &PublicKey) -> VerifyingKey { (*public_key).into() } } impl From> for PublicKey where C: PrimeCurve + CurveArithmetic, { fn from(verifying_key: VerifyingKey) -> PublicKey { verifying_key.inner } } impl From<&VerifyingKey> for PublicKey where C: PrimeCurve + CurveArithmetic, { fn from(verifying_key: &VerifyingKey) -> PublicKey { (*verifying_key).into() } } impl PartialOrd for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn partial_cmp(&self, other: &Self) -> Option { self.inner.partial_cmp(&other.inner) } } impl Ord for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn cmp(&self, other: &Self) -> Ordering { self.inner.cmp(&other.inner) } } impl TryFrom<&[u8]> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Error = Error; fn try_from(bytes: &[u8]) -> Result { Self::from_sec1_bytes(bytes) } } #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for VerifyingKey where C: AssociatedOid + CurveArithmetic + PrimeCurve, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Params = ObjectIdentifier; const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = PublicKey::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl SignatureAlgorithmIdentifier for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Signature: AssociatedAlgorithmIdentifier>, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier = Signature::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl TryFrom> for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { PublicKey::try_from(spki).map(|inner| Self { inner }) } } #[cfg(feature = "pem")] impl EncodePublicKey for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn to_public_key_der(&self) -> pkcs8::spki::Result { self.inner.to_public_key_der() } } #[cfg(feature = "pem")] impl FromStr for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Err = Error; fn from_str(s: &str) -> Result { Self::from_public_key_pem(s).map_err(|_| Error::new()) } } #[cfg(all(feature = "pem", feature = "serde"))] impl Serialize for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { self.inner.serialize(serializer) } } #[cfg(all(feature = "pem", feature = "serde"))] impl<'de, C> Deserialize<'de> for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { PublicKey::::deserialize(deserializer).map(Into::into) } } ecdsa-0.16.9/tests/lib.rs000064400000000000000000000005611046102023000132600ustar 00000000000000//! Smoke tests which use `MockCurve` #![cfg(feature = "dev")] use elliptic_curve::dev::MockCurve; type Signature = ecdsa::Signature; type SignatureBytes = ecdsa::SignatureBytes; #[test] fn rejects_all_zero_signature() { let all_zero_bytes = SignatureBytes::default(); assert!(Signature::try_from(all_zero_bytes.as_ref()).is_err()); }