• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

namib-project / dcaf-rs / 11935120896

20 Nov 2024 02:11PM UTC coverage: 86.555% (+1.3%) from 85.242%
11935120896

Pull #27

github

web-flow
Merge d2b3d706b into 383248641
Pull Request #27: ci: update grcov to latest stable version

6116 of 7066 relevant lines covered (86.56%)

167.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.54
/src/token/cose/crypto_impl/rustcrypto/sign/ecdsa.rs
1
/*
2
 * Copyright (c) 2024 The NAMIB Project Developers.
3
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
 * option. This file may not be copied, modified, or distributed
7
 * except according to those terms.
8
 *
9
 * SPDX-License-Identifier: MIT OR Apache-2.0
10
 */
11
use core::ops::Add;
12

13
use coset::{iana, Algorithm};
14
use digest::const_oid::ObjectIdentifier;
15
use digest::Digest;
16
use ecdsa::elliptic_curve::generic_array::ArrayLength;
17
use ecdsa::hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive};
18
use ecdsa::signature::{DigestSigner, Verifier};
19
use ecdsa::{
20
    PrimeCurve, RecoveryId, Signature, SignatureWithOid, SigningKey, VerifyingKey,
21
    ECDSA_SHA256_OID, ECDSA_SHA384_OID, ECDSA_SHA512_OID,
22
};
23
use elliptic_curve::{
24
    sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint},
25
    CurveArithmetic, PublicKey, SecretKey,
26
};
27
use p256::NistP256;
28
use p384::NistP384;
29
use rand::{CryptoRng, RngCore};
30
use sha2::{Sha256, Sha384, Sha512};
31

32
use crate::error::CoseCipherError;
33
use crate::token::cose::crypto_impl::rustcrypto::CoseRustCryptoCipherError;
34
use crate::token::cose::crypto_impl::rustcrypto::RustCryptoContext;
35
use crate::token::cose::{CoseEc2Key, CryptoBackend, EllipticCurve};
36
use alloc::vec::Vec;
37

38
impl<RNG: RngCore + CryptoRng> RustCryptoContext<RNG> {
39
    /// Perform an ECDSA signature operation with the ECDSA variant given in `algorithm` for the
40
    /// given `payload` using the provided `key`.
41
    pub(super) fn sign_ecdsa(
25✔
42
        algorithm: iana::Algorithm,
25✔
43
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
25✔
44
        payload: &[u8],
25✔
45
    ) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
25✔
46
        match algorithm {
25✔
47
            iana::Algorithm::ES256 => Self::sign_ecdsa_with_digest::<Sha256>(key, payload),
21✔
48
            iana::Algorithm::ES384 => Self::sign_ecdsa_with_digest::<Sha384>(key, payload),
2✔
49
            iana::Algorithm::ES512 => Self::sign_ecdsa_with_digest::<Sha512>(key, payload),
2✔
50
            a => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
51
                a,
×
52
            ))),
×
53
        }
54
    }
25✔
55

56
    /// Perform an ECDSA verification operation with the ECDSA variant given in `algorithm` for the
57
    /// given `payload` and `sig`nature using the provided `key`.
58
    pub(super) fn verify_ecdsa(
37✔
59
        algorithm: iana::Algorithm,
37✔
60
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
37✔
61
        sig: &[u8],
37✔
62
        payload: &[u8],
37✔
63
    ) -> Result<(), CoseCipherError<<Self as CryptoBackend>::Error>> {
37✔
64
        let oid = match algorithm {
37✔
65
            iana::Algorithm::ES256 => ECDSA_SHA256_OID,
29✔
66
            iana::Algorithm::ES384 => ECDSA_SHA384_OID,
4✔
67
            iana::Algorithm::ES512 => ECDSA_SHA512_OID,
4✔
68
            a => {
×
69
                return Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
70
                    a,
×
71
                )))
×
72
            }
73
        };
74

