| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! spki implements minimal parsing of SubjectPublicKeyInfo structures. |
| |
| use crate::der; |
| |
| #[derive(Debug, PartialEq)] |
| #[allow(clippy::upper_case_acronyms)] |
| pub enum PublicKeyType { |
| P256, |
| RSA, |
| } |
| |
| /// Parses a SubjectPublicKeyInfo, returning the type of the key and the public |
| /// key itself in a type-specific format. |
| pub fn parse(input: &[u8]) -> Option<(PublicKeyType, &[u8])> { |
| const EC_KEY: &[u8] = b"\x2A\x86\x48\xCE\x3D\x02\x01"; |
| const RSA: &[u8] = b"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"; |
| const P256: &[u8] = b"\x2A\x86\x48\xCE\x3D\x03\x01\x07"; |
| |
| // See https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 |
| // |
| // SubjectPublicKeyInfo ::= SEQUENCE { |
| // algorithm AlgorithmIdentifier, |
| // subjectPublicKey BIT STRING |
| // } |
| |
| let (top_level, input) = der::next_tagged(input, der::SEQUENCE)?; |
| if !input.is_empty() { |
| return None; |
| } |
| |
| let (algo_id, top_level) = der::next_tagged(top_level, der::SEQUENCE)?; |
| |
| // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.1.2 |
| // |
| // AlgorithmIdentifier ::= SEQUENCE { |
| // algorithm OBJECT IDENTIFIER, |
| // parameters ANY DEFINED BY algorithm OPTIONAL |
| // } |
| |
| let (oid, algo_id) = der::next_tagged(algo_id, der::OBJECT_IDENTIFIER)?; |
| let key_type = if oid == EC_KEY { |
| let (curve, algo_id) = der::next_tagged(algo_id, der::OBJECT_IDENTIFIER)?; |
| if curve != P256 || !algo_id.is_empty() { |
| return None; |
| } |
| PublicKeyType::P256 |
| } else if oid == RSA { |
| let (none_body, algo_id) = der::next_tagged(algo_id, der::NONE)?; |
| if !none_body.is_empty() || !algo_id.is_empty() { |
| return None; |
| } |
| PublicKeyType::RSA |
| } else { |
| return None; |
| }; |
| |
| let (pubkey, top_level) = der::next_tagged(top_level, der::BIT_STRING)?; |
| if pubkey.is_empty() || !top_level.is_empty() { |
| return None; |
| } |
| |
| // The first byte of a BIT STRING is the number of unused bits. This is |
| // always zero for the key types that we deal with. |
| let (unused_bits, pubkey) = der::u8_next(pubkey)?; |
| if unused_bits != 0 { |
| return None; |
| } |
| |
| Some((key_type, pubkey)) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_parse_p256() { |
| const P256_SPKI : &[u8] = b"\x30\x59\x30\x13\x06\x07\x2A\x86\x48\xCE\x3D\x02\x01\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07\x03\x42\x00\x04\xC0\xA2\x04\xCF\xE4\x2C\x4B\xA8\x75\xC5\x2D\x8F\x99\x6B\xEB\xA3\x1B\x80\x6E\x4B\x39\xEE\xA2\x30\x37\x34\x4D\x08\xB2\xA8\xF9\xCC\xD8\xED\x65\xC4\xC0\x08\x42\x83\x2D\xE3\x18\x7A\x7C\x4C\xCE\xF2\xFA\xE1\xF6\x1C\x09\x6C\x50\xFE\xC9\xA9\x21\x64\x43\x96\x2B\x65"; |
| |
| let Some((p256_type, p256_key)) = parse(P256_SPKI) else { |
| panic!("failed to parse P-256 SPKI"); |
| }; |
| assert_eq!(p256_type, PublicKeyType::P256); |
| assert_eq!(p256_key.len(), 65); |
| assert_eq!(p256_key[0], 0x04); |
| } |
| |
| #[test] |
| fn test_parse_rsa() { |
| const RSA_SPKI : &[u8] = b"\x30\x82\x01\x22\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x82\x01\x0F\x00\x30\x82\x01\x0A\x02\x82\x01\x01\x00\xC8\xCE\xF7\xBC\x8E\x91\xDB\x52\xC0\x49\xD5\x87\xAA\xB2\x2E\x4E\x04\xAC\xA1\xB7\xA3\x88\x8D\x18\xC8\x5D\x89\x74\x3F\xA8\x7C\x8B\xD2\xBA\xB8\xC3\x84\x6A\x36\x52\xFC\x3A\x47\x8A\x01\xD5\x2E\xEE\xA1\x9D\xFB\x62\xCE\xEA\x97\x65\x9E\xF8\xAF\xED\x94\xF0\x56\x6E\xB6\x43\xBB\xFA\x3B\x05\xFF\x1D\xE7\x9E\x68\xAF\x90\x25\x7C\x18\x83\x1B\xE3\x5E\x7E\x48\x8A\x50\xF5\xAD\xC1\x8F\x2B\x46\xDE\x2E\xE9\x88\x3B\x4E\x1C\xC1\x8F\xF1\xB5\x3E\x34\x8F\xD0\xCA\x13\x01\x39\x34\xB8\x16\x4F\x68\xB9\xF4\xED\xBF\x6F\xE8\x3A\xFE\x26\xE8\x7F\xB8\x22\x90\xC6\xF1\x30\xF5\x4D\x6E\x73\xDA\xA3\x3D\x8C\xC6\xA3\x41\x58\xDB\xA4\x5C\x5D\xEE\x1C\x28\xC5\x63\xF5\x08\xC4\x58\x26\x49\xA8\x7D\x11\x98\x06\x71\xB2\x9C\x65\xB8\x13\x2A\x93\xE1\x1A\xB9\x03\x86\x7E\xE1\x9E\x9A\xF5\xC0\x15\xB1\x20\x73\x12\x0E\x85\x0A\x46\x43\x4F\x2B\x92\xBE\xEF\xF1\xB2\x49\xB1\xF6\xE4\xF5\x26\x9A\x48\x6E\x41\x9F\x84\xA3\xD9\x45\xD1\x4F\x28\xE2\xEC\xD2\xFC\x76\x06\x45\x18\xE8\xBB\xC0\x99\x3F\x75\x49\x61\x0F\x94\x46\xB6\x80\x36\x5C\x69\xD4\x82\xAA\x77\xB3\xB3\x00\xE4\x60\xAB\x47\x02\x03\x01\x00\x01"; |
| |
| let Some((rsa_type, rsa_key)) = parse(RSA_SPKI) else { |
| panic!("failed to parse P-256 SPKI"); |
| }; |
| assert_eq!(rsa_type, PublicKeyType::RSA); |
| assert_eq!(rsa_key.len(), 270); |
| assert_eq!(rsa_key[0], 0x30); |
| } |
| } |