Skip to content

[keyring] expose keyring trace in response #223

Closed
@mattsb42-aws

Description

@mattsb42-aws

As I was writing up the keyring examples for #221, I realized that while the keyring trace is exposed in the keyring results, it is not exposed in the actual encrypt/decrypt results.

There are two places where we need to expose this:

  • Streaming APIs

We can do this simply by adding the keyring trace as an attribute on the stream object.

  • One-step APIs

This one is a bit more complicated. Because we currently return (bytes, MessageHeader) rather than a return object, our options are limited. There are two ways I can think to approach this:

  1. Add a keyring_trace attribute to MessageHeader. This is the simplest option, and the least likely to break people, but is a bit messy because the header doesn't really contain the keyring trace.
  2. Change the one-step APIs to return a results object that is structured like the one-step return values from the other ESDK implementations but also quacks like a two-member sequence. This would let us be more flexible in the future when adding other return data but would also let us avoid breaking existing users who expect a two-member tuple. [PREFERRED]

One-step APIs

The current interface of the one-step APIs is that they return a 2-member tuple containing the result bytes (plaintext for decrypt and encrypted message for encrypt) and the MessageHeader instance for that message.

https://aws-encryption-sdk-python.readthedocs.io/en/latest/generated/aws_encryption_sdk.html#aws_encryption_sdk.encrypt

my_ciphertext, encryptor_header = aws_encryption_sdk.encrypt(
    source=my_plaintext,
    keyring=my_keyring,
)

Option 1 : Modify MessageHeader

Pros:

  • This would be the simplest change.
  • Basically guaranteed to not break anyone.

Cons:

  • This would mix unrelated concepts. The message header does not include the keyring trace and adding it to that structure would confuse concepts and relationships.
  • If we ever need to add something else to the output, we would find ourselves in a similar situation again, having to put even more unrelated stuff into MessageHeader.
my_ciphertext, encryptor_header = aws_encryption_sdk.encrypt(
    source=my_plaintext,
    keyring=my_keyring,
)
encryptor_header.keyring_trace

Option 2 : Result object that quacks like a 2-member tuple [PREFERRED]

# Legacy interaction: compatible but no access to keyring trace
my_ciphertext, encryptor_header = aws_encryption_sdk.encrypt(
    source=my_plaintext,
    keyring=my_keyring,
)

# New interaction: provides access to keyring trace
encryption_result = aws_encryption_sdk.encrypt(
    source=my_plaintext,
    keyring=my_keyring,
)
# ciphertext
encryption_result.result
# header
encryption_result.header
# keyring trace
encryption_result.keyring_trace

Pros:

  • This would let us expose the keyring trace without combining unrelated concepts.
  • If we need to add another output value in the future, this gives us a simple and consistent way to do it.
  • This is very unlikely to break any customers. We can make the output value behave like a 2-member tuple so that anyone interacting with it as if it were a tuple will not be surprised.
  • This would let us align the Python client's one-step interface with the one-step interfaces in the other language implementations.

Cons:

  • If anyone is relying on the output value actually being an instance of the tuple class, this would break. Relying on implicit types in this way is generally considered an antipattern.

Discarded options

  • Output a 3-member tuple (bytes, MessageHeader, Sequence[KeyringTrace]). This is a non-starter because the most common way that we expect people to be using these APIs is to unpack the output values on call. Adding another member to the output tuple would result in an error when they attempt to unpack just two members from a three-member tuple.
>>> a,b  = (1,2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
  • Subclass tuple for result object. Subclassing built-in classes is generally considered an anti-pattern. Additionally, if we were to add access to the stored values by attribute names, which would be the best way to expose these values while also not breaking compatibility with the previous output value, we would end up with something that says that it is a tuple but behaves very differently than an actual tuple.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions