dcrypt_sign/ecdsa/
common.rs

1//! Common utilities for ECDSA implementations
2
3use dcrypt_api::{error::Error as ApiError, Result as ApiResult};
4
5/// ECDSA signature components (r, s)
6#[derive(Clone, Debug)]
7pub struct SignatureComponents {
8    pub r: Vec<u8>,
9    pub s: Vec<u8>,
10}
11
12impl SignatureComponents {
13    /// Serialize signature to DER format
14    pub fn to_der(&self) -> Vec<u8> {
15        // DER encoding: SEQUENCE { INTEGER r, INTEGER s }
16        let mut der = Vec::new();
17
18        // Add SEQUENCE tag
19        der.push(0x30);
20
21        // Calculate and add length (placeholder, will update)
22        let len_pos = der.len();
23        der.push(0x00);
24
25        // Encode r
26        der.push(0x02); // INTEGER tag
27        let r_bytes = self.encode_integer(&self.r);
28        der.push(r_bytes.len() as u8);
29        der.extend_from_slice(&r_bytes);
30
31        // Encode s
32        der.push(0x02); // INTEGER tag
33        let s_bytes = self.encode_integer(&self.s);
34        der.push(s_bytes.len() as u8);
35        der.extend_from_slice(&s_bytes);
36
37        // Update sequence length
38        let total_len = der.len() - len_pos - 1;
39        der[len_pos] = total_len as u8;
40
41        der
42    }
43
44    /// Parse signature from DER format
45    pub fn from_der(der: &[u8]) -> ApiResult<Self> {
46        if der.len() < 8 {
47            return Err(ApiError::InvalidSignature {
48                context: "ECDSA DER parsing",
49                #[cfg(feature = "std")]
50                message: "DER signature too short".to_string(),
51            });
52        }
53
54        // Check SEQUENCE tag
55        if der[0] != 0x30 {
56            return Err(ApiError::InvalidSignature {
57                context: "ECDSA DER parsing",
58                #[cfg(feature = "std")]
59                message: "Invalid DER SEQUENCE tag".to_string(),
60            });
61        }
62
63        let mut pos = 2; // Skip tag and length
64
65        // Parse r
66        if der[pos] != 0x02 {
67            return Err(ApiError::InvalidSignature {
68                context: "ECDSA DER parsing",
69                #[cfg(feature = "std")]
70                message: "Invalid DER INTEGER tag for r".to_string(),
71            });
72        }
73        pos += 1;
74        let r_len = der[pos] as usize;
75        pos += 1;
76        let r = der[pos..pos + r_len].to_vec();
77        pos += r_len;
78
79        // Parse s
80        if der[pos] != 0x02 {
81            return Err(ApiError::InvalidSignature {
82                context: "ECDSA DER parsing",
83                #[cfg(feature = "std")]
84                message: "Invalid DER INTEGER tag for s".to_string(),
85            });
86        }
87        pos += 1;
88        let s_len = der[pos] as usize;
89        pos += 1;
90        let s = der[pos..pos + s_len].to_vec();
91
92        Ok(SignatureComponents {
93            r: Self::decode_integer(&r),
94            s: Self::decode_integer(&s),
95        })
96    }
97
98    /// Encode integer for DER (add leading zero if high bit set)
99    fn encode_integer(&self, bytes: &[u8]) -> Vec<u8> {
100        if bytes.is_empty() || bytes[0] & 0x80 == 0 {
101            bytes.to_vec()
102        } else {
103            // Add leading zero byte
104            let mut result = vec![0x00];
105            result.extend_from_slice(bytes);
106            result
107        }
108    }
109
110    /// Decode integer from DER (remove leading zeros)
111    fn decode_integer(bytes: &[u8]) -> Vec<u8> {
112        let mut result = bytes.to_vec();
113        while result.len() > 1 && result[0] == 0x00 {
114            result.remove(0);
115        }
116        result
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_der_encoding() {
126        let sig = SignatureComponents {
127            r: vec![0x01, 0x23, 0x45, 0x67],
128            s: vec![0x89, 0xAB, 0xCD, 0xEF],
129        };
130
131        let der = sig.to_der();
132        let parsed = SignatureComponents::from_der(&der).unwrap();
133
134        assert_eq!(sig.r, parsed.r);
135        assert_eq!(sig.s, parsed.s);
136    }
137
138    #[test]
139    fn test_der_with_high_bit() {
140        // Test encoding when high bit is set (requires leading zero)
141        let sig = SignatureComponents {
142            r: vec![0xFF, 0x23, 0x45, 0x67],
143            s: vec![0x79, 0xAB, 0xCD, 0xEF],
144        };
145
146        let der = sig.to_der();
147
148        // Check that r has leading zero in DER
149        assert_eq!(der[3], 5); // r length should be 5 (extra zero byte)
150        assert_eq!(der[4], 0x00); // leading zero
151        assert_eq!(der[5], 0xFF); // original first byte
152
153        // Parse back and verify
154        let parsed = SignatureComponents::from_der(&der).unwrap();
155        assert_eq!(sig.r, parsed.r);
156        assert_eq!(sig.s, parsed.s);
157    }
158}