Skip to main content

gmssl_rs/sm9/
mod.rs

1// SM9 identity-based cryptography.
2//
3// SM9 (GM/T 0044-2016) supports identity-based signatures (IBS) and
4// identity-based encryption (IBE) using bilinear pairings on BN curves.
5
6use std::mem::MaybeUninit;
7use gmssl_rs_sys;
8
9use crate::error::{ok_or_library_error, verify_result, GmsslError};
10use crate::pem_helpers;
11
12// ============================================================================
13// SM9 Sign Master Key (held by KGC)
14// ============================================================================
15
16pub struct Sm9SignMasterKey {
17    key: gmssl_rs_sys::SM9_SIGN_MASTER_KEY,
18}
19
20impl std::fmt::Debug for Sm9SignMasterKey {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_struct("Sm9SignMasterKey").finish()
23    }
24}
25
26impl Sm9SignMasterKey {
27    /// Generate a new SM9 signature master key.
28    pub fn generate() -> Result<Self, GmsslError> {
29        let mut key = MaybeUninit::uninit();
30        ok_or_library_error(
31            unsafe { gmssl_rs_sys::sm9_sign_master_key_generate(key.as_mut_ptr()) },
32            "sm9_sign_master_key_generate",
33        )?;
34        Ok(Sm9SignMasterKey {
35            key: unsafe { key.assume_init() },
36        })
37    }
38
39    /// Extract a user's signing private key from the master key.
40    pub fn extract_key(&self, id: &str) -> Result<Sm9SignKey, GmsslError> {
41        let id_c = std::ffi::CString::new(id)
42            .map_err(|_| GmsslError::InvalidInput("ID contains NUL byte"))?;
43        let mut key = MaybeUninit::uninit();
44        ok_or_library_error(
45            unsafe {
46                gmssl_rs_sys::sm9_sign_master_key_extract_key(
47                    &self.key,
48                    id_c.as_ptr(),
49                    id.len(),
50                    key.as_mut_ptr(),
51                )
52            },
53            "sm9_sign_master_key_extract_key",
54        )?;
55        Ok(Sm9SignKey {
56            key: unsafe { key.assume_init() },
57            id: id.to_string(),
58        })
59    }
60
61    /// Import from encrypted PEM (in-memory).
62    pub fn from_encrypted_pem(pem_data: &[u8], password: &str) -> Result<Self, GmsslError> {
63        let pass_c = std::ffi::CString::new(password)
64            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
65        let mut key = MaybeUninit::uninit();
66        let fp = unsafe { pem_helpers::file_from_bytes(pem_data)? };
67        let ret = unsafe {
68            gmssl_rs_sys::sm9_sign_master_key_info_decrypt_from_pem(
69                key.as_mut_ptr(),
70                pass_c.as_ptr(),
71                fp,
72            )
73        };
74        unsafe { libc::fclose(fp) };
75        ok_or_library_error(ret, "sm9_sign_master_key_info_decrypt_from_pem")?;
76        Ok(Sm9SignMasterKey {
77            key: unsafe { key.assume_init() },
78        })
79    }
80
81    /// Export to encrypted PEM (in-memory).
82    pub fn to_encrypted_pem(&self, password: &str) -> Result<Vec<u8>, GmsslError> {
83        let pass_c = std::ffi::CString::new(password)
84            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
85        unsafe {
86            pem_helpers::collect_to_bytes(|fp| {
87                gmssl_rs_sys::sm9_sign_master_key_info_encrypt_to_pem(
88                    &self.key,
89                    pass_c.as_ptr(),
90                    fp,
91                )
92            })
93        }
94    }
95}
96
97// ============================================================================
98// SM9 Sign User Key
99// ============================================================================
100
101pub struct Sm9SignKey {
102    key: gmssl_rs_sys::SM9_SIGN_KEY,
103    id: String,
104}
105
106impl std::fmt::Debug for Sm9SignKey {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        f.debug_struct("Sm9SignKey").field("id", &self.id).finish()
109    }
110}
111
112impl Sm9SignKey {
113    /// Get the user identity associated with this key.
114    pub fn id(&self) -> &str {
115        &self.id
116    }
117
118    /// Import from encrypted PEM (in-memory).
119    pub fn from_encrypted_pem(pem_data: &[u8], password: &str) -> Result<Self, GmsslError> {
120        let pass_c = std::ffi::CString::new(password)
121            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
122        let mut key = MaybeUninit::uninit();
123        let fp = unsafe { pem_helpers::file_from_bytes(pem_data)? };
124        let ret = unsafe {
125            gmssl_rs_sys::sm9_sign_key_info_decrypt_from_pem(
126                key.as_mut_ptr(),
127                pass_c.as_ptr(),
128                fp,
129            )
130        };
131        unsafe { libc::fclose(fp) };
132        ok_or_library_error(ret, "sm9_sign_key_info_decrypt_from_pem")?;
133        Ok(Sm9SignKey {
134            key: unsafe { key.assume_init() },
135            id: String::new(), // PEM doesn't encode the ID
136        })
137    }
138
139    /// Export to encrypted PEM (in-memory).
140    pub fn to_encrypted_pem(&self, password: &str) -> Result<Vec<u8>, GmsslError> {
141        let pass_c = std::ffi::CString::new(password)
142            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
143        unsafe {
144            pem_helpers::collect_to_bytes(|fp| {
145                gmssl_rs_sys::sm9_sign_key_info_encrypt_to_pem(
146                    &self.key,
147                    pass_c.as_ptr(),
148                    fp,
149                )
150            })
151        }
152    }
153}
154
155// ============================================================================
156// SM9 Sign/Verify
157// ============================================================================
158
159/// Sign a message using an SM9 signing key.
160pub fn sm9_sign(key: &Sm9SignKey, data: &[u8]) -> Result<Vec<u8>, GmsslError> {
161    let mut ctx = MaybeUninit::uninit();
162    ok_or_library_error(
163        unsafe { gmssl_rs_sys::sm9_sign_init(ctx.as_mut_ptr()) },
164        "sm9_sign_init",
165    )?;
166    ok_or_library_error(
167        unsafe { gmssl_rs_sys::sm9_sign_update(ctx.as_mut_ptr(), data.as_ptr(), data.len()) },
168        "sm9_sign_update",
169    )?;
170
171    let mut sig = vec![0u8; 256];
172    let mut siglen: usize = sig.len();
173    ok_or_library_error(
174        unsafe {
175            gmssl_rs_sys::sm9_sign_finish(
176                ctx.as_mut_ptr(),
177                &key.key,
178                sig.as_mut_ptr(),
179                &mut siglen,
180            )
181        },
182        "sm9_sign_finish",
183    )?;
184    // GmSSL sets *siglen = 0 before DER encoding, causing size_t wrap.
185    // Recover actual size from ASN.1 SEQUENCE header.
186    truncate_der_sequence(&mut sig);
187    Ok(sig)
188}
189
190/// Verify an SM9 signature.
191pub fn sm9_verify(
192    mpk: &Sm9SignMasterKey,
193    id: &str,
194    data: &[u8],
195    sig: &[u8],
196) -> Result<bool, GmsslError> {
197    let id_c = std::ffi::CString::new(id)
198        .map_err(|_| GmsslError::InvalidInput("ID contains NUL byte"))?;
199
200    let mut ctx = MaybeUninit::uninit();
201    ok_or_library_error(
202        unsafe { gmssl_rs_sys::sm9_verify_init(ctx.as_mut_ptr()) },
203        "sm9_verify_init",
204    )?;
205    ok_or_library_error(
206        unsafe { gmssl_rs_sys::sm9_verify_update(ctx.as_mut_ptr(), data.as_ptr(), data.len()) },
207        "sm9_verify_update",
208    )?;
209
210    verify_result(
211        unsafe {
212            gmssl_rs_sys::sm9_verify_finish(
213                ctx.as_mut_ptr(),
214                sig.as_ptr(),
215                sig.len(),
216                &mpk.key,
217                id_c.as_ptr(),
218                id.len(),
219            )
220        },
221        "sm9_verify_finish",
222    )
223}
224
225// ============================================================================
226// SM9 Enc Master Key (held by KGC)
227// ============================================================================
228
229pub struct Sm9EncMasterKey {
230    key: gmssl_rs_sys::SM9_ENC_MASTER_KEY,
231}
232
233impl std::fmt::Debug for Sm9EncMasterKey {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        f.debug_struct("Sm9EncMasterKey").finish()
236    }
237}
238
239impl Sm9EncMasterKey {
240    /// Generate a new SM9 encryption master key.
241    pub fn generate() -> Result<Self, GmsslError> {
242        let mut key = MaybeUninit::uninit();
243        ok_or_library_error(
244            unsafe { gmssl_rs_sys::sm9_enc_master_key_generate(key.as_mut_ptr()) },
245            "sm9_enc_master_key_generate",
246        )?;
247        Ok(Sm9EncMasterKey {
248            key: unsafe { key.assume_init() },
249        })
250    }
251
252    /// Extract a user's decryption private key from the master key.
253    pub fn extract_key(&self, id: &str) -> Result<Sm9EncKey, GmsslError> {
254        let id_c = std::ffi::CString::new(id)
255            .map_err(|_| GmsslError::InvalidInput("ID contains NUL byte"))?;
256        let mut key = MaybeUninit::uninit();
257        ok_or_library_error(
258            unsafe {
259                gmssl_rs_sys::sm9_enc_master_key_extract_key(
260                    &self.key,
261                    id_c.as_ptr(),
262                    id.len(),
263                    key.as_mut_ptr(),
264                )
265            },
266            "sm9_enc_master_key_extract_key",
267        )?;
268        Ok(Sm9EncKey {
269            key: unsafe { key.assume_init() },
270            id: id.to_string(),
271        })
272    }
273
274    /// Import from encrypted PEM (in-memory).
275    pub fn from_encrypted_pem(pem_data: &[u8], password: &str) -> Result<Self, GmsslError> {
276        let pass_c = std::ffi::CString::new(password)
277            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
278        let mut key = MaybeUninit::uninit();
279        let fp = unsafe { pem_helpers::file_from_bytes(pem_data)? };
280        let ret = unsafe {
281            gmssl_rs_sys::sm9_enc_master_key_info_decrypt_from_pem(
282                key.as_mut_ptr(),
283                pass_c.as_ptr(),
284                fp,
285            )
286        };
287        unsafe { libc::fclose(fp) };
288        ok_or_library_error(ret, "sm9_enc_master_key_info_decrypt_from_pem")?;
289        Ok(Sm9EncMasterKey {
290            key: unsafe { key.assume_init() },
291        })
292    }
293
294    /// Export to encrypted PEM (in-memory).
295    pub fn to_encrypted_pem(&self, password: &str) -> Result<Vec<u8>, GmsslError> {
296        let pass_c = std::ffi::CString::new(password)
297            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
298        unsafe {
299            pem_helpers::collect_to_bytes(|fp| {
300                gmssl_rs_sys::sm9_enc_master_key_info_encrypt_to_pem(
301                    &self.key,
302                    pass_c.as_ptr(),
303                    fp,
304                )
305            })
306        }
307    }
308}
309
310// ============================================================================
311// SM9 Enc User Key
312// ============================================================================
313
314pub struct Sm9EncKey {
315    key: gmssl_rs_sys::SM9_ENC_KEY,
316    id: String,
317}
318
319impl std::fmt::Debug for Sm9EncKey {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        f.debug_struct("Sm9EncKey").field("id", &self.id).finish()
322    }
323}
324
325impl Sm9EncKey {
326    /// Get the user identity associated with this key.
327    pub fn id(&self) -> &str {
328        &self.id
329    }
330
331    /// Import from encrypted PEM (in-memory).
332    pub fn from_encrypted_pem(pem_data: &[u8], password: &str) -> Result<Self, GmsslError> {
333        let pass_c = std::ffi::CString::new(password)
334            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
335        let mut key = MaybeUninit::uninit();
336        let fp = unsafe { pem_helpers::file_from_bytes(pem_data)? };
337        let ret = unsafe {
338            gmssl_rs_sys::sm9_enc_key_info_decrypt_from_pem(
339                key.as_mut_ptr(),
340                pass_c.as_ptr(),
341                fp,
342            )
343        };
344        unsafe { libc::fclose(fp) };
345        ok_or_library_error(ret, "sm9_enc_key_info_decrypt_from_pem")?;
346        Ok(Sm9EncKey {
347            key: unsafe { key.assume_init() },
348            id: String::new(),
349        })
350    }
351
352    /// Export to encrypted PEM (in-memory).
353    pub fn to_encrypted_pem(&self, password: &str) -> Result<Vec<u8>, GmsslError> {
354        let pass_c = std::ffi::CString::new(password)
355            .map_err(|_| GmsslError::InvalidInput("password contains NUL byte"))?;
356        unsafe {
357            pem_helpers::collect_to_bytes(|fp| {
358                gmssl_rs_sys::sm9_enc_key_info_encrypt_to_pem(
359                    &self.key,
360                    pass_c.as_ptr(),
361                    fp,
362                )
363            })
364        }
365    }
366}
367
368// ============================================================================
369// SM9 Encrypt/Decrypt
370// ============================================================================
371
372/// Encrypt data for a recipient identified by `id` using the master public key.
373pub fn sm9_encrypt(
374    mpk: &Sm9EncMasterKey,
375    id: &str,
376    data: &[u8],
377) -> Result<Vec<u8>, GmsslError> {
378    if data.len() > gmssl_rs_sys::SM9_MAX_PLAINTEXT_SIZE {
379        return Err(GmsslError::InvalidInput(
380            "SM9 plaintext exceeds 255 bytes maximum",
381        ));
382    }
383    let id_c = std::ffi::CString::new(id)
384        .map_err(|_| GmsslError::InvalidInput("ID contains NUL byte"))?;
385
386    let mut out = vec![0u8; gmssl_rs_sys::SM9_MAX_CIPHERTEXT_SIZE];
387    let mut outlen: usize = out.len();
388    ok_or_library_error(
389        unsafe {
390            gmssl_rs_sys::sm9_encrypt(
391                &mpk.key,
392                id_c.as_ptr(),
393                id.len(),
394                data.as_ptr(),
395                data.len(),
396                out.as_mut_ptr(),
397                &mut outlen,
398            )
399        },
400        "sm9_encrypt",
401    )?;
402    // GmSSL sets *outlen = 0 before DER encoding, causing size_t wrap.
403    // Recover actual size from ASN.1 SEQUENCE header.
404    truncate_der_sequence(&mut out);
405    Ok(out)
406}
407
408/// Decrypt data using the user's private key.
409pub fn sm9_decrypt(key: &Sm9EncKey, id: &str, ciphertext: &[u8]) -> Result<Vec<u8>, GmsslError> {
410    let id_c = std::ffi::CString::new(id)
411        .map_err(|_| GmsslError::InvalidInput("ID contains NUL byte"))?;
412
413    let mut out = vec![0u8; ciphertext.len()];
414    let mut outlen: usize = out.len();
415    ok_or_library_error(
416        unsafe {
417            gmssl_rs_sys::sm9_decrypt(
418                &key.key,
419                id_c.as_ptr(),
420                id.len(),
421                ciphertext.as_ptr(),
422                ciphertext.len(),
423                out.as_mut_ptr(),
424                &mut outlen,
425            )
426        },
427        "sm9_decrypt",
428    )?;
429    out.truncate(outlen);
430    Ok(out)
431}
432
433/// Truncate a Vec containing a DER SEQUENCE to its actual encoded size.
434fn truncate_der_sequence(data: &mut Vec<u8>) {
435    if data.len() >= 2 && data[0] == 0x30 {
436        let content_len = data[1] as usize;
437        let total = if content_len < 0x80 {
438            2 + content_len
439        } else if content_len == 0x81 && data.len() >= 3 {
440            2 + 1 + data[2] as usize
441        } else if content_len == 0x82 && data.len() >= 4 {
442            let l = u16::from_be_bytes([data[2], data[3]]) as usize;
443            2 + 2 + l
444        } else {
445            return;
446        };
447        if total <= data.len() {
448            data.truncate(total);
449        }
450    }
451}
452
453#[cfg(test)]
454mod tests;