aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2024-02-27 10:37:09 +0100
committerAlex Auvolat <alex@adnab.me>2024-02-27 10:46:12 +0100
commit8aec3fa3b23e354936e250349dbe5bcfe158b0b3 (patch)
tree8d97e79c0a70c1a898ecb0fb11fde73647f531fe
parent512109fda54194bd663bcec6b35cf195feefc337 (diff)
downloadgarage-sse-c2.tar.gz
garage-sse-c2.zip
[sse-c] enable encryption in multipart uploads + refactoringsse-c2
-rw-r--r--src/api/s3/encryption.rs64
-rw-r--r--src/api/s3/get.rs6
-rw-r--r--src/api/s3/multipart.rs6
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,