-
Notifications
You must be signed in to change notification settings - Fork 27
feat(native-rust): message-header inline structures (EDKs, encryption context) #901
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
Merged
lucasmcdonald3
merged 22 commits into
aws-crypto-rust/mainline
from
aws-crypto-rust/header-inline-review
May 22, 2026
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
1bb4d40
feat(native-rust): message header inline structures (EDK, encryption …
4002040
docs(native-rust): sync EDK section headers from unreviewed
20129c4
docs(native-rust): sync split write_edk section headers from unreviewed
a193415
docs(native-rust): sync encryption_context docs from unreviewed
1dd8de6
test(native-rust): sync encryption_context_aad helper refactor from u…
17dda4e
test(native-rust): sync EDK ordering section pattern from unreviewed
0912758
sync(native-rust): dedupe EDK tests, trim encryption_context comments…
b448f04
docs(native-rust): sync serializable_types/serialize_functions commen…
72d8818
test(native-rust): sync AAD order section pattern from unreviewed
1cec139
test(native-rust): sync EDK annotation-above-assert placement from un…
afc1858
test(native-rust): sync EDK test dedup from unreviewed
1694cb0
test(native-rust): sync split EDK spec citations from unreviewed
97e221f
fix(native-rust): sync AAD length convention + defensive EDK checks f…
ba880f0
docs(native-rust): sync reworded key-value-pairs-length annotation fr…
82e427a
docs(native-rust): sync simplified key-value-pairs-length rejection a…
e81af7d
perf(native-rust): sync drop-consumed-counter from unreviewed
ac29222
docs(native-rust): sync per-field get_length layout from unreviewed
9f6f58d
docs(native-rust): sync length cast-safety note from unreviewed
ea52040
docs(native-rust): sync PROPOSED marker removal from unreviewed
1df286f
feat(native-rust): sync header inline structures from unreviewed
79cabc7
style(native-rust): sync write_u8 to_be_bytes consistency from unrevi…
d63bf9a
style(native-rust): sync edk local rename from unreviewed
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,236 @@ | ||
| // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| //! Encrypted data key serialization and deserialization. | ||
|
|
||
| use super::serialize_functions::{read_seq_u16, read_str_u16, read_u16, write_bytes, write_u16}; | ||
| use super::{Error, ser_err}; | ||
| use crate::types::{SafeRead, SafeWrite}; | ||
| use aws_mpl_legacy::EncryptedDataKey; | ||
|
|
||
|
|
||
| pub(crate) fn write_edks(w: &mut dyn SafeWrite, edks: &[EncryptedDataKey]) -> Result<(), Error> { | ||
| //= spec/data-format/message-header.md#encrypted-data-keys | ||
| //# The Encrypted Data Keys MUST consist of, in order, | ||
| //# Encrypted Data Key Count, | ||
| //# and Encrypted Data Key Entries. | ||
|
|
||
| // Encrypted Data Key Count | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# This value MUST be greater than 0. | ||
| if edks.is_empty() { | ||
| return ser_err("Cannot serialize empty encrypted data keys list"); | ||
| } | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# The length of the serialized encrypted data key count MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# The encrypted data key count MUST be interpreted as a UInt16. | ||
| let Ok(edk_count) = u16::try_from(edks.len()) else { | ||
| return ser_err("Count too large for UInt16"); | ||
| }; | ||
| write_u16(w, edk_count)?; | ||
|
|
||
| // Encrypted Data Key Entries | ||
|
|
||
| for edk in edks { | ||
| write_edk(w, edk)?; | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub(crate) fn write_edk(w: &mut dyn SafeWrite, edk: &EncryptedDataKey) -> Result<(), Error> { | ||
| //= spec/data-format/message-header.md#encrypted-data-key-entries | ||
| //# Each Encrypted Data Key Entry MUST consist of, in order, | ||
| //# Key Provider ID Length, | ||
| //# Key Provider ID, | ||
| //# Key Provider Information Length, | ||
| //# Key Provider Information, | ||
| //# Encrypted Data Key Length, | ||
| //# and Encrypted Data Key. | ||
|
|
||
| // Key Provider ID Length | ||
|
|
||
| let kp_id_bytes = edk.key_provider_id.as_bytes(); | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-id-length | ||
| //# The key provider ID length MUST be interpreted as a UInt16. | ||
| let Ok(kp_id_len) = u16::try_from(kp_id_bytes.len()) else { | ||
| return ser_err("Key provider ID length too long for 16 bits"); | ||
| }; | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-id-length | ||
| //# The length of the serialized key provider ID length field MUST be 2 bytes. | ||
| write_u16(w, kp_id_len)?; | ||
|
|
||
| // Key Provider ID | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-id | ||
| //= reason=The length field is derived from the same byte slice that is serialized, so they are equal by construction. | ||
| //# The length of the serialized key provider ID MUST be equal to the value of the [Key Provider ID Length](#key-provider-id-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-id | ||
| //# The key provider ID MUST be interpreted as UTF-8 encoded bytes. | ||
| write_bytes(w, kp_id_bytes)?; | ||
|
|
||
| // Key Provider Information Length | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-information-length | ||
| //# The key provider information length MUST be interpreted as a UInt16. | ||
| let Ok(kp_info_len) = u16::try_from(edk.key_provider_info.len()) else { | ||
| return ser_err("Key provider info length too long for 16 bits"); | ||
| }; | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-information-length | ||
| //# The length of the serialized key provider information length field MUST be 2 bytes. | ||
| write_u16(w, kp_info_len)?; | ||
|
|
||
| // Key Provider Information | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-information | ||
| //= reason=The length field is derived from the same byte slice that is serialized, so they are equal by construction. | ||
| //# The length of the serialized key provider information MUST be equal to the value of the [Key Provider Information Length](#key-provider-information-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-information | ||
| //# The key provider information MUST be interpreted as bytes. | ||
| write_bytes(w, &edk.key_provider_info)?; | ||
|
|
||
| // Encrypted Data Key Length | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-length | ||
| //# The encrypted data key length MUST be interpreted as a UInt16. | ||
| let Ok(edk_len) = u16::try_from(edk.ciphertext.len()) else { | ||
| return ser_err("Encrypted data key length too long for 16 bits"); | ||
| }; | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-length | ||
| //# The length of the serialized encrypted data key length field MUST be 2 bytes. | ||
| write_u16(w, edk_len)?; | ||
|
|
||
| // Encrypted Data Key | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key | ||
| //= reason=The length field is derived from the same byte slice that is serialized, so they are equal by construction. | ||
| //# The length of the serialized encrypted data key MUST be equal to the value of the [Encrypted Data Key Length](#encrypted-data-key-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key | ||
| //= type=implication | ||
| //# The encrypted data key MUST be interpreted as bytes. | ||
| write_bytes(w, &edk.ciphertext) | ||
| } | ||
|
|
||
| pub(crate) fn read_edks( | ||
| r: &mut dyn SafeRead, | ||
| max_edks: Option<std::num::NonZeroUsize>, | ||
| raw: &mut dyn SafeWrite, | ||
| ) -> Result<Vec<EncryptedDataKey>, Error> { | ||
| //= spec/data-format/message-header.md#encrypted-data-keys | ||
| //# The Encrypted Data Keys MUST consist of, in order, | ||
| //# Encrypted Data Key Count, | ||
| //# and Encrypted Data Key Entries. | ||
|
|
||
| // Encrypted Data Key Count | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# The length of the serialized encrypted data key count MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# The encrypted data key count MUST be interpreted as a UInt16. | ||
| let count = read_u16(r, raw)?; | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# This value MUST be greater than 0. | ||
| if count == 0 { | ||
| return ser_err("Encrypted data key count must be greater than 0"); | ||
| } | ||
|
|
||
| if let Some(max_edks) = max_edks | ||
| && usize::from(count) > max_edks.get() | ||
| { | ||
| //= spec/data-format/message-header.md#encrypted-data-key-count | ||
| //# This value MUST be less than or equal to the [maximum number of encrypted data keys](../client-apis/client.md#maximum-number-of-encrypted-data-keys) if the maximum number is configured. | ||
| // | ||
| //= spec/client-apis/decrypt.md#v2-header-deserialization | ||
| //# If the number of [encrypted data keys](../framework/structures.md#encrypted-data-keys) | ||
| //# deserialized from the [message header](../data-format/message-header.md) | ||
| //# is greater than the [maximum number of encrypted data keys](client.md#maximum-number-of-encrypted-data-keys) configured in the [client](client.md), | ||
| //# then as soon as that can be determined during deserializing | ||
| //# decrypt MUST process no more bytes and yield an error. | ||
| return ser_err("Ciphertext encrypted data keys exceed maximum encrypted data keys limit"); | ||
| } | ||
|
|
||
| // Encrypted Data Key Entries | ||
|
|
||
| let mut edks = Vec::with_capacity(usize::from(count)); | ||
|
josecorella marked this conversation as resolved.
|
||
| for _ in 0..count { | ||
| edks.push(read_edk(r, raw)?); | ||
| } | ||
| Ok(edks) | ||
| } | ||
|
|
||
| pub(crate) fn read_edk( | ||
| r: &mut dyn SafeRead, | ||
| raw: &mut dyn SafeWrite, | ||
| ) -> Result<EncryptedDataKey, Error> { | ||
| //= spec/data-format/message-header.md#encrypted-data-key-entries | ||
| //# Each Encrypted Data Key Entry MUST consist of, in order, | ||
| //# Key Provider ID Length, | ||
| //# Key Provider ID, | ||
| //# Key Provider Information Length, | ||
| //# Key Provider Information, | ||
| //# Encrypted Data Key Length, | ||
| //# and Encrypted Data Key. | ||
|
|
||
| // Key Provider ID Length and Key Provider ID | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-id-length | ||
| //# The key provider ID length MUST be interpreted as a UInt16. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-id-length | ||
| //# The length of the serialized key provider ID length field MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-id | ||
| //= reason=read_str_u16 reads a u16 length then that many bytes, so the length field and data are equal by construction. | ||
| //# The length of the serialized key provider ID MUST be equal to the value of the [Key Provider ID Length](#key-provider-id-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-id | ||
| //# The key provider ID MUST be interpreted as UTF-8 encoded bytes. | ||
| let provider_id = read_str_u16(r, raw)?; | ||
|
|
||
| // Key Provider Information Length and Key Provider Information | ||
|
|
||
| //= spec/data-format/message-header.md#key-provider-information-length | ||
| //# The key provider information length MUST be interpreted as a UInt16. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-information-length | ||
| //# The length of the serialized key provider information length field MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-information | ||
| //= reason=read_seq_u16 reads a u16 length then that many bytes, so the length field and data are equal by construction. | ||
| //# The length of the serialized key provider information MUST be equal to the value of the [Key Provider Information Length](#key-provider-information-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#key-provider-information | ||
| //# The key provider information MUST be interpreted as bytes. | ||
| let provider_info = read_seq_u16(r, raw)?; | ||
|
|
||
| // Encrypted Data Key Length and Encrypted Data Key | ||
|
|
||
| //= spec/data-format/message-header.md#encrypted-data-key-length | ||
| //# The encrypted data key length MUST be interpreted as a UInt16. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key-length | ||
| //# The length of the serialized encrypted data key length field MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key | ||
| //= reason=read_seq_u16 reads a u16 length then that many bytes, so the length field and data are equal by construction. | ||
| //# The length of the serialized encrypted data key MUST be equal to the value of the [Encrypted Data Key Length](#encrypted-data-key-length) field. | ||
| // | ||
| //= spec/data-format/message-header.md#encrypted-data-key | ||
| //= type=implication | ||
| //# The encrypted data key MUST be interpreted as bytes. | ||
| let edk = read_seq_u16(r, raw)?; | ||
|
|
||
| Ok(EncryptedDataKey::new(provider_id, provider_info, edk)) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| //! Encryption context serialization for message header and AAD. | ||
|
|
||
| use super::serializable_types::ESDKCanonicalEncryptionContext; | ||
| use super::serialize_functions::{read_str_u16, read_u16, write_bytes, write_u16}; | ||
| use super::{Error, ser_err}; | ||
| use crate::types::{SafeRead, SafeWrite}; | ||
|
|
||
| /// Read the header's AAD encryption context sub-section. | ||
| pub(crate) fn read_canonical_ec( | ||
| r: &mut dyn SafeRead, | ||
| raw: &mut dyn SafeWrite, | ||
| ) -> Result<ESDKCanonicalEncryptionContext, Error> { | ||
| // Empty EC: length 0, no further bytes. | ||
| let bytes = usize::from(read_u16(r, raw)?); | ||
| if bytes == 0 { | ||
| return Ok(Vec::new()); | ||
| } | ||
|
|
||
| // Count, then `count` (key, value) pairs. | ||
| let count = usize::from(read_u16(r, raw)?); | ||
| let mut result: ESDKCanonicalEncryptionContext = Vec::with_capacity(count); | ||
| for _ in 0..count { | ||
| let key = read_str_u16(r, raw)?; | ||
| let value = read_str_u16(r, raw)?; | ||
| result.push((key, value)); | ||
| } | ||
|
lucasmcdonald3 marked this conversation as resolved.
|
||
|
|
||
| Ok(result) | ||
| } | ||
|
|
||
| /// Write canonical EC bytes for signing/AES-GCM AAD; empty EC writes nothing. | ||
| pub(crate) fn write_empty_ec_or_write_aad( | ||
| w: &mut dyn SafeWrite, | ||
| data: &ESDKCanonicalEncryptionContext, | ||
| ) -> Result<(), Error> { | ||
| if data.is_empty() { | ||
| //= spec/data-format/message-header.md#key-value-pairs | ||
| //# When the [encryption context](../framework/structures.md#encryption-context) is empty, | ||
| //# this field MUST NOT be included in the [AAD](#aad). | ||
| Ok(()) | ||
| } else { | ||
| write_aad(w, data) | ||
| } | ||
| } | ||
|
|
||
| /// Serialized length of the key-value-pairs body in bytes. | ||
| fn get_length(data: &ESDKCanonicalEncryptionContext) -> usize { | ||
| let mut length = 0; | ||
| for pair in data { | ||
| // key_len(2) + key bytes + val_len(2) + val bytes. | ||
| // `.len()` on a String returns the number of UTF-8 bytes, which is what we serialize. | ||
| length += 2 + pair.0.len() + 2 + pair.1.len(); | ||
|
lucasmcdonald3 marked this conversation as resolved.
|
||
| } | ||
| length | ||
| } | ||
|
|
||
| /// Write the header's AAD EC sub-section: length + key-value pairs. | ||
| pub(crate) fn write_aad_section( | ||
| w: &mut dyn SafeWrite, | ||
| data: &ESDKCanonicalEncryptionContext, | ||
| ) -> Result<(), Error> { | ||
| if data.is_empty() { | ||
| //= spec/data-format/message-header.md#key-value-pairs-length | ||
| //# When the [encryption context](../framework/structures.md#encryption-context) is empty, the value of this field MUST be 0. | ||
| write_u16(w, 0)?; | ||
| return Ok(()); | ||
| } | ||
|
|
||
| //= spec/data-format/message-header.md#aad | ||
| //# The AAD MUST consist of, in order, | ||
| //# Key Value Pairs Length, | ||
| //# and Key Value Pairs. | ||
|
|
||
| // Key Value Pairs Length: covers the Key Value Pair Count field plus all pairs. | ||
|
|
||
| //= spec/data-format/message-header.md#key-value-pairs-length | ||
| //# The length of the serialized key value pairs length field MUST be 2 bytes. | ||
| let bytes = 2 + get_length(data); // 2 for the Key Value Pair Count UInt16. | ||
|
|
||
| //= spec/data-format/message-header.md#key-value-pairs-length | ||
| //# The length of the serialized key value pairs length field MUST be 2 bytes. | ||
| // | ||
| //= spec/data-format/message-header.md#key-value-pairs-length | ||
| //# The key value pairs length MUST be interpreted as a UInt16. | ||
| let Ok(bytes_u16) = u16::try_from(bytes) else { | ||
| return ser_err("Encryption context key value pair length value is too large for u16"); | ||
| }; | ||
| write_u16(w, bytes_u16)?; | ||
|
|
||
| // Key Value Pairs | ||
|
|
||
| write_aad(w, data) | ||
| } | ||
|
|
||
| /// Write the key-value-pairs body: count, then (key, value) pairs. | ||
| pub(crate) fn write_aad( | ||
|
lucasmcdonald3 marked this conversation as resolved.
|
||
| w: &mut dyn SafeWrite, | ||
| data: &ESDKCanonicalEncryptionContext, | ||
| ) -> Result<(), Error> { | ||
| // Count. | ||
| let Ok(data_len) = u16::try_from(data.len()) else { | ||
| return ser_err("Encryption context key value pair count is too large for u16"); | ||
| }; | ||
| write_u16(w, data_len)?; | ||
|
|
||
| for pair in data { | ||
| //= spec/data-format/message-header.md#key-value-pairs | ||
| //# The encryption context key-value pairs MUST be serialized according to its [specification for serialization](../framework/structures.md#serialization). | ||
|
|
||
| // Key: length + UTF-8 bytes. | ||
| let Ok(key_len) = u16::try_from(pair.0.len()) else { | ||
| return ser_err("Encryption context key length is too large for u16"); | ||
| }; | ||
| write_u16(w, key_len)?; | ||
| write_bytes(w, pair.0.as_bytes())?; | ||
|
|
||
| // Value: length + UTF-8 bytes. | ||
| let Ok(val_len) = u16::try_from(pair.1.len()) else { | ||
| return ser_err("Encryption context value length is too large for u16"); | ||
| }; | ||
| write_u16(w, val_len)?; | ||
| write_bytes(w, pair.1.as_bytes())?; | ||
| } | ||
| Ok(()) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.