diff --git a/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs b/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs index 7a148af8dd7e..ed1c777aec8c 100644 --- a/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs +++ b/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs @@ -50,7 +50,7 @@ public sealed class AzblobServiceConfig : IServiceConfig /// public string? EncryptionAlgorithm { get; init; } /// - /// The encryption key of Azblob service backend. + /// The Base64-encoded AES-256 customer-provided encryption key. The key must decode to exactly 32 bytes. Building an operator returns `ConfigInvalid` for invalid Base64 or a different decoded length. /// public string? EncryptionKey { get; init; } /// diff --git a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java index 448b999143d3..ad1c1a557af4 100644 --- a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java +++ b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java @@ -187,7 +187,9 @@ class Azblob implements ServiceConfig { */ public final String encryptionAlgorithm; /** - *

The encryption key of Azblob service backend.

+ *

The Base64-encoded AES-256 customer-provided encryption key.

+ *

The key must decode to exactly 32 bytes. Building an operator returns + * ConfigInvalid for invalid Base64 or a different decoded length.

*/ public final String encryptionKey; /** diff --git a/bindings/python/python/opendal/config.py b/bindings/python/python/opendal/config.py index 2bc02ffd1439..b03821195cc6 100644 --- a/bindings/python/python/opendal/config.py +++ b/bindings/python/python/opendal/config.py @@ -96,7 +96,7 @@ class AzblobConfig(TypedDict): encryption_algorithm: NotRequired[str] """The encryption algorithm of Azblob service backend.""" encryption_key: NotRequired[str] - """The encryption key of Azblob service backend.""" + """The Base64-encoded AES-256 customer-provided encryption key. The key must decode to exactly 32 bytes. Building an operator returns `ConfigInvalid` for invalid Base64 or a different decoded length.""" encryption_key_sha256: NotRequired[str] """The encryption key sha256 of Azblob service backend.""" endpoint: NotRequired[str] diff --git a/core/services/azblob/src/backend.rs b/core/services/azblob/src/backend.rs index 5208c4350521..1f8da3aeaca7 100644 --- a/core/services/azblob/src/backend.rs +++ b/core/services/azblob/src/backend.rs @@ -150,7 +150,9 @@ impl AzblobBuilder { /// /// # Args /// - /// `v`: Base64-encoded key that matches algorithm specified in `encryption_algorithm`. + /// `v`: Base64-encoded AES-256 key that decodes to exactly 32 bytes. + /// Building an operator returns [`ErrorKind::ConfigInvalid`] if the key + /// is not valid Base64 or has a different decoded length. /// /// # Note /// @@ -212,7 +214,9 @@ impl AzblobBuilder { /// /// # Args /// - /// `key`: Base64-encoded SHA256 digest of the key specified in encryption_key. + /// `key`: Raw AES-256 key containing exactly 32 bytes. + /// Building an operator returns [`ErrorKind::ConfigInvalid`] if the key + /// has a different length. /// /// # Note /// @@ -347,13 +351,32 @@ impl Builder for AzblobBuilder { } } - let encryption_key = - match &self.config.encryption_key { - None => None, - Some(v) => Some(build_header_value(v).map_err(|err| { + let encryption_key = match &self.config.encryption_key { + None => None, + Some(v) => { + let key = BASE64_STANDARD.decode(v).map_err(|_| { + Error::new( + ErrorKind::ConfigInvalid, + "encryption_key must be valid Base64", + ) + .with_operation("Builder::build") + .with_context("service", AZBLOB_SCHEME) + .with_context("key", "encryption_key") + })?; + if key.len() != 32 { + return Err(Error::new( + ErrorKind::ConfigInvalid, + "encryption_key must decode to exactly 32 bytes", + ) + .with_operation("Builder::build") + .with_context("service", AZBLOB_SCHEME) + .with_context("key", "encryption_key")); + } + Some(build_header_value(v).map_err(|err| { err.with_context("key", "server_side_encryption_customer_key") - })?), - }; + })?) + } + }; let encryption_key_sha256 = match &self.config.encryption_key_sha256 { None => None, @@ -663,3 +686,66 @@ impl Service for AzblobBackend { ))) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn invalid_encoded_customer_keys_are_rejected() { + for key in [ + String::new(), + "not-base64!".to_string(), + BASE64_STANDARD.encode([42; 16]), + BASE64_STANDARD.encode([42; 31]), + BASE64_STANDARD.encode([42; 33]), + BASE64_STANDARD.encode([42; 64]), + ] { + let config = AzblobConfig { + container: "test".to_string(), + endpoint: Some("https://test.blob.core.windows.net".to_string()), + encryption_key: Some(key.clone()), + ..Default::default() + }; + let err = Operator::from_config(config).expect_err("invalid key must fail to build"); + assert_eq!(err.kind(), ErrorKind::ConfigInvalid); + let message = format!("{err:?}"); + assert!(message.contains("encryption_key")); + if !key.is_empty() { + assert!(!message.contains(&key)); + } + } + } + + #[test] + fn invalid_raw_customer_keys_are_rejected() { + for len in [0, 16, 31, 33, 64] { + let builder = AzblobBuilder::default() + .container("test") + .endpoint("https://test.blob.core.windows.net") + .server_side_encryption_with_customer_key(&vec![42; len]); + let err = Operator::new(builder).expect_err("invalid key must fail to build"); + assert_eq!(err.kind(), ErrorKind::ConfigInvalid); + } + } + + #[test] + fn valid_customer_keys_build() { + let key = [42; 32]; + let builder = AzblobBuilder::default() + .container("test") + .endpoint("https://test.blob.core.windows.net") + .server_side_encryption_with_customer_key(&key); + Operator::new(builder).expect("32-byte raw key must build"); + + let config = AzblobConfig { + container: "test".to_string(), + endpoint: Some("https://test.blob.core.windows.net".to_string()), + encryption_key: Some(BASE64_STANDARD.encode(key)), + encryption_key_sha256: Some(BASE64_STANDARD.encode(Sha256::digest(key))), + encryption_algorithm: Some("AES256".to_string()), + ..Default::default() + }; + Operator::from_config(config).expect("Base64-encoded 32-byte key must build"); + } +} diff --git a/core/services/azblob/src/config.rs b/core/services/azblob/src/config.rs index 0e8933ba2193..b9654f772eff 100644 --- a/core/services/azblob/src/config.rs +++ b/core/services/azblob/src/config.rs @@ -57,7 +57,10 @@ pub struct AzblobConfig { )] pub account_key: Option, - /// The encryption key of Azblob service backend. + /// The Base64-encoded AES-256 customer-provided encryption key. + /// + /// The key must decode to exactly 32 bytes. Building an operator returns + /// `ConfigInvalid` for invalid Base64 or a different decoded length. pub encryption_key: Option, /// The encryption key sha256 of Azblob service backend. diff --git a/website/data/services.json b/website/data/services.json index d3aa21b40c95..008ae0add998 100644 --- a/website/data/services.json +++ b/website/data/services.json @@ -227,7 +227,7 @@ "type": "string", "required": false, "group": "General", - "comments": "The encryption key of Azblob service backend." + "comments": "The Base64-encoded AES-256 customer-provided encryption key.\n\nThe key must decode to exactly 32 bytes. Building an operator returns\n`ConfigInvalid` for invalid Base64 or a different decoded length." }, { "name": "encryption_key_sha256", @@ -274,37 +274,37 @@ "binding": "rust", "language": "rust", "minimal": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n (\"container\".to_string(), \"...\".to_string()),\n])?;", - "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // (\"root\".to_string(), \"...\".to_string()),\n\n // The container name of Azblob service backend.\n (\"container\".to_string(), \"...\".to_string()),\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // The account name of Azblob service backend.\n // (\"account_name\".to_string(), \"...\".to_string()),\n\n // The account key of Azblob service backend.\n // (\"account_key\".to_string(), \"...\".to_string()),\n\n // The encryption key of Azblob service backend.\n // (\"encryption_key\".to_string(), \"...\".to_string()),\n\n // The encryption key sha256 of Azblob service backend.\n // (\"encryption_key_sha256\".to_string(), \"...\".to_string()),\n\n // The encryption algorithm of Azblob service backend.\n // (\"encryption_algorithm\".to_string(), \"...\".to_string()),\n\n // The sas token of Azblob service backend.\n // (\"sas_token\".to_string(), \"...\".to_string()),\n\n // Skip signature will skip loading credentials and signing requests.\n // (\"skip_signature\".to_string(), \"true\".to_string()),\n])?;" + "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // (\"root\".to_string(), \"...\".to_string()),\n\n // The container name of Azblob service backend.\n (\"container\".to_string(), \"...\".to_string()),\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // The account name of Azblob service backend.\n // (\"account_name\".to_string(), \"...\".to_string()),\n\n // The account key of Azblob service backend.\n // (\"account_key\".to_string(), \"...\".to_string()),\n\n // The Base64-encoded AES-256 customer-provided encryption key.\n //\n // The key must decode to exactly 32 bytes. Building an operator returns\n // `ConfigInvalid` for invalid Base64 or a different decoded length.\n // (\"encryption_key\".to_string(), \"...\".to_string()),\n\n // The encryption key sha256 of Azblob service backend.\n // (\"encryption_key_sha256\".to_string(), \"...\".to_string()),\n\n // The encryption algorithm of Azblob service backend.\n // (\"encryption_algorithm\".to_string(), \"...\".to_string()),\n\n // The sas token of Azblob service backend.\n // (\"sas_token\".to_string(), \"...\".to_string()),\n\n // Skip signature will skip loading credentials and signing requests.\n // (\"skip_signature\".to_string(), \"true\".to_string()),\n])?;" }, { "binding": "java", "language": "java", "minimal": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\nconfig.put(\"container\", \"...\");\nOperator operator = Operator.of(\"azblob\", config);", - "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\n// The root of Azblob service backend.\n//\n// All operations will happen under this root.\n// config.put(\"root\", \"...\");\n\n// The container name of Azblob service backend.\nconfig.put(\"container\", \"...\");\n\n// The endpoint of Azblob service backend.\n//\n// Endpoint must be full uri, e.g.\n//\n// - Azblob: `https://accountname.blob.core.windows.net`\n// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n// config.put(\"endpoint\", \"...\");\n\n// The account name of Azblob service backend.\n// config.put(\"account_name\", \"...\");\n\n// The account key of Azblob service backend.\n// config.put(\"account_key\", \"...\");\n\n// The encryption key of Azblob service backend.\n// config.put(\"encryption_key\", \"...\");\n\n// The encryption key sha256 of Azblob service backend.\n// config.put(\"encryption_key_sha256\", \"...\");\n\n// The encryption algorithm of Azblob service backend.\n// config.put(\"encryption_algorithm\", \"...\");\n\n// The sas token of Azblob service backend.\n// config.put(\"sas_token\", \"...\");\n\n// Skip signature will skip loading credentials and signing requests.\n// config.put(\"skip_signature\", \"true\");\nOperator operator = Operator.of(\"azblob\", config);" + "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\n// The root of Azblob service backend.\n//\n// All operations will happen under this root.\n// config.put(\"root\", \"...\");\n\n// The container name of Azblob service backend.\nconfig.put(\"container\", \"...\");\n\n// The endpoint of Azblob service backend.\n//\n// Endpoint must be full uri, e.g.\n//\n// - Azblob: `https://accountname.blob.core.windows.net`\n// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n// config.put(\"endpoint\", \"...\");\n\n// The account name of Azblob service backend.\n// config.put(\"account_name\", \"...\");\n\n// The account key of Azblob service backend.\n// config.put(\"account_key\", \"...\");\n\n// The Base64-encoded AES-256 customer-provided encryption key.\n//\n// The key must decode to exactly 32 bytes. Building an operator returns\n// `ConfigInvalid` for invalid Base64 or a different decoded length.\n// config.put(\"encryption_key\", \"...\");\n\n// The encryption key sha256 of Azblob service backend.\n// config.put(\"encryption_key_sha256\", \"...\");\n\n// The encryption algorithm of Azblob service backend.\n// config.put(\"encryption_algorithm\", \"...\");\n\n// The sas token of Azblob service backend.\n// config.put(\"sas_token\", \"...\");\n\n// Skip signature will skip loading credentials and signing requests.\n// config.put(\"skip_signature\", \"true\");\nOperator operator = Operator.of(\"azblob\", config);" }, { "binding": "python", "language": "python", "minimal": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n container=\"...\",\n)", - "full": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # root=\"...\",\n\n # The container name of Azblob service backend.\n container=\"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # endpoint=\"...\",\n\n # The account name of Azblob service backend.\n # account_name=\"...\",\n\n # The account key of Azblob service backend.\n # account_key=\"...\",\n\n # The encryption key of Azblob service backend.\n # encryption_key=\"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # encryption_key_sha256=\"...\",\n\n # The encryption algorithm of Azblob service backend.\n # encryption_algorithm=\"...\",\n\n # The sas token of Azblob service backend.\n # sas_token=\"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # skip_signature=\"true\",\n)" + "full": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # root=\"...\",\n\n # The container name of Azblob service backend.\n container=\"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # endpoint=\"...\",\n\n # The account name of Azblob service backend.\n # account_name=\"...\",\n\n # The account key of Azblob service backend.\n # account_key=\"...\",\n\n # The Base64-encoded AES-256 customer-provided encryption key.\n #\n # The key must decode to exactly 32 bytes. Building an operator returns\n # `ConfigInvalid` for invalid Base64 or a different decoded length.\n # encryption_key=\"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # encryption_key_sha256=\"...\",\n\n # The encryption algorithm of Azblob service backend.\n # encryption_algorithm=\"...\",\n\n # The sas token of Azblob service backend.\n # sas_token=\"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # skip_signature=\"true\",\n)" }, { "binding": "nodejs", "language": "javascript", "minimal": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n container: \"...\",\n});", - "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // root: \"...\",\n\n // The container name of Azblob service backend.\n container: \"...\",\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // endpoint: \"...\",\n\n // The account name of Azblob service backend.\n // account_name: \"...\",\n\n // The account key of Azblob service backend.\n // account_key: \"...\",\n\n // The encryption key of Azblob service backend.\n // encryption_key: \"...\",\n\n // The encryption key sha256 of Azblob service backend.\n // encryption_key_sha256: \"...\",\n\n // The encryption algorithm of Azblob service backend.\n // encryption_algorithm: \"...\",\n\n // The sas token of Azblob service backend.\n // sas_token: \"...\",\n\n // Skip signature will skip loading credentials and signing requests.\n // skip_signature: \"true\",\n});" + "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // root: \"...\",\n\n // The container name of Azblob service backend.\n container: \"...\",\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // endpoint: \"...\",\n\n // The account name of Azblob service backend.\n // account_name: \"...\",\n\n // The account key of Azblob service backend.\n // account_key: \"...\",\n\n // The Base64-encoded AES-256 customer-provided encryption key.\n //\n // The key must decode to exactly 32 bytes. Building an operator returns\n // `ConfigInvalid` for invalid Base64 or a different decoded length.\n // encryption_key: \"...\",\n\n // The encryption key sha256 of Azblob service backend.\n // encryption_key_sha256: \"...\",\n\n // The encryption algorithm of Azblob service backend.\n // encryption_algorithm: \"...\",\n\n // The sas token of Azblob service backend.\n // sas_token: \"...\",\n\n // Skip signature will skip loading credentials and signing requests.\n // skip_signature: \"true\",\n});" }, { "binding": "ruby", "language": "ruby", "minimal": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n \"container\" => \"...\",\n})", - "full": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # \"root\" => \"...\",\n\n # The container name of Azblob service backend.\n \"container\" => \"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # \"endpoint\" => \"...\",\n\n # The account name of Azblob service backend.\n # \"account_name\" => \"...\",\n\n # The account key of Azblob service backend.\n # \"account_key\" => \"...\",\n\n # The encryption key of Azblob service backend.\n # \"encryption_key\" => \"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # \"encryption_key_sha256\" => \"...\",\n\n # The encryption algorithm of Azblob service backend.\n # \"encryption_algorithm\" => \"...\",\n\n # The sas token of Azblob service backend.\n # \"sas_token\" => \"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # \"skip_signature\" => \"true\",\n})" + "full": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # \"root\" => \"...\",\n\n # The container name of Azblob service backend.\n \"container\" => \"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # \"endpoint\" => \"...\",\n\n # The account name of Azblob service backend.\n # \"account_name\" => \"...\",\n\n # The account key of Azblob service backend.\n # \"account_key\" => \"...\",\n\n # The Base64-encoded AES-256 customer-provided encryption key.\n #\n # The key must decode to exactly 32 bytes. Building an operator returns\n # `ConfigInvalid` for invalid Base64 or a different decoded length.\n # \"encryption_key\" => \"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # \"encryption_key_sha256\" => \"...\",\n\n # The encryption algorithm of Azblob service backend.\n # \"encryption_algorithm\" => \"...\",\n\n # The sas token of Azblob service backend.\n # \"sas_token\" => \"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # \"skip_signature\" => \"true\",\n})" }, { "binding": "go", "language": "go", "minimal": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t\"container\": \"...\",\n})", - "full": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t// The root of Azblob service backend.\n\t//\n\t// All operations will happen under this root.\n\t// \"root\": \"...\",\n\n\t// The container name of Azblob service backend.\n\t\"container\": \"...\",\n\n\t// The endpoint of Azblob service backend.\n\t//\n\t// Endpoint must be full uri, e.g.\n\t//\n\t// - Azblob: `https://accountname.blob.core.windows.net`\n\t// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n\t// \"endpoint\": \"...\",\n\n\t// The account name of Azblob service backend.\n\t// \"account_name\": \"...\",\n\n\t// The account key of Azblob service backend.\n\t// \"account_key\": \"...\",\n\n\t// The encryption key of Azblob service backend.\n\t// \"encryption_key\": \"...\",\n\n\t// The encryption key sha256 of Azblob service backend.\n\t// \"encryption_key_sha256\": \"...\",\n\n\t// The encryption algorithm of Azblob service backend.\n\t// \"encryption_algorithm\": \"...\",\n\n\t// The sas token of Azblob service backend.\n\t// \"sas_token\": \"...\",\n\n\t// Skip signature will skip loading credentials and signing requests.\n\t// \"skip_signature\": \"true\",\n})" + "full": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t// The root of Azblob service backend.\n\t//\n\t// All operations will happen under this root.\n\t// \"root\": \"...\",\n\n\t// The container name of Azblob service backend.\n\t\"container\": \"...\",\n\n\t// The endpoint of Azblob service backend.\n\t//\n\t// Endpoint must be full uri, e.g.\n\t//\n\t// - Azblob: `https://accountname.blob.core.windows.net`\n\t// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n\t// \"endpoint\": \"...\",\n\n\t// The account name of Azblob service backend.\n\t// \"account_name\": \"...\",\n\n\t// The account key of Azblob service backend.\n\t// \"account_key\": \"...\",\n\n\t// The Base64-encoded AES-256 customer-provided encryption key.\n\t//\n\t// The key must decode to exactly 32 bytes. Building an operator returns\n\t// `ConfigInvalid` for invalid Base64 or a different decoded length.\n\t// \"encryption_key\": \"...\",\n\n\t// The encryption key sha256 of Azblob service backend.\n\t// \"encryption_key_sha256\": \"...\",\n\n\t// The encryption algorithm of Azblob service backend.\n\t// \"encryption_algorithm\": \"...\",\n\n\t// The sas token of Azblob service backend.\n\t// \"sas_token\": \"...\",\n\n\t// Skip signature will skip loading credentials and signing requests.\n\t// \"skip_signature\": \"true\",\n})" } ] },