简体   繁体   中英

Rust: How to unify signature curves with Generics?

I'm looking to a build a structure that stores/serializes/deserializes an ECDSA signature as part of a larger structure. This structure can use NIST P-256, NIST P-384, or secp256k1 as part of user input. This is what I'm trying to do (simplified):

#![allow(dead_code, unused_imports)]

use ecdsa::{signature::Signer, SigningKey};
use elliptic_curve::{PublicKey, SecretKey};
use k256::Secp256k1;
use p256::NistP256;
use p384::NistP384;
use rand_core::OsRng;

#[derive(Debug)]
pub struct Signature<T> {
    signature: ecdsa::Signature<T>,
}

impl<T> Signature<T> {
    pub fn sign(secret_key: SecretKey<T>, message: &[u8]) -> Signature<T> {
        let signature = SigningKey::from(&secret_key).sign(&message);

        Signature { signature }
    }
}

fn main() {
    let key: SecretKey<NistP256> = SecretKey::random(&mut OsRng);

    let sig: Signature<NistP256> = Signature::sign(key, "foo".as_bytes());

    dbg!(sig);
}

My intent here is to pass in the appropriate Curve as a Generic.. and it seems like it should work and I /think/ I'm actually doing this mostly right, but I keep getting the errors:

error[E0277]: the trait bound `T: elliptic_curve::Curve` is not satisfied
  --> src/main.rs:16:29
   |
16 |     pub fn sign(secret_key: SecretKey<T>, message: &[u8]) -> Signature<T> {
   |                             ^^^^^^^^^^^^ the trait `elliptic_curve::Curve` is not implemented for `T`
   |
note: required by a bound in `SecretKey`
  --> /home/xxx/.cargo/registry/src/github.com-1ecc6299db9ec823/elliptic-curve-0.12.3/src/secret_key.rs:84:25
   |
84 | pub struct SecretKey<C: Curve> {
   |                         ^^^^^ required by this bound in `SecretKey`
help: consider restricting type parameter `T`
   |
15 | impl<T: elliptic_curve::Curve> Signature<T> {
   |       +++++++++++++++++++++++

error[E0277]: the trait bound `T: ecdsa::PrimeCurve` is not satisfied
   --> src/main.rs:12:16
    |
12  |     signature: ecdsa::Signature<T>,
    |                ^^^^^^^^^^^^^^^^^^^ the trait `ecdsa::PrimeCurve` is not implemented for `T`
    |
note: required by a bound in `ecdsa::Signature`
   --> /home/xxx/.cargo/registry/src/github.com-1ecc6299db9ec823/ecdsa-0.14.4/src/lib.rs:132:25
    |
132 | pub struct Signature<C: PrimeCurve>
    |                         ^^^^^^^^^^ required by this bound in `ecdsa::Signature`
help: consider restricting type parameter `T`
    |
11  | pub struct Signature<T: ecdsa::PrimeCurve> {
    |                       +++++++++++++++++++

The errors thrown complain about traits which the curve NistP256 does, in fact, implement.

I can do this just fine:

#![allow(dead_code, unused_imports)]

use ecdsa::{signature::Signer, SigningKey};
use elliptic_curve::{PublicKey, SecretKey};
use k256::Secp256k1;
use p256::NistP256;
use p384::NistP384;
use rand_core::OsRng;

#[derive(Debug)]
pub struct Signature {
    signature: ecdsa::Signature<NistP256>,
}

impl Signature {
    pub fn sign(secret_key: SecretKey<NistP256>, message: &[u8]) -> Self {
        let signature = SigningKey::from(&secret_key).sign(&message);

        Self { signature }
    }
}

fn main() {
    let key: SecretKey<NistP256> = SecretKey::random(&mut OsRng);

    let sig: Signature = Signature::sign(key, "foo".as_bytes());

    dbg!(sig);
}

I don't want to have to create structs and impls for all the curves I intend to support and then figure out how to unify them into the final structure.

So either I'm getting something fundamentally wrong with generics conceptually or the implementation of the RustCrypto signatures crates are weird enough that they can't be generic'd.

Where am I doing this wrong?

Thanks to the Rustlang Forum, I eventually came up with the following:

Basically every Generic upstream code mentions, you have to also mention... Rust currently does not do implicit generic bounds.

#![allow(dead_code, unused_imports)]

use ecdsa::{
    hazmat::{DigestPrimitive, SignPrimitive},
    signature::{DigestSigner, Signer},
    PrimeCurve, SignatureSize, SigningKey,
};
use elliptic_curve::{
    generic_array::ArrayLength, Curve, ProjectiveArithmetic, ScalarArithmetic, SecretKey,
};
use k256::Secp256k1;
use p256::NistP256;
use p384::NistP384;
use rand_core::OsRng;

#[derive(Debug)]
pub struct Signature<T: Curve + PrimeCurve>
where
    SignatureSize<T>: ArrayLength<u8>,
{
    signature: ecdsa::Signature<T>,
}

impl<T> Signature<T>
where
    T: PrimeCurve,
    SignatureSize<T>: ArrayLength<u8>,
{
    pub fn sign(secret_key: SecretKey<T>, message: &[u8]) -> Signature<T>
    where
        T: PrimeCurve + ScalarArithmetic + ProjectiveArithmetic + DigestPrimitive,
        T::Scalar: SignPrimitive<T>,
        SigningKey<T>: DigestSigner<T::Digest, ecdsa::Signature<T>>,
    {
        let signing_key = SigningKey::from(&secret_key);
        let signature = signing_key.sign(&message);

        Self { signature }
    }
}

fn main() {
    let key: SecretKey<Secp256k1> = SecretKey::random(&mut OsRng);

    let sig: Signature<Secp256k1> = Signature::sign(key, "foo".as_bytes());

    dbg!(sig);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM