Security: Cross-validate KeyMaterialType in NewHandleWithNoSecrets to prevent spoofing - #59
Conversation
…ent spoofing NewHandleWithNoSecrets/ReadWithNoSecrets trust the serialized KeyData.key_material_type enum to decide whether a keyset contains secret material. This field is attacker-controlled in the input bytes. An attacker can serialize a symmetric key (e.g. HMAC) with key_material_type set to ASYMMETRIC_PUBLIC. The hasSecrets() check passes because it only inspects the spoofed enum. The HMAC parser then successfully parses the key, producing a working MAC primitive with attacker-known key material — enabling tag forgery. Fix: after parsing, re-serialize each key through the trusted parser and verify the actual KeyMaterialType. Also check if parsed keys implement the privateKey interface. If any key's trusted type indicates secret material, reject the keyset. Fixes tink-crypto#43
|
Hi @morambro @juergw — this PR fixes the KeyMaterialType spoofing vulnerability reported in #43 (originally reported through g.co/vulnz and redirected here by Google VRP). The core issue is that hasSecrets() trusts the serialized key_material_type enum, which is attacker-controlled. A symmetric key (e.g., HMAC) serialized with key_material_type=ASYMMETRIC_PUBLIC passes the no-secrets check, and the HMAC parser happily parses it into a working MAC primitive with attacker-known key material. My fix adds a post-parse cross-validation step: after parsing via newKeysetHandleFromProto, each key is re-serialized through the trusted serializer and its actual KeyMaterialType is checked. If any key turns out to be SYMMETRIC or ASYMMETRIC_PRIVATE despite the input claiming otherwise, the keyset is rejected. This is a defense-in-depth approach that works regardless of whether individual key parsers enforce type consistency. Happy to adjust the approach if you prefer a different strategy! |
|
Hi Ashutosh0x
Overall, I am not sure what the attack is here: what are the attacker's capabilities? What's the attacker goal? From your description, it seems the attacker has both control on the keyset (to change
I think I'd rather fix each |
Summary
Fix
NewHandleWithNoSecrets/ReadWithNoSecretsto cross-validate the actualKeyMaterialTypeafter parsing, preventing attackers from importing symmetric secret key material by spoofingkey_material_typetoASYMMETRIC_PUBLIC.Vulnerability (Issue #43)
hasSecrets()trusts the serializedKeyData.key_material_typeenum to decide whether a keyset contains secret material. This field is attacker-controlled in the input bytes.Attack path:
tinkpb.Keysetcontaining an HMAC key with attacker-chosen key bytesKeyData.key_material_typetoASYMMETRIC_PUBLICReadWithNoSecretsorNewHandleWithNoSecretshasSecrets()accepts because it only checks the spoofed enummac.New(handle)returns a working primitiveFix
After parsing keys via
newKeysetHandleFromProto, re-serialize each parsed key through the trusted serializer and verify the actualKeyMaterialType. Also check if any parsed key implements theprivateKeyinterface. If any key's trusted type indicates secret material, reject the keyset with an error.This is a defense-in-depth approach — the trusted parser knows the real key type regardless of what the attacker claimed in the serialized enum.
Impact
Callers using
ReadWithNoSecrets/NewHandleWithNoSecretsas a trust boundary for importing externally-provided "public" keysets can be tricked into accepting symmetric key material. For HMAC, this enables tag forgery for any authentication signal derived from that MAC.Fixes #43