75
        match &key.crv {
37✔
76
            EllipticCurve::Assigned(iana::EllipticCurve::P_256) => {
33✔
77
                Self::verify_ecdsa_with_curve::<NistP256>(key, oid, sig, payload)
33✔
78
            }
79
            EllipticCurve::Assigned(iana::EllipticCurve::P_384) => {
4✔
80
                Self::verify_ecdsa_with_curve::<NistP384>(key, oid, sig, payload)
4✔
81
            }
82
            // P-521 must implement DigestPrimitive in order to be usable in ECDSA, which was only
83
            // recently added and is not released yet (will come with p521 0.14.0).
84
            /*EllipticCurve::Assigned(iana::EllipticCurve::P_521) => {
85
                Self::ecdsa_sign_with_curve::<D, NistP521>(key, payload)
86
            }*/
87
            v => Err(CoseCipherError::UnsupportedCurve(v.clone())),
×
88
        }
89
    }
37✔
90

91
    /// Perform an ECDSA signature operation with the ECDSA hash function `D` for the
92
    /// given `payload` using the provided `key`.
93
    fn sign_ecdsa_with_digest<D: Digest>(
25✔
94
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
25✔
95
        payload: &[u8],
25✔
96
    ) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>> {
25✔
97
        match &key.crv {
25✔
98
            EllipticCurve::Assigned(iana::EllipticCurve::P_256) => {
23✔
99
                Self::sign_ecdsa_with_digest_and_curve::<D, NistP256>(key, payload)
23✔
100
            }
101
            EllipticCurve::Assigned(iana::EllipticCurve::P_384) => {
2✔
102
                Self::sign_ecdsa_with_digest_and_curve::<D, NistP384>(key, payload)
2✔
103
            }
104
            // P-521 must implement DigestPrimitive in order to be usable in ECDSA, which was only
105
            // recently added and is not released yet (will come with p521 0.14.0).
106
            /*EllipticCurve::Assigned(iana::EllipticCurve::P_521) => {
107
                Self::ecdsa_sign_with_curve::<D, NistP521>(key, payload)
108
            }*/
109
            v => Err(CoseCipherError::UnsupportedCurve(v.clone())),
×
110
        }
111
    }
25✔
112

113
    /// Perform an ECDSA signature operation with the ECDSA hash function `D` and curve `CRV` for
114
    /// the given `payload` using the provided `key`.
115
    fn sign_ecdsa_with_digest_and_curve<
25✔
116
        D: Digest,
25✔
117
        CRV: PrimeCurve + CurveArithmetic + DigestPrimitive,
25✔
118
    >(
25✔
119
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
25✔
120
        payload: &[u8],
25✔
121
    ) -> Result<Vec<u8>, CoseCipherError<<Self as CryptoBackend>::Error>>
25✔
122
    where
25✔
123
        <CRV as CurveArithmetic>::Scalar: SignPrimitive<CRV>,
25✔
124
        <<CRV as ecdsa::elliptic_curve::Curve>::FieldBytesSize as Add>::Output: ArrayLength<u8>,
25✔
125
    {
25✔
126
        let digest = Digest::new_with_prefix(payload);
25✔
127
        let sign_key = Self::cose_ec2_to_ec_private_key::<CRV>(key)?;
25✔
128
        let (signature, _recid) = <SigningKey<CRV> as DigestSigner<
25✔
129
            D,
25✔
130
            (Signature<CRV>, RecoveryId),
25✔
131
        >>::sign_digest(&sign_key, digest);
25✔
132
        Ok(signature.to_vec())
25✔
133
    }
25✔
134

135
    /// Perform an ECDSA verification operation with the ECDSA hash function given in `oid` for the
136
    /// given `payload` and `sig`nature using the provided `key`.
137
    fn verify_ecdsa_with_curve<CRV: PrimeCurve + CurveArithmetic + DigestPrimitive>(
37✔
138
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
37✔
139
        oid: ObjectIdentifier,
37✔
140
        sig: &[u8],
37✔
141
        payload: &[u8],
37✔
142
    ) -> Result<(), CoseCipherError<<Self as CryptoBackend>::Error>>
37✔
143
    where
37✔
144
        <CRV as CurveArithmetic>::AffinePoint: VerifyPrimitive<CRV>,
37✔
145
        <<CRV as ecdsa::elliptic_curve::Curve>::FieldBytesSize as Add>::Output: ArrayLength<u8>,
37✔
146
        <CRV as ecdsa::elliptic_curve::Curve>::FieldBytesSize: ModulusSize,
