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

Impracticalities when automating versioning #1

Closed
nstilt1 opened this issue Mar 9, 2024 · 1 comment · Fixed by #7
Closed

Impracticalities when automating versioning #1

nstilt1 opened this issue Mar 9, 2024 · 1 comment · Fixed by #7
Assignees
Labels
enhancement New feature or request question Further information is requested

Comments

@nstilt1
Copy link
Owner

nstilt1 commented Mar 9, 2024

It is impractical to automate versioning, updating EPOCH times, and rotating HMAC keys with the current state of this library. The following challenges are easy to address:

  • changing BinaryId::VERSION and EPOCH from constant parameters to an argument in BinaryId::generate() and every other method that uses it

Example Implementation

This little example would depend on the above change, but it might be a little impractical. It also might be better to use a seeded CSPRNG as the core and use the version number in the RNG's nonce.

pub struct MasterKey<K: CryptoKeyGenerator, const VERSION_LIFESPAN: u64, const MASTER_EPOCH: u64> {
    core: K,
    current_version: u16,
    current_epoch: u64,
    // the current version's key generator, primarily used for generating new IDs
    current_keygenerator: K
}

fn now() -> u64 {
    let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
}

impl<...> MasterKey<...> {
    fn get_current_version() -> u16 {
        (now() - MASTER_EPOCH) / VERSION_LIFESPAN
    }
    fn get_version_epoch(version: u16) -> u64 {
        MASTER_EPOCH + VERSION_LIFESPAN * version as u64
    }
    
    pub fn new(hmac_key: &[u8], app_id: &[u8]) -> Self {
        let version = Self::get_current_version();
        let current_epoch = get_version_epoch(version);
        let core: K::new(hmac_key, app_id);
        Self {
            core,
            current_version: version,
            current_epoch,
            // this method does not exist as of now, and it probably won't exist
            current_generator: core.generate_key_generator(version, current_epoch)
        }
    }

    /// using the current generator to make new IDs
    pub fn generate_some_id(&self) -> Id {
        self.current_generator.generate_some_id(...);
    }
    ...
}

While it may be in the realm of being possible, it would add an extra argument when using KeyGenerator to generate IDs directly, and it would increase the amount of computations required to generate a single ID of an arbitrary version, whether it is the current version or a version less than the current version.

It might be a bit inefficient when a given MasterKey is required to generate and validate IDs from different versions automatically because it does not seem simple to store non-current-version KeyGenerators without using alloc, which might limit the number of times the program needs to regenerate a Key Generator in a single session.

Then... let's say 5 versions down, your main signature algorithm or your hash function is found to have a critical vulnerability, and you need to switch either the signature algorithm or the hash function, or both. You would need a way to tell MasterKey that for versions 4 and below, it needs to use X algorithm, and use Y algorithm if the version is greater than 4. Switching signature algorithms would be a little bit easier since those key IDs can expire, but switching hash algorithms in the HMAC/HKDF would be a bit more challenging.

I'm not saying that the current state of this crate is ideal, but I am saying that completely automating crypto-system changes might be infeasible. This really depends upon your own threat model, and maybe some things are just meant to be a little bit more manual.

My question is:
Is there a good way to automate HMAC key rotations using a regular version argument that would make it reasonable to change VERSION from a constant parameter to a non-const?

@nstilt1 nstilt1 added enhancement New feature or request question Further information is requested labels Mar 9, 2024
@nstilt1 nstilt1 self-assigned this Mar 20, 2024
@nstilt1
Copy link
Owner Author

nstilt1 commented Mar 20, 2024

This example implementation is a bit complex when a single instance of a Generator of sorts needs to generate IDs and keys from multiple versions. I will probably simplify the process to use a CSPRNG (likely ChaCha8) to output a "version nonce" that correlates to the version. This nonce will be used when generating MACs for the IDs, as well as for the private keys. It will allow for only one Mac instance to be active and only one Hkdf instance to be active. I have not decided on how long the nonce will be, but the OutputSize seems reasonable, but so does the length of ChaCha's output. The only problem with ChaCha's output size is that it varies by the backend. I will likely include this as a generic argument of type ArraySize.

nstilt1 added a commit that referenced this issue Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant