mysql_common/crypto/
der.rs

1// Copyright (c) 2021 Anatoly Ikorsky
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use base64::{engine::general_purpose::STANDARD, Engine};
10use num_bigint::BigUint;
11use regex::bytes::Regex;
12use std::mem::size_of;
13
14/// Type of a der-encoded public key.
15#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
16pub enum PubKeyFileType {
17    Pkcs1,
18    Pkcs8,
19}
20
21/// Converts pem encoded RSA public key to der.
22pub 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
68/// der bytes -> (len, rest of der bytes)
69fn 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
78/// der bytes -> (sequence bytes, rest of der bytes)
79pub 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
87/// der bytes -> (unused bits, bytes of bit string, rest of der bytes)
88pub 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
97/// der bytes -> (uint, rest of der bytes)
98pub 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
107/// Extracts modulus and exponent from pkcs1 der public key representation
108pub 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
115/// Extracts modulus and exponent from pkcs8 der public key representation
116pub fn parse_pub_key_pkcs8(der: &[u8]) -> (BigUint, BigUint) {
117    let (seq_data, _) = parse_sequence(der);
118    // ignore algorithm
119    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
125/// Extracts modulus and exponent from specified der public key representation
126pub 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}