Skip to content

Compare asymmetric keys by value through the base interface - #204

Merged
Xor-el merged 1 commit into
masterfrom
key-parameter-equals
Sep 27, 2026
Merged

Xor-el merged 1 commit into
masterfrom
key-parameter-equals

Conversation

@Xor-el

@Xor-el Xor-el commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Summary

TAsymmetricKeyParameter.Equals (the base IAsymmetricKeyParameter.Equals) compared only the
public/private flag: Result := FPrivateKey = AOther.PrivateKey. So any two public keys — or any two
private keys — held as the base IAsymmetricKeyParameter compared equal regardless of their key
material
. Callers that only have the base interface (chain/credential code comparing a leaf's key
against a candidate) had no correct value comparison available; the real value equality lived on the
family sub-interfaces (IRsaKeyParameters.Equals, IECPublicKeyParameters.Equals, …) as typed
overloads, unreachable without first knowing the concrete family.

Change

The base Equals now routes through a protected virtual SameKey hook that every concrete key type
overrides:

  • Classical families (RSA, EC, Ed25519/Ed448, X25519/X448, DH, DSA, BIP-340) dispatch to their
    existing typed Equals after a family Supports check — so a key of another family is simply not
    equal.
  • PQ families (ML-DSA, ML-KEM, SLH-DSA) compare the parameter set (SameText on the parameters'
    name — nil-safe, and distinguishes same-length sets such as SLH-DSA sets sharing n, or ML-DSA
    pure vs pre-hash) plus a constant-time compare of the encoded key.
  • The base default is fail-closed identity, so an un-overridden key type is equal only to itself.

Equals returns False for nil and never raises: DH and DSA additionally tolerate nil domain
parameters (consistent with their existing GetHashCode nil-guard).

SameKey is a protected hook rather than an override of Equals because a virtual override of a
method that also has typed reintroduce; overload siblings does not compile cleanly across both
supported compilers; the hook keeps a single interface-visible Equals while letting each family
supply the comparison.

Tests

Added TestBaseInterfaceEqualsComparesValue (RSA, via the base IAsymmetricKeyParameter): equal key
material compares equal, differing material does not (a flag-only check would wrongly say equal), a
key equals itself, and a key does not equal nil.

Full suite green on FPC x86_64, FPC i386, and Delphi Win32 (1577 tests, 0 failures on each).

TAsymmetricKeyParameter.Equals only compared the public/private flag, so any two
public keys (or any two private keys) reachable only as IAsymmetricKeyParameter
compared equal regardless of their key material. Value equality lived on the family
sub-interfaces as typed overloads, unreachable without knowing the concrete family.

Route the base Equals through a protected virtual SameKey hook that every concrete
key type overrides: classical families dispatch to their typed Equals, and the PQ
families compare parameter set plus a constant-time compare of the encoded key. The
base default is fail-closed identity, so an un-overridden type is equal only to
itself. DH and DSA tolerate nil domain parameters so Equals never raises.
@Xor-el
Xor-el merged commit 692644d into master Sep 27, 2026
28 checks passed
@Xor-el
Xor-el deleted the key-parameter-equals branch September 27, 2026 16:59
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.

1 participant