Skip to content
brycx edited this page Jan 19, 2022 · 8 revisions

This section explains some design related topics of Orion and how Orion is built.

Types

The types that Orion uses have mostly been based upon how sodiumoxide and Mundane use types.

Types are used extensively in the high-level API and this section gives an overview of how a type that could hold sensitive data might be implemented.

Opaque types

Orion uses the same approach to opaque types as those described by Mundane. Types are represented through a struct, where data is kept in a private field to limit access.

Specifically, in the high-level interface, the type SecretKey has a private field named value, which is only accessible from the methods and traits that SecretKey implements.

Traits

Several traits are important to implement when a type is holding sensitive data or otherwise needs extra protection, to avoid a user accidentally misusing it. These are the traits that the SecretKey in the high-level API implements:

  • Drop: This trait is the SecretKey's destructor. As soon as it goes out of scope, Drop zeroes out the sensitive data located in the field value of SecretKey. Relevant information on this in the Security section.

  • PartialEq: This trait is implemented for SecretKey to ensure that, whenever SecretKey is compared with another object of that type, the comparison will happen in constant-time. As such, if someone were to unknowingly try to insecurely compare, for example, two authentication tags: A == B, this would not result in a timing vulnerability. This is mainly implemented for misuse-resistance, as most modules already provide a secure verification function.

  • Debug: This trait is implemented in a way that makes it much harder for sensitive data in SecretKey to be leaked into logs. Should someone try to print the data contained in SecretKey, it would display SecretKey {***OMITTED***} instead of the actual data in the value field.

  • Default: The Default implementation lets a user initialize an object of type SecretKey by calling let key = SecretKey::default();. This will use a CSPRNG to generate a SecretKey of a pre-defined length, which in this case is 256 bits. This is only provided to make it as easy as possible, for the user to use SecretKey securely.

Functions and interfaces

The functions presented here are what aim to give the minimum amount of access to SecretKey, while still offering all the functionality one might need.

These are the functions that the SecretKey in the high-level API implements:

  • from_slice(): This lets the user initialize a SecretKey object from a slice passed to this function. Orion does not zero out the source slice, and a user of Orion would have to take care of that themselves.

  • unprotected_as_bytes(): This function returns the data in the value field of SecretKey as a byte slice. It is called unprotected because a user could copy the returned byte slice and save it elsewhere, without any of the protections that SecretKey offers. The documentation warns about the use of this and the name has been selected to hopefully nudge users towards not using this function, unless strictly necessary.

  • len(): A simple function that returns the length of the data contained in the value field of SecretKey.

  • generate(): This lets a user initialize a SecretKey object by generating it using a CSPRNG with a provided length. This is offered in case users need longer keys than the default 256 bit.

Errors

Orion's approach to error-handling has been taken directly from ring. By using an opaque error type we avoid leaking sensitive information as much as possible.

Most of the time an error is returned, the cause of this will also be rather obvious to the user. Situations like trying to finalize a streaming state twice without resetting it first, using invalid key sizes, etc. This is another reason as to why a single error type approach is used.

Clone this wiki locally