Repository: google/OpenSK
File: build.rs (Rust)

Code before:

    // Load the OpenSSL PEM ECC key
    let ecc_data = include_bytes!("crypto_data/opensk.key");
    let pkey = ec::EcKey::private_key_from_pem(ecc_data)
        .ok()
        .expect("Failed to load OpenSK private key file");

    // Check key validity
    pkey.check_key().unwrap();
    assert_eq!(pkey.group().curve_name(), Some(Nid::X9_62_PRIME256V1));

    // Private keys generated by OpenSSL have variable size but we only handle
    // constant size. Serialization is done in big endian so if the size is less
    // than 32 bytes, we need to prepend with null bytes.
    // If the size is 33 bytes, this means the serialized BigInt is negative.
    // Any other size is invalid.
    let priv_key_hex = pkey.private_key().to_hex_str().unwrap();
    let priv_key_vec = pkey.private_key().to_vec();
    let key_len = priv_key_vec.len();

    assert!(
        key_len >= 30,
        "Invalid private key (too small): {} ({:#?})",
        priv_key_hex,
        priv_key_vec,
    );
    assert!(
        key_len <= 33,
        "Invalid private key (too big): {} ({:#?})",
        priv_key_hex,
        priv_key_vec,
    );

    // Copy OpenSSL generated key to our vec, starting from the end
    let mut output_vec = [0u8; 32];
    let min_key_len = std::cmp::min(key_len, 32);
    output_vec[32usize.saturating_sub(min_key_len)..]
        .copy_from_slice(&priv_key_vec[key_len.saturating_sub(min_key_len)..]);

    // Create the raw private key out of the OpenSSL data
    let mut priv_key_bin_file = File::create(&priv_key_bin_path).unwrap();
    priv_key_bin_file.write_all(&output_vec).unwrap();

    // Convert the PEM certificate to DER and extract the serial for AAGUID
    let input_pem_cert = include_bytes!("crypto_data/opensk_cert.pem");
    let cert = x509::X509::from_pem(input_pem_cert)
        .ok()
        .expect("Failed to load OpenSK certificate");

    // Do some sanity check on the certificate
    assert!(cert

Review comment (bug):
There's no reason to ban keys with length < 30 bytes. As far as I understand it, these are just keys with first bytes equal to zero - which openssl serializes as a variable length array.

Rejecting keys with many zeros at the beginning would introduce bias in the generation of private keys, which would be wrong from a cryptographic perspective.

If that's indeed openssl's process, in principle the probability to observe a key of length <= N bytes is `256^(N-32)` - so if you generate enough keys you should observe one with `N = 29` bytes for example.

Code after:

    // Load the OpenSSL PEM ECC key
    let ecc_data = include_bytes!("crypto_data/opensk.key");
    let pkey = ec::EcKey::private_key_from_pem(ecc_data)
        .ok()
        .expect("Failed to load OpenSK private key file");

    // Check key validity
    pkey.check_key().unwrap();
    assert_eq!(pkey.group().curve_name(), Some(Nid::X9_62_PRIME256V1));

    // Private keys generated by OpenSSL have variable size but we only handle
    // constant size. Serialization is done in big endian so if the size is less
    // than 32 bytes, we need to prepend with null bytes.
    // If the size is 33 bytes, this means the serialized BigInt is negative.
    // Any other size is invalid.
    let priv_key_hex = pkey.private_key().to_hex_str().unwrap();
    let priv_key_vec = pkey.private_key().to_vec();
    let key_len = priv_key_vec.len();

    assert!(
        key_len <= 33,
        "Invalid private key (too big): {} ({:#?})",
        priv_key_hex,
        priv_key_vec,
    );

    // Copy OpenSSL generated key to our vec, starting from the end
    let mut output_vec = [0u8; 32];
    let min_key_len = std::cmp::min(key_len, 32);
    output_vec[32 - min_key_len..].copy_from_slice(&priv_key_vec[key_len - min_key_len..]);

    // Create the raw private key out of the OpenSSL data
    let mut priv_key_bin_file = File::create(&priv_key_bin_path).unwrap();
    priv_key_bin_file.write_all(&output_vec).unwrap();

    // Convert the PEM certificate to DER and extract the serial for AAGUID
    let input_pem_cert = include_bytes!("crypto_data/opensk_cert.pem");
    let cert = x509::X509::from_pem(input_pem_cert)
        .ok()
        .expect("Failed to load OpenSK certificate");

    // Do some sanity check on the certificate
    assert!(cert
        .public_key()
        .unwrap()
        .public_eq(&PKey::from_ec_key(pkey).unwrap()));
    let now = asn1::Asn1Time::days_from_now(0).unwrap();
    assert!(cert.not_after() > now);
    assert!(cert.not_before() <= now);