Skip to content

Conversation

randombit
Copy link
Contributor

Changes include

  • DerivedKeyMaterial is, instead of being just the raw VetKey treated as an HKDF key, is first hashed. This prevents working backwards from a DerivedKeyMaterial to the original VetKey.
  • The user specified domain separator is prefixed with our own specific domain separator
  • The GCM ciphertexts now include an 8 byte header with a version field to allow easier transitions in the future
  • Key derivation takes into account both the user specified domain separator and the ciphertext version
  • Added support for associated data

These changes are not backwards compatible. Backwards compatability could be added in the future if required (eg if the header is unknown, attempt to decrypt using the old scheme - if it works, return the recovered plaintext). The hope is that this is not required.

@randombit randombit requested a review from a team as a code owner August 19, 2025 23:28
Copy link
Member

@fspreiss fspreiss left a comment

Choose a reason for hiding this comment

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

Thanks, @randombit!

@randombit
Copy link
Contributor Author

This is now ready for a new review

Copy link
Member

@fspreiss fspreiss left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the comments, @randombit, and for adding the respective changes also in Typescript.

assert_eq!(
dkm.decrypt_message(&ctext, domain_sep, aad).unwrap(),
test_message,
);
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to add a test ensuring that decrypting with a wrong aad fails? (Same for Typescript).

Copy link
Member

Choose a reason for hiding this comment

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

IICU, these are only formatting changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, yes. I think we might want to fix these in a different PR...

Copy link
Member

Choose a reason for hiding this comment

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

We should also include the changes of this PR in the CHANGELOGs (for both the TS and the Rust library).


static fromCryptoKey(cryptokey: CryptoKey): DerivedKeyMaterial {
return new DerivedKeyMaterial(cryptokey);
static async fromCryptoKey(raw: CryptoKey): Promise<DerivedKeyMaterial> {
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a breaking change.
My understanding is that if someone has stored the CryptoKey returned from DerivedKeyMaterial::getCryptoKey with the current library version in an IndexedDB, then passing that to DerivedKeyMaterial::fromCryptoKey with the new library version won't work.

Not sure yet what the best way is to fix it. Do we also have to make this backward-compatible?

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 should not be a breaking change, or at least was not intended to be. fromCryptoKey and getCryptoKey return the CryptoKey which wrapping the raw VetKey bytes. Then fromCryptoKey performs an additional HKDF

Copy link
Member

Choose a reason for hiding this comment

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

It is possible to store CryptoKey objects in an IndexedDB in web browsers (example from encrypted notes dapp). Consider a dapp that (for whatever reason) stores the CryptoKeys returned by @dfinity/vetkeys -0.4.0's DerivedKeyMaterial::getCryptoKey in an IndexedDB.

If that dapp is now updated to a version that contains this change here, IIUC, importing the CryptoKeys (that are fetched from the IndexedDB) using the new DerivedKeyMaterial::fromCryptoKey would fail in line 590 with an InvalidAccessError because the raw key doesn't allow the deriveBits usage because the old library version didn't set that usage.

Copy link
Member

Choose a reason for hiding this comment

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

Thinking about this more, even if importing CryptoKeys that were exported with an old library version fails, it doesn't seem strictly necessary to make this backwards-compatible because developers work around this on their end by wiping and re-creating their (e.g, IndexedDB) cache. This would require re-fetching the underlying vetKey, but that seems reasonable. So maybe all we need here is a brief explanation in the changelog.

* The CryptoKey is not exportable
*/
async deriveAesGcmCryptoKey(
private async deriveAesGcmCryptoKey(
Copy link
Member

Choose a reason for hiding this comment

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

If we make this private, we should mention it in the CHANGELOG as breaking change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants