chromium/third_party/cloud_authenticator/processor/src/spki.rs

// 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);
    }
}