mysql_common/crypto/
der.rs
1use base64::{engine::general_purpose::STANDARD, Engine};
10use num_bigint::BigUint;
11use regex::bytes::Regex;
12use std::mem::size_of;
13
14#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
16pub enum PubKeyFileType {
17 Pkcs1,
18 Pkcs8,
19}
20
21pub fn pem_to_der(pem: impl AsRef<[u8]>) -> (Vec<u8>, PubKeyFileType) {
23 let pkcs1_re = Regex::new(
24 "-----BEGIN RSA PUBLIC KEY-----\
25 ([^-]*)\
26 -----END RSA PUBLIC KEY-----",
27 )
28 .unwrap();
29 let pkcs8_re = Regex::new(
30 "-----BEGIN PUBLIC KEY-----\
31 ([^-]*)\
32 -----END PUBLIC KEY-----",
33 )
34 .unwrap();
35
36 let (captures, key_file_type) = pkcs1_re
37 .captures(pem.as_ref())
38 .map(|captures| (captures, PubKeyFileType::Pkcs1))
39 .unwrap_or_else(|| {
40 pkcs8_re
41 .captures(pem.as_ref())
42 .map(|captures| (captures, PubKeyFileType::Pkcs8))
43 .expect("valid PEM is mandatory here")
44 });
45 let pem_body = captures.get(1).unwrap().as_bytes();
46 let pem_body = pem_body
47 .iter()
48 .filter(|x| !b" \n\t\r\x0b\x0c".contains(x))
49 .cloned()
50 .collect::<Vec<_>>();
51
52 let der = STANDARD
53 .decode(&*pem_body)
54 .expect("valid base64 is mandatory here");
55
56 (der, key_file_type)
57}
58
59fn big_uint_to_usize(x: BigUint) -> usize {
60 let mut y = 0;
61 let x_le = x.to_bytes_le();
62 for (i, b) in x_le.into_iter().enumerate().take(size_of::<usize>()) {
63 y += (b as usize) << (8 * i);
64 }
65 y
66}
67
68fn parse_len(der: &[u8]) -> (BigUint, &[u8]) {
70 if der[0] & 0x80 > 0 {
71 let len = (der[0] & (!0x80)) as usize;
72 (BigUint::from_bytes_be(&der[1..=len]), &der[len + 1..])
73 } else {
74 (BigUint::from(der[0]), &der[1..])
75 }
76}
77
78pub fn parse_sequence(mut der: &[u8]) -> (&[u8], &[u8]) {
80 assert_eq!(der[0], 0x30, "expecting SEQUENCE in primitive encoding");
81 der = &der[1..];
82 let (sequence_len, der) = parse_len(der);
83 let sequence_len = big_uint_to_usize(sequence_len);
84 (&der[..sequence_len], &der[sequence_len..])
85}
86
87pub fn parse_bit_string(mut der: &[u8]) -> (u8, &[u8], &[u8]) {
89 assert_eq!(der[0], 0x03, "expecting BIT STRING in primitive encoding");
90 der = &der[1..];
91 let (bit_string_len, der) = parse_len(der);
92 let bit_string_len = big_uint_to_usize(bit_string_len);
93 let unused_bits = der[0];
94 (unused_bits, &der[1..bit_string_len], &der[bit_string_len..])
95}
96
97pub fn parse_uint(mut der: &[u8]) -> (BigUint, &[u8]) {
99 assert_eq!(der[0], 0x02, "expecting INTEGER");
100 der = &der[1..];
101 let (uint_len, der) = parse_len(der);
102 let uint_len = big_uint_to_usize(uint_len);
103 let out = BigUint::from_bytes_be(&der[..uint_len]);
104 (out, &der[uint_len..])
105}
106
107pub fn parse_pub_key_pkcs1(der: &[u8]) -> (BigUint, BigUint) {
109 let (pub_key_fields, _) = parse_sequence(der);
110 let (modulus, pub_key_fields) = parse_uint(pub_key_fields);
111 let (exponent, _) = parse_uint(pub_key_fields);
112 (modulus, exponent)
113}
114
115pub fn parse_pub_key_pkcs8(der: &[u8]) -> (BigUint, BigUint) {
117 let (seq_data, _) = parse_sequence(der);
118 let (_, der) = parse_sequence(seq_data);
120 let (unused_bits, pub_key, _) = parse_bit_string(der);
121 assert_eq!(unused_bits, 0, "expecting no unused bits");
122 parse_pub_key_pkcs1(pub_key)
123}
124
125pub fn parse_pub_key(der: &[u8], file_type: PubKeyFileType) -> (BigUint, BigUint) {
127 match file_type {
128 PubKeyFileType::Pkcs1 => parse_pub_key_pkcs1(der),
129 PubKeyFileType::Pkcs8 => parse_pub_key_pkcs8(der),
130 }
131}
132
133#[test]
134fn test_pem_to_der() {
135 const PEM_DATA: &[u8] = br"-----BEGIN PUBLIC KEY-----
136MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxSKOcxiet8lLMn8ImyUE
137bGGKob5EdRz/4wdiw12ED0GfKKTKhVnodFCfm1mdy7bKOX5QxL9skrvYodpW43eR
138R5bfOzIgy1qIB8RYb6qOXRBw1oA4snBDqtUjDv/lbHLJN+IbzM4oU+e3Lt9rXyLX
139VY289ewONPweXHqSCnTL91w+wkU1peIFV2QhZ+upUCdCtwOn5hnJPNgxtbklFoya
140C8W3Z7Xx7He2QDJsEWAqX197efw0L6j8X8Tyd8Uwb7zUB1tfMGhHfm9EwejPAtzx
1414GztQNtNMtGS2oGZLQBLV9hib4dDL92iiZeckg2LAf4GsJofLLR8mcHCRoqVbQJ1
142YQIDAQAB
143-----END PUBLIC KEY-----";
144
145 let (_der, key_type) = pem_to_der(PEM_DATA);
146
147 assert_eq!(key_type, PubKeyFileType::Pkcs8);
148}