diff options
author | Alex Auvolat <alex@adnab.me> | 2024-02-27 10:37:09 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2024-02-27 10:46:12 +0100 |
commit | 8aec3fa3b23e354936e250349dbe5bcfe158b0b3 (patch) | |
tree | 8d97e79c0a70c1a898ecb0fb11fde73647f531fe | |
parent | 512109fda54194bd663bcec6b35cf195feefc337 (diff) | |
download | garage-sse-c2.tar.gz garage-sse-c2.zip |
[sse-c] enable encryption in multipart uploads + refactoringsse-c2
-rw-r--r-- | src/api/s3/encryption.rs | 64 | ||||
-rw-r--r-- | src/api/s3/get.rs | 6 | ||||
-rw-r--r-- | src/api/s3/multipart.rs | 6 |
3 files changed, 45 insertions, 31 deletions
diff --git a/src/api/s3/encryption.rs b/src/api/s3/encryption.rs index 17974df1..723aeb07 100644 --- a/src/api/s3/encryption.rs +++ b/src/api/s3/encryption.rs @@ -46,7 +46,7 @@ const X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY: HeaderName = const X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5: HeaderName = HeaderName::from_static("x-amz-copy-source-server-side-encryption-customer-key-md5"); -const CUSTOMER_ALGORITHM_AES256: HeaderValue = HeaderValue::from_static("AES256"); +const CUSTOMER_ALGORITHM_AES256: &[u8] = b"AES256"; type StreamNonceSize = aes_gcm::aead::stream::NonceSize<Aes256Gcm, StreamLE31<Aes256Gcm>>; @@ -86,7 +86,7 @@ impl EncryptionParams { } } - pub fn check_decrypt_for_get<'a>( + pub fn check_decrypt<'a>( garage: &Garage, req: &Request<impl Body>, obj_enc: &'a ObjectVersionEncryption, @@ -97,7 +97,7 @@ impl EncryptionParams { &X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, &X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, )?; - Self::check_decrypt(garage, key, obj_enc) + Self::check_decrypt_common(garage, key, obj_enc) } pub fn check_decrypt_for_copy_source<'a>( @@ -111,10 +111,10 @@ impl EncryptionParams { &X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, &X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, )?; - Self::check_decrypt(garage, key, obj_enc) + Self::check_decrypt_common(garage, key, obj_enc) } - fn check_decrypt<'a>( + fn check_decrypt_common<'a>( garage: &Garage, key: Option<Key<Aes256Gcm>>, obj_enc: &'a ObjectVersionEncryption, @@ -294,34 +294,46 @@ fn parse_request_headers( key_header: &HeaderName, md5_header: &HeaderName, ) -> Result<Option<Key<Aes256Gcm>>, Error> { - match req.headers().get(alg_header) { - Some(alg) if *alg == CUSTOMER_ALGORITHM_AES256 => { + parse_encryption_params( + req.headers().get(alg_header).map(HeaderValue::as_bytes), + req.headers().get(key_header).map(HeaderValue::as_bytes), + req.headers().get(md5_header).map(HeaderValue::as_bytes), + ) +} + +fn parse_encryption_params( + alg: Option<&[u8]>, + key: Option<&[u8]>, + md5: Option<&[u8]>, +) -> Result<Option<Key<Aes256Gcm>>, Error> { + match alg { + Some(CUSTOMER_ALGORITHM_AES256) => { use md5::{Digest, Md5}; - let key_b64 = req - .headers() - .get(key_header) - .ok_or_bad_request(format!("Missing {} header", key_header))?; + let key_b64 = + key.ok_or_bad_request("Missing server-side-encryption-customer-key header")?; let key_bytes: [u8; 32] = BASE64_STANDARD .decode(&key_b64) - .ok_or_bad_request(format!("Invalid {} header", key_header))? + .ok_or_bad_request( + "Invalid server-side-encryption-customer-key header: invalid base64", + )? .try_into() .ok() - .ok_or_bad_request(format!("Invalid {} header", key_header))?; + .ok_or_bad_request( + "Invalid server-side-encryption-customer-key header: invalid length", + )?; - let md5_b64 = req - .headers() - .get(md5_header) - .ok_or_bad_request(format!("Missing {} header", md5_header))?; - let md5_bytes = BASE64_STANDARD - .decode(&md5_b64) - .ok_or_bad_request(format!("Invalid {} header", md5_header))?; + let md5_b64 = + md5.ok_or_bad_request("Missing server-side-encryption-customer-key-md5 header")?; + let md5_bytes = BASE64_STANDARD.decode(&md5_b64).ok_or_bad_request( + "Invalid server-side-encryption-customer-key-md5 header: invalid bass64", + )?; let mut hasher = Md5::new(); hasher.update(&key_bytes[..]); if hasher.finalize().as_slice() != md5_bytes.as_slice() { return Err(Error::bad_request( - "Encryption key MD5 checksum does not match", + "Server-side encryption client key MD5 checksum does not match", )); } @@ -330,7 +342,15 @@ fn parse_request_headers( Some(alg) => Err(Error::InvalidEncryptionAlgorithm( alg.to_str().unwrap_or("??").to_string(), )), - None => Ok(None), + None => { + if key.is_some() || md5.is_some() { + Err(Error::bad_request( + "Unexpected server-side-encryption-customer-key{,-md5} header(s)", + )) + } else { + Ok(None) + } + } } } diff --git a/src/api/s3/get.rs b/src/api/s3/get.rs index 5e2a9830..65f6eacc 100644 --- a/src/api/s3/get.rs +++ b/src/api/s3/get.rs @@ -167,8 +167,7 @@ pub async fn handle_head( return Ok(cached); } - let (_enc, headers) = - EncryptionParams::check_decrypt_for_get(&garage, req, &version_meta.encryption)?; + let (_enc, headers) = EncryptionParams::check_decrypt(&garage, req, &version_meta.encryption)?; if let Some(pn) = part_number { match version_data { @@ -258,8 +257,7 @@ pub async fn handle_get( return Ok(cached); } - let (enc, headers) = - EncryptionParams::check_decrypt_for_get(&garage, req, &last_v_meta.encryption)?; + let (enc, headers) = EncryptionParams::check_decrypt(&garage, req, &last_v_meta.encryption)?; match (part_number, parse_range_header(req, last_v_meta.size)?) { (Some(_), Some(_)) => Err(Error::bad_request( diff --git a/src/api/s3/multipart.rs b/src/api/s3/multipart.rs index 3b542f01..1d71e7a6 100644 --- a/src/api/s3/multipart.rs +++ b/src/api/s3/multipart.rs @@ -41,10 +41,6 @@ pub async fn handle_create_multipart_upload( // Determine whether object should be encrypted, and if so the key let encryption = EncryptionParams::new_from_req(&garage, &req)?; - if encryption.is_encrypted() { - // TODO - unimplemented!("multipart upload encryption"); - } let object_encryption = encryption.encrypt_headers(headers)?; // Create object in object table @@ -110,7 +106,7 @@ pub async fn handle_put_part( ObjectVersionState::Uploading { encryption, .. } => encryption, _ => unreachable!(), }; - let (encryption, _) = EncryptionParams::check_decrypt_for_get( + let (encryption, _) = EncryptionParams::check_decrypt( &garage, &Request::from_parts(req_head, empty_body::<Error>()), &object_encryption, |