summaryrefslogtreecommitdiff
path: root/ext/crypto/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/crypto/lib.rs')
-rw-r--r--ext/crypto/lib.rs397
1 files changed, 363 insertions, 34 deletions
diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs
index dcf03d64c..d811e504d 100644
--- a/ext/crypto/lib.rs
+++ b/ext/crypto/lib.rs
@@ -40,6 +40,7 @@ use rsa::padding::PaddingScheme;
use rsa::pkcs1::der::Decodable;
use rsa::pkcs1::der::Encodable;
use rsa::pkcs1::FromRsaPrivateKey;
+use rsa::pkcs1::FromRsaPublicKey;
use rsa::pkcs1::ToRsaPrivateKey;
use rsa::pkcs8::der::asn1;
use rsa::pkcs8::FromPrivateKey;
@@ -277,10 +278,16 @@ pub enum KeyFormat {
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
+pub enum KeyType {
+ Secret,
+ Private,
+ Public,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "lowercase")]
pub struct KeyData {
- // TODO(littledivy): Kept here to be used to importKey() in future.
- #[allow(dead_code)]
- r#type: KeyFormat,
+ r#type: KeyType,
data: ZeroCopyBuf,
}
@@ -458,8 +465,7 @@ pub async fn op_crypto_verify_key(
let verification = match algorithm {
Algorithm::RsassaPkcs1v15 => {
- let public_key: RsaPublicKey =
- RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
+ let public_key = read_rsa_public_key(args.key)?;
let (padding, hashed) = match args
.hash
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
@@ -515,8 +521,7 @@ pub async fn op_crypto_verify_key(
.salt_length
.ok_or_else(|| type_error("Missing argument saltLength".to_string()))?
as usize;
- let public_key: RsaPublicKey =
- RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
+ let public_key = read_rsa_public_key(args.key)?;
let rng = OsRng;
let (padding, hashed) = match args
@@ -903,6 +908,17 @@ pub struct EncryptArg {
length: Option<usize>,
}
+fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
+ let public_key = match key_data.r#type {
+ KeyType::Private => {
+ RsaPrivateKey::from_pkcs1_der(&*key_data.data)?.to_public_key()
+ }
+ KeyType::Public => RsaPublicKey::from_pkcs1_der(&*key_data.data)?,
+ KeyType::Secret => unreachable!("unexpected KeyType::Secret"),
+ };
+ Ok(public_key)
+}
+
pub async fn op_crypto_encrypt_key(
_state: Rc<RefCell<OpState>>,
args: EncryptArg,
@@ -913,8 +929,7 @@ pub async fn op_crypto_encrypt_key(
match algorithm {
Algorithm::RsaOaep => {
- let public_key: RsaPublicKey =
- RsaPrivateKey::from_pkcs1_der(&*args.key.data)?.to_public_key();
+ let public_key = read_rsa_public_key(args.key)?;
let label = args.label.map(|l| String::from_utf8_lossy(&*l).to_string());
let mut rng = OsRng;
let padding = match args
@@ -1230,7 +1245,7 @@ pub async fn op_crypto_import_key(
// 2-3.
let pk_info =
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
- custom_error("DOMExceptionOperationError", e.to_string())
+ custom_error("DOMExceptionDataError", e.to_string())
})?;
// 4-5.
@@ -1248,7 +1263,12 @@ pub async fn op_crypto_import_key(
SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384),
// sha512WithRSAEncryption
SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512),
- _ => return Err(type_error("Unsupported algorithm".to_string())),
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported algorithm".to_string(),
+ ))
+ }
};
// 7.
@@ -1262,17 +1282,17 @@ pub async fn op_crypto_import_key(
}
// 8-9.
- let private_key =
- rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
- |e| custom_error("DOMExceptionOperationError", e.to_string()),
- )?;
+ let private_key = rsa::pkcs1::RsaPrivateKey::from_der(
+ pk_info.private_key,
+ )
+ .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?;
let bytes_consumed = private_key.encoded_len().map_err(|e| {
custom_error("DOMExceptionDataError", e.to_string())
})?;
if bytes_consumed
- != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
+ != spki::der::Length::new(pk_info.private_key.len() as u16)
{
return Err(custom_error(
"DOMExceptionDataError",
@@ -1288,7 +1308,78 @@ pub async fn op_crypto_import_key(
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
})
}
- // TODO(@littledivy): spki
+ KeyFormat::Spki => {
+ let hash = args
+ .hash
+ .ok_or_else(|| type_error("Missing argument hash".to_string()))?;
+
+ // 2-3.
+ let pk_info =
+ spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ // 4-5.
+ let alg = pk_info.algorithm.oid;
+
+ // 6.
+ let pk_hash = match alg {
+ // rsaEncryption
+ RSA_ENCRYPTION_OID => None,
+ // sha1WithRSAEncryption
+ SHA1_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha1),
+ // sha256WithRSAEncryption
+ SHA256_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha256),
+ // sha384WithRSAEncryption
+ SHA384_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha384),
+ // sha512WithRSAEncryption
+ SHA512_RSA_ENCRYPTION_OID => Some(CryptoHash::Sha512),
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported algorithm".to_string(),
+ ))
+ }
+ };
+
+ // 7.
+ if let Some(pk_hash) = pk_hash {
+ if pk_hash != hash {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Hash mismatch".to_string(),
+ ));
+ }
+ }
+
+ // 8-9.
+ let public_key =
+ rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key)
+ .map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ let bytes_consumed = public_key.encoded_len().map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ if bytes_consumed
+ != spki::der::Length::new(pk_info.subject_public_key.len() as u16)
+ {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Some bytes were not consumed".to_string(),
+ ));
+ }
+
+ Ok(ImportKeyResult {
+ data: pk_info.subject_public_key.to_vec().into(),
+ public_exponent: Some(
+ public_key.public_exponent.as_bytes().to_vec().into(),
+ ),
+ modulus_length: Some(public_key.modulus.as_bytes().len() * 8),
+ })
+ }
// TODO(@littledivy): jwk
_ => Err(type_error("Unsupported format".to_string())),
}
@@ -1303,7 +1394,7 @@ pub async fn op_crypto_import_key(
// 2-3.
let pk_info =
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
- custom_error("DOMExceptionOperationError", e.to_string())
+ custom_error("DOMExceptionDataError", e.to_string())
})?;
// 4-5.
@@ -1355,6 +1446,12 @@ pub async fn op_crypto_import_key(
));
}
+ // TODO(lucacasonato):
+ // If the parameters field of the maskGenAlgorithm field of params
+ // is not an instance of the HashAlgorithm ASN.1 type that is
+ // identical in content to the hashAlgorithm field of params,
+ // throw a NotSupportedError.
+
hash
}
_ => {
@@ -1376,17 +1473,17 @@ pub async fn op_crypto_import_key(
}
// 8-9.
- let private_key =
- rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
- |e| custom_error("DOMExceptionOperationError", e.to_string()),
- )?;
+ let private_key = rsa::pkcs1::RsaPrivateKey::from_der(
+ pk_info.private_key,
+ )
+ .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?;
- let bytes_consumed = private_key
- .encoded_len()
- .map_err(|e| custom_error("DataError", e.to_string()))?;
+ let bytes_consumed = private_key.encoded_len().map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
if bytes_consumed
- != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
+ != spki::der::Length::new(pk_info.private_key.len() as u16)
{
return Err(custom_error(
"DOMExceptionDataError",
@@ -1402,7 +1499,120 @@ pub async fn op_crypto_import_key(
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
})
}
- // TODO(@littledivy): spki
+ KeyFormat::Spki => {
+ let hash = args
+ .hash
+ .ok_or_else(|| type_error("Missing argument hash".to_string()))?;
+
+ // 2-3.
+ let pk_info =
+ spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ // 4-5.
+ let alg = pk_info.algorithm.oid;
+
+ // 6.
+ let pk_hash = match alg {
+ // rsaEncryption
+ RSA_ENCRYPTION_OID => None,
+ // id-RSASSA-PSS
+ RSASSA_PSS_OID => {
+ let params = PssPrivateKeyParameters::try_from(
+ pk_info.algorithm.parameters.ok_or_else(|| {
+ custom_error(
+ "DOMExceptionDataError",
+ "Malformed parameters".to_string(),
+ )
+ })?,
+ )
+ .map_err(|_| {
+ custom_error(
+ "DOMExceptionDataError",
+ "Malformed parameters".to_string(),
+ )
+ })?;
+
+ let hash_alg = params.hash_algorithm;
+ let hash = match hash_alg.oid {
+ // id-sha1
+ ID_SHA1_OID => Some(CryptoHash::Sha1),
+ // id-sha256
+ ID_SHA256_OID => Some(CryptoHash::Sha256),
+ // id-sha384
+ ID_SHA384_OID => Some(CryptoHash::Sha384),
+ // id-sha256
+ ID_SHA512_OID => Some(CryptoHash::Sha512),
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported hash algorithm".to_string(),
+ ))
+ }
+ };
+
+ if params.mask_gen_algorithm.oid != ID_MFG1 {
+ return Err(custom_error(
+ "DOMExceptionNotSupportedError",
+ "Unsupported hash algorithm".to_string(),
+ ));
+ }
+
+ // TODO(lucacasonato):
+ // If the parameters field of the maskGenAlgorithm field of params
+ // is not an instance of the HashAlgorithm ASN.1 type that is
+ // identical in content to the hashAlgorithm field of params,
+ // throw a NotSupportedError.
+
+ hash
+ }
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported algorithm".to_string(),
+ ))
+ }
+ };
+
+ // 7.
+ if let Some(pk_hash) = pk_hash {
+ if pk_hash != hash {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Hash mismatch".to_string(),
+ ));
+ }
+ }
+
+ // 8-9.
+ let public_key =
+ rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key)
+ .map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ let bytes_consumed = public_key.encoded_len().map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ if bytes_consumed
+ != spki::der::Length::new(pk_info.subject_public_key.len() as u16)
+ {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Some bytes were not consumed".to_string(),
+ ));
+ }
+
+ Ok(ImportKeyResult {
+ data: pk_info.subject_public_key.to_vec().into(),
+ public_exponent: Some(
+ public_key.public_exponent.as_bytes().to_vec().into(),
+ ),
+ modulus_length: Some(public_key.modulus.as_bytes().len() * 8),
+ })
+ }
// TODO(@littledivy): jwk
_ => Err(type_error("Unsupported format".to_string())),
}
@@ -1417,7 +1627,7 @@ pub async fn op_crypto_import_key(
// 2-3.
let pk_info =
rsa::pkcs8::PrivateKeyInfo::from_der(data).map_err(|e| {
- custom_error("DOMExceptionOperationError", e.to_string())
+ custom_error("DOMExceptionDataError", e.to_string())
})?;
// 4-5.
@@ -1469,6 +1679,12 @@ pub async fn op_crypto_import_key(
));
}
+ // TODO(lucacasonato):
+ // If the parameters field of the maskGenAlgorithm field of params
+ // is not an instance of the HashAlgorithm ASN.1 type that is
+ // identical in content to the hashAlgorithm field of params,
+ // throw a NotSupportedError.
+
hash
}
_ => {
@@ -1490,17 +1706,17 @@ pub async fn op_crypto_import_key(
}
// 8-9.
- let private_key =
- rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key).map_err(
- |e| custom_error("DOMExceptionOperationError", e.to_string()),
- )?;
+ let private_key = rsa::pkcs1::RsaPrivateKey::from_der(
+ pk_info.private_key,
+ )
+ .map_err(|e| custom_error("DOMExceptionDataError", e.to_string()))?;
let bytes_consumed = private_key.encoded_len().map_err(|e| {
custom_error("DOMExceptionDataError", e.to_string())
})?;
if bytes_consumed
- != rsa::pkcs1::der::Length::new(pk_info.private_key.len() as u16)
+ != spki::der::Length::new(pk_info.private_key.len() as u16)
{
return Err(custom_error(
"DOMExceptionDataError",
@@ -1516,7 +1732,120 @@ pub async fn op_crypto_import_key(
modulus_length: Some(private_key.modulus.as_bytes().len() * 8),
})
}
- // TODO(@littledivy): spki
+ KeyFormat::Spki => {
+ let hash = args
+ .hash
+ .ok_or_else(|| type_error("Missing argument hash".to_string()))?;
+
+ // 2-3.
+ let pk_info =
+ spki::SubjectPublicKeyInfo::from_der(data).map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ // 4-5.
+ let alg = pk_info.algorithm.oid;
+
+ // 6.
+ let pk_hash = match alg {
+ // rsaEncryption
+ RSA_ENCRYPTION_OID => None,
+ // id-RSAES-OAEP
+ RSAES_OAEP_OID => {
+ let params = OaepPrivateKeyParameters::try_from(
+ pk_info.algorithm.parameters.ok_or_else(|| {
+ custom_error(
+ "DOMExceptionDataError",
+ "Malformed parameters".to_string(),
+ )
+ })?,
+ )
+ .map_err(|_| {
+ custom_error(
+ "DOMExceptionDataError",
+ "Malformed parameters".to_string(),
+ )
+ })?;
+
+ let hash_alg = params.hash_algorithm;
+ let hash = match hash_alg.oid {
+ // id-sha1
+ ID_SHA1_OID => Some(CryptoHash::Sha1),
+ // id-sha256
+ ID_SHA256_OID => Some(CryptoHash::Sha256),
+ // id-sha384
+ ID_SHA384_OID => Some(CryptoHash::Sha384),
+ // id-sha256
+ ID_SHA512_OID => Some(CryptoHash::Sha512),
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported hash algorithm".to_string(),
+ ))
+ }
+ };
+
+ if params.mask_gen_algorithm.oid != ID_MFG1 {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported hash algorithm".to_string(),
+ ));
+ }
+
+ // TODO(lucacasonato):
+ // If the parameters field of the maskGenAlgorithm field of params
+ // is not an instance of the HashAlgorithm ASN.1 type that is
+ // identical in content to the hashAlgorithm field of params,
+ // throw a NotSupportedError.
+
+ hash
+ }
+ _ => {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Unsupported algorithm".to_string(),
+ ))
+ }
+ };
+
+ // 7.
+ if let Some(pk_hash) = pk_hash {
+ if pk_hash != hash {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Hash mismatch".to_string(),
+ ));
+ }
+ }
+
+ // 8-9.
+ let public_key =
+ rsa::pkcs1::RsaPublicKey::from_der(pk_info.subject_public_key)
+ .map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ let bytes_consumed = public_key.encoded_len().map_err(|e| {
+ custom_error("DOMExceptionDataError", e.to_string())
+ })?;
+
+ if bytes_consumed
+ != spki::der::Length::new(pk_info.subject_public_key.len() as u16)
+ {
+ return Err(custom_error(
+ "DOMExceptionDataError",
+ "Some bytes were not consumed".to_string(),
+ ));
+ }
+
+ Ok(ImportKeyResult {
+ data: pk_info.subject_public_key.to_vec().into(),
+ public_exponent: Some(
+ public_key.public_exponent.as_bytes().to_vec().into(),
+ ),
+ modulus_length: Some(public_key.modulus.as_bytes().len() * 8),
+ })
+ }
// TODO(@littledivy): jwk
_ => Err(type_error("Unsupported format".to_string())),
}