Skip to content

Inconsistent chain validation on Windows / Linux which results to 2 failed unit tests on Linux #293

@dbalikhin

Description

@dbalikhin

It works on Windows, but on Linux there is a different behavior that seems actually normal, see below.

Failed tests:

  • CryptoUtilsTests.TestValidateTrustChainSubAnchor() line 66
  • AuthenticatorAttestationResponse.VerifyAsync(CredentialCreateOptions originalOptions, Fido2Configuration config, IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser, IMetadataService metadataService, Byte[] requestTokenBindingId, CancellationToken cancellationToken) line 187
    Fido2Tests.TestInvalidU2FAttestationASync() line 624

Message: 
Fido2NetLib.Fido2VerificationException : Invalid certificate chain

From:

if (chain.Build(trustPath[0]))
{
// if the chain validates, make sure one of the attestation root certificates is one of the chain elements
foreach (X509Certificate2? attestationRootCertificate in attestationRootCertificates)
{
// skip the first element, as that is the attestation cert
if (chain.ChainElements
.Cast<X509ChainElement>()
.Skip(1)
.Any(x => x.Certificate.Thumbprint == attestationRootCertificate.Thumbprint))
return true;
}

What happens in ValidateTrustChain:

  1.         // try to build a chain with what we've got
         if (chain.Build(trustPath[0]))
    

We disable revocation flag and allow unknown CA

On Windows we will get success + Status Untrusted Root (A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.) which is expected.
On Linux we will get success + Status Partial Chain (Unable to find local issuer)

  1.             // now, verify chain again with all checks turned on
             if (chain.Build(trustPath[0]))
    

This one will succeed on Windows but fail on Linux. The required item won't be added to CustomTrustStore on Linux, because "chain.ChainElements[^1].Certificate" doesn't contain it.

So we will have something like this:

        if (chain.Build(trustPath[0])) // first chain build
        {
            if (chain.ChainStatus[0].Status == X509ChainStatusFlags.UntrustedRoot)
            {
                // if that validated, we should have a root for this chain now, add it to the custom trust store
                chain.ChainPolicy.CustomTrustStore.Clear();
                chain.ChainPolicy.CustomTrustStore.Add(chain.ChainElements[^1].Certificate);
                
            }
            else if (chain.ChainStatus[0].Status == X509ChainStatusFlags.PartialChain)
            {
                ???  
                Chain is validated but we won't have a root of this chain. 
            }

..
}

Any ideas on how to handle PartialChain on Linux then?

PartialChain result returned by OpenSSL (which is used under the hood on Linux).

Related discussions:
dotnet/runtime#49615 (comment)

dotnet/runtime#49615
dotnet/dotnet-api-docs#6660
dotnet/runtime#29164
dotnet/runtime#28314

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions