|
| 1 | +use anyhow::Context as _; |
| 2 | +use deno_core::ToJsBuffer; |
| 3 | +use ring::{ |
| 4 | + rand::SecureRandom, |
| 5 | + signature::{ |
| 6 | + EcdsaKeyPair, |
| 7 | + Ed25519KeyPair, |
| 8 | + KeyPair, |
| 9 | + }, |
| 10 | +}; |
| 11 | +use rsa::pkcs1::{ |
| 12 | + EncodeRsaPrivateKey, |
| 13 | + EncodeRsaPublicKey, |
| 14 | +}; |
| 15 | + |
| 16 | +use super::{ |
| 17 | + shared::RustRawKeyData, |
| 18 | + Algorithm, |
| 19 | + CryptoOps, |
| 20 | + Curve25519Algorithm, |
| 21 | + GenerateKeypairAlgorithm, |
| 22 | + GeneratedKey, |
| 23 | + GeneratedKeypair, |
| 24 | +}; |
| 25 | +use crate::environment::crypto_rng::CryptoRng; |
| 26 | + |
| 27 | +impl CryptoOps { |
| 28 | + pub fn generate_keypair( |
| 29 | + rng: CryptoRng, |
| 30 | + algorithm: GenerateKeypairAlgorithm, |
| 31 | + ) -> anyhow::Result<GeneratedKeypair> { |
| 32 | + match algorithm { |
| 33 | + GenerateKeypairAlgorithm::Rsa { |
| 34 | + name: Algorithm::RsaPss | Algorithm::RsaOaep | Algorithm::RsassaPkcs1v15, |
| 35 | + modulus_length, |
| 36 | + public_exponent, |
| 37 | + } => { |
| 38 | + let exp = rsa::BigUint::from_bytes_be(&public_exponent); |
| 39 | + let private_key = |
| 40 | + rsa::RsaPrivateKey::new_with_exp(&mut rng.rsa(), modulus_length, &exp)?; |
| 41 | + let public_key = private_key.to_public_key(); |
| 42 | + Ok(GeneratedKeypair { |
| 43 | + private_raw_data: GeneratedKey::KeyData(RustRawKeyData::Private( |
| 44 | + private_key.to_pkcs1_der()?.as_bytes().to_vec().into(), |
| 45 | + )), |
| 46 | + public_raw_data: GeneratedKey::KeyData(RustRawKeyData::Public( |
| 47 | + public_key.to_pkcs1_der()?.into_vec().into(), |
| 48 | + )), |
| 49 | + }) |
| 50 | + }, |
| 51 | + GenerateKeypairAlgorithm::Ec { |
| 52 | + name: Algorithm::Ecdsa | Algorithm::Ecdh, |
| 53 | + named_curve, |
| 54 | + } => { |
| 55 | + let private_key_pkcs8 = |
| 56 | + EcdsaKeyPair::generate_pkcs8(named_curve.into(), &rng.ring()) |
| 57 | + .ok() |
| 58 | + .context("failed to generate ecdsa keypair")?; |
| 59 | + let keypair = EcdsaKeyPair::from_pkcs8( |
| 60 | + named_curve.into(), |
| 61 | + private_key_pkcs8.as_ref(), |
| 62 | + &rng.ring(), |
| 63 | + ) |
| 64 | + .ok() |
| 65 | + .context("failed to parse ecdsa pkcs8 that we just generated")?; |
| 66 | + Ok(GeneratedKeypair { |
| 67 | + private_raw_data: GeneratedKey::KeyData(RustRawKeyData::Private( |
| 68 | + // private key is PKCS#8-encoded |
| 69 | + private_key_pkcs8.as_ref().to_vec().into(), |
| 70 | + )), |
| 71 | + public_raw_data: GeneratedKey::KeyData(RustRawKeyData::Public( |
| 72 | + // public key is just the elliptic curve point |
| 73 | + keypair.public_key().as_ref().to_vec().into(), |
| 74 | + )), |
| 75 | + }) |
| 76 | + }, |
| 77 | + GenerateKeypairAlgorithm::Curve25519 { |
| 78 | + name: Curve25519Algorithm::Ed25519, |
| 79 | + } => { |
| 80 | + let pkcs8_keypair = Ed25519KeyPair::generate_pkcs8(&rng.ring()) |
| 81 | + .ok() |
| 82 | + .context("failed to generate ed25519 key")?; |
| 83 | + // ring is really annoying and needs to jump through hoops to get the public key |
| 84 | + // that we just generated |
| 85 | + let public_key = Ed25519KeyPair::from_pkcs8(pkcs8_keypair.as_ref()) |
| 86 | + .ok() |
| 87 | + .context("failed to parse ed25519 pkcs8 that we just generated")? |
| 88 | + .public_key() |
| 89 | + .as_ref() |
| 90 | + .to_vec(); |
| 91 | + // ring is really really annoying and doesn't export the raw |
| 92 | + // seed at all, so use RustCrypto instead |
| 93 | + let private_key = Self::import_pkcs8_ed25519(pkcs8_keypair.as_ref()) |
| 94 | + .context("failed to import ed25519 pkcs8 that we just generated")?; |
| 95 | + Ok(GeneratedKeypair { |
| 96 | + private_raw_data: GeneratedKey::RawBytes(private_key), |
| 97 | + public_raw_data: GeneratedKey::RawBytes(public_key.into()), |
| 98 | + }) |
| 99 | + }, |
| 100 | + GenerateKeypairAlgorithm::Curve25519 { |
| 101 | + name: Curve25519Algorithm::X25519, |
| 102 | + } => { |
| 103 | + // ring refuses to generate exportable X25519 keys |
| 104 | + // (this should be unreachable as X25519 is rejected in the UDF runtime as well) |
| 105 | + anyhow::bail!("TODO: not yet supported"); |
| 106 | + }, |
| 107 | + _ => anyhow::bail!("invalid algorithm"), |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + pub fn generate_key_bytes(rng: CryptoRng, length: usize) -> anyhow::Result<ToJsBuffer> { |
| 112 | + anyhow::ensure!(length <= 1024, "key too long"); |
| 113 | + let mut buf = vec![0; length]; |
| 114 | + rng.ring() |
| 115 | + .fill(&mut buf) |
| 116 | + .ok() |
| 117 | + .context("failed to generate random bytes")?; |
| 118 | + Ok(buf.into()) |
| 119 | + } |
| 120 | +} |
0 commit comments