37✔
147
        <CRV as CurveArithmetic>::AffinePoint: FromEncodedPoint<CRV>,
37✔
148
        <CRV as CurveArithmetic>::AffinePoint: ToEncodedPoint<CRV>,
37✔
149
    {
37✔
150
        let sign_key = Self::cose_ec2_to_ec_public_key::<CRV>(key)?;
37✔
151
        let signature = SignatureWithOid::new(Signature::<CRV>::from_slice(sig)?, oid)?;
37✔
152
        <VerifyingKey<CRV> as Verifier<SignatureWithOid<CRV>>>::verify(
37✔
153
            &sign_key, payload, &signature,
37✔
154
        )
37✔
155
        .map_err(CoseCipherError::from)
37✔
156
    }
37✔
157

158
    /// Convert a public or private COSE EC2 key to its public key RustCrypto representation.
159
    fn cose_ec2_to_ec_public_key<CRV: PrimeCurve + CurveArithmetic>(
37✔
160
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
37✔
161
    ) -> Result<VerifyingKey<CRV>, CoseCipherError<<Self as CryptoBackend>::Error>>
37✔
162
    where
37✔
163
        <CRV as ecdsa::elliptic_curve::Curve>::FieldBytesSize: ModulusSize,
37✔
164
        <CRV as CurveArithmetic>::AffinePoint: FromEncodedPoint<CRV>,
37✔
165
        <CRV as CurveArithmetic>::AffinePoint: ToEncodedPoint<CRV>,
37✔
166
    {
37✔
167
        if key.x.is_none() || (key.y.is_none() && key.sign.is_none()) {
37✔
168
            // According to the contract provided by the calling COSE library, D must be set, so we
169
            // can attempt to reconstruct the public key from the private key.
170
            SecretKey::from_slice(key.d.expect(
×
171
                "invalid EC2 key was provided, at least one of the key parameters must be set",
×
172
            ))
×
173
            .map(|sc| VerifyingKey::from(sc.public_key()))
×
174
            .map_err(CoseCipherError::from)
×
175
        } else {
176
            // x must be Some here due to the previous condition.
177
            let pubkey_coord = if let Some(y) = key.y {
37✔
178
                EncodedPoint::<CRV>::from_affine_coordinates(key.x.unwrap().into(), y.into(), false)
37✔
179
            } else {
180
                EncodedPoint::<CRV>::from_affine_coordinates(
×
181
                    key.x.unwrap().into(),
×
182
                    u8::from(key.sign.unwrap()).to_be_bytes().as_slice().into(),
×
183
                    true,
×
184
                )
×
185
            };
186
            let pubkey = PublicKey::from_encoded_point(&pubkey_coord);
37✔
187
            if pubkey.is_some().into() {
37✔
188
                Ok(VerifyingKey::from(pubkey.unwrap()))
37✔
189
            } else {
190
                Err(CoseCipherError::Other(
×
191
                    CoseRustCryptoCipherError::InvalidPoint,
×
192
                ))
×
193
            }
194
        }
195
    }
37✔
196

197
    /// Convert a private COSE EC2 key to its RustCrypto representation.
198
    fn cose_ec2_to_ec_private_key<CRV: PrimeCurve + CurveArithmetic>(
25✔
199
        key: &CoseEc2Key<'_, <Self as CryptoBackend>::Error>,
25✔
200
    ) -> Result<SigningKey<CRV>, CoseCipherError<<Self as CryptoBackend>::Error>>
25✔
201
    where
25✔
202
        <CRV as CurveArithmetic>::Scalar: SignPrimitive<CRV>,
25✔
203
        <<CRV as ecdsa::elliptic_curve::Curve>::FieldBytesSize as Add>::Output: ArrayLength<u8>,
25✔
204
    {
25✔
205
        SecretKey::<CRV>::from_slice(
25✔
206
            key.d
25✔
207
                .expect("invalid EC2 private key was provided, key parameter d must be set"),
25✔
208
        )
25✔
209
        .map(SigningKey::<CRV>::from)
25✔
210
        .map_err(CoseCipherError::from)
25✔
211
    }
25✔
212
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc