Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#93 Add support for asymmetric keys to vault transit engine #100

Merged
merged 2 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ tracing = { version = "0.1.37", features = ["log"] }

[dev-dependencies]
base64 = "0.21"
chrono = "0.4.38"
Comment on lines 40 to +41
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't use this anymore right?

Suggested change
base64 = "0.21"
chrono = "0.4.38"
base64 = "0.21"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used as a dev-dependency to check the creation_time in the tests can be parsed. This differs from the symmetric route, as vault returns a unix integer timestamp for that; whereas for asymmetric keys it returns an ISO8601 timestamp. TLDR; checks that vault returns ISO8601

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data-encoding = "2.3.3"
tokio-test = "0.4.2"
tracing-subscriber = { version = "0.3.16", default-features = false, features = ["env-filter", "fmt"] }
Expand Down
4 changes: 4 additions & 0 deletions src/api/transit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ pub enum KeyType {
/// ECDSA using the P-521 elliptic curve (asymmetric)
EcdsaP521,
/// RSA with bit size of 2048 (asymmetric)
// kebab-case conversion doesn't work for words starting with a digit.
#[serde(rename = "rsa-2048")]
Rsa2048,
/// RSA with bit size of 3072 (asymmetric)
#[serde(rename = "rsa-3072")]
Rsa3072,
/// RSA with bit size of 4096 (asymmetric)
#[serde(rename = "rsa-4096")]
Rsa4096,
}

Expand Down
20 changes: 19 additions & 1 deletion src/api/transit/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ pub struct ReadKeyResponse {
pub derived: bool,
pub exportable: bool,
pub allow_plaintext_backup: bool,
pub keys: HashMap<String, u64>,
/// If the key is asymmetric, the API returns the public keys
pub keys: ReadKeyData,
pub min_decryption_version: u64,
pub min_encryption_version: u64,
pub name: String,
Expand All @@ -23,6 +24,23 @@ pub struct ReadKeyResponse {
pub imported: Option<bool>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ReadKeyData {
/// A key ID integer (string) to unix timestamp.
Symmetric(HashMap<String, u64>),
/// A key ID integer (string) to public key mapping.
Asymmetric(HashMap<String, ReadPublicKeyEntry>),
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct ReadPublicKeyEntry {
/// An ISO8601 timestamp
pub creation_time: String,
pub name: String,
pub public_key: String,
}

/// Response from executing
/// [ListKeysRequest][crate::api::transit::requests::ListKeysRequest]
#[derive(Deserialize, Debug, Serialize)]
Expand Down
41 changes: 41 additions & 0 deletions tests/transit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ mod key {
)
.await;
assert!(resp.is_ok());

let resp = key::create(
&endpoint.client,
&endpoint.path,
&endpoint.keys.asymmetric,
Some(
CreateKeyRequest::builder()
.exportable(false)
.derived(false)
.key_type(KeyType::Rsa2048),
),
)
.await;
assert!(resp.is_ok());
}

pub async fn test_read(endpoint: &TransitEndpoint) {
Expand All @@ -107,6 +121,31 @@ mod key {
.unwrap();
// requires config update first
assert!(!&resp.deletion_allowed);

let resp = key::read(&endpoint.client, &endpoint.path, &endpoint.keys.asymmetric)
.await
.unwrap();
assert_eq!(&resp.name, &endpoint.keys.asymmetric);
assert!(matches!(&resp.key_type, KeyType::Rsa2048));
match &resp.keys {
vaultrs::api::transit::responses::ReadKeyData::Symmetric(_) => {
panic!("Key must be asymmetric")
}
vaultrs::api::transit::responses::ReadKeyData::Asymmetric(keys) => {
for (_key_id, key_metadata) in keys {
let _datetime: chrono::DateTime<chrono::Utc> = key_metadata
.creation_time
.parse()
.expect("Parse ISO8601 timestamp correctly");
assert!(key_metadata
.public_key
.starts_with("-----BEGIN PUBLIC KEY-----\n"));
assert!(key_metadata
.public_key
.ends_with("\n-----END PUBLIC KEY-----\n"));
}
}
}
}

pub async fn test_list(endpoint: &TransitEndpoint) {
Expand Down Expand Up @@ -418,6 +457,7 @@ pub struct TestKeys {
pub export: String,
pub delete: String,
pub signing: String,
pub asymmetric: String,
}

pub struct TestData {
Expand Down Expand Up @@ -458,6 +498,7 @@ impl TransitEndpoint {
export: "export-key".into(),
delete: "delete-key".into(),
signing: "signing-key".into(),
asymmetric: "asymmetric-key".into(),
},
data: TestData::new("test-context", "super secret data"),
};
Expand Down
Loading