Skip to content

Commit 50add86

Browse files
committed
fix: android root CA error
1 parent 307a50a commit 50add86

2 files changed

Lines changed: 76 additions & 4 deletions

File tree

acme/challenge.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,7 +1481,8 @@ Eczm34A5FNijV3s0/f0UPl7zbJcTx6xwqMIRq6NCMEAwDwYDVR0TAQH/BAUwAwEB
14811481
/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFFIyuyz7RkOb3NaBqQ5lZuA0QepA
14821482
MAoGCCqGSM49BAMDA2gAMGUCMETfjPO/HwqReR2CS7p0ZWoD/LHs6hDi422opifH
14831483
EUaYLxwGlT9SLdjkVpz0UUOR5wIxAIoGyxGKRHVTpqpGRFiJtQEOOTp/+s1GcxeY
1484-
uR2zh/80lQyu9vAFCj6E4AXc+osmRg==`
1484+
uR2zh/80lQyu9vAFCj6E4AXc+osmRg==
1485+
-----END CERTIFICATE-----`
14851486

14861487
// OID for the Android attestation extension
14871488
// https://source.android.com/docs/security/features/keystore/attestation#id-attestation
@@ -1587,9 +1588,12 @@ func doAndroidKeyAttestionFormat(_ context.Context, prov Provisioner, ch *Challe
15871588
switch root.PublicKey.(type) {
15881589
case *rsa.PublicKey:
15891590
rsaRoot, err := pemutil.ParseCertificate([]byte(AndroidRootCARSA))
1591+
if err != nil {
1592+
return nil, WrapErrorISE(err, "error parsing root CA RSA")
1593+
}
15901594
oldRsaRoot, err := pemutil.ParseCertificate([]byte(OldAndroidRootCARSA))
15911595
if err != nil {
1592-
return nil, WrapErrorISE(err, "error parsing root ca")
1596+
return nil, WrapErrorISE(err, "error parsing old root CA RSA")
15931597
}
15941598
// 1. verify public key
15951599
if !root.PublicKey.(*rsa.PublicKey).Equal(rsaRoot.PublicKey) {
@@ -1602,7 +1606,7 @@ func doAndroidKeyAttestionFormat(_ context.Context, prov Provisioner, ch *Challe
16021606
if err != nil {
16031607
return nil, WrapErrorISE(err, "error parsing root ca")
16041608
}
1605-
if !root.PublicKey.(*rsa.PublicKey).Equal(ecdsaRoot.PublicKey) {
1609+
if !root.PublicKey.(*ecdsa.PublicKey).Equal(ecdsaRoot.PublicKey) {
16061610
return nil, NewDetailedError(ErrorBadAttestationStatementType, "root certificate not signed by Google")
16071611
}
16081612
attestationRoots.AddCert(ecdsaRoot)
@@ -1701,7 +1705,7 @@ func doAndroidKeyAttestionFormat(_ context.Context, prov Provisioner, ch *Challe
17011705

17021706
// validate challenge
17031707
if subtle.ConstantTimeCompare([]byte(keyAuth), data.Attestation.AttestationChallenge) != 1 {
1704-
return nil, NewDetailedError(ErrorBadAttestationStatementType, fmt.Sprintf("challenge mismatch; expected %q, got %q", keyAuth, string(data.Attestation.AttestationChallenge)))
1708+
return nil, NewDetailedError(ErrorBadAttestationStatementType, "challenge mismatch; expected %q, got %q", keyAuth, string(data.Attestation.AttestationChallenge))
17051709
}
17061710

17071711
return data, nil

acme/challenge_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5097,6 +5097,74 @@ func Test_deviceAttest01Validate(t *testing.T) {
50975097
}
50985098
}
50995099

5100+
func Test_doAndroidKeyAttestionFormat_noAttestationRoots(t *testing.T) {
5101+
// This test exercises the fallback path when no attestation roots
5102+
// are configured (the !attOk branch), verifying that:
5103+
// 1. The ECDSA root parsing and type assertion works correctly
5104+
// 2. Non-Google roots are properly rejected
5105+
ca, err := minica.New()
5106+
require.NoError(t, err)
5107+
5108+
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
5109+
require.NoError(t, err)
5110+
5111+
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
5112+
keyAuthSum := sha256.Sum256([]byte(keyAuth))
5113+
sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256)
5114+
require.NoError(t, err)
5115+
5116+
atts := attestation.KeyDescription{
5117+
AttestationVersion: 300,
5118+
AttestationSecurityLevel: 1,
5119+
AttestationChallenge: sig,
5120+
TeeEnforced: attestation.AuthorizationList{
5121+
AttestationIdSerial: []byte("serial-number"),
5122+
},
5123+
}
5124+
attestByte, err := attestation.CreateKeyDescription(&atts)
5125+
require.NoError(t, err)
5126+
5127+
leaf, err := ca.Sign(&x509.Certificate{
5128+
Subject: pkix.Name{CommonName: "attestation cert"},
5129+
PublicKey: signer.Public(),
5130+
ExtraExtensions: []pkix.Extension{
5131+
{Id: oidAndroidAttestation, Value: attestByte},
5132+
},
5133+
})
5134+
require.NoError(t, err)
5135+
5136+
att := &attestationObject{
5137+
Format: "android-key",
5138+
AttStatement: map[string]any{
5139+
"x5c": []any{leaf.Raw, ca.Intermediate.Raw, ca.Root.Raw},
5140+
},
5141+
}
5142+
5143+
// Create provisioner without attestation roots
5144+
prov := &provisioner.ACME{
5145+
Type: "ACME",
5146+
Name: "acme",
5147+
Challenges: []provisioner.ACMEChallenge{provisioner.DEVICE_ATTEST_01},
5148+
}
5149+
require.NoError(t, prov.Init(provisioner.Config{
5150+
Claims: config.GlobalProvisionerClaims,
5151+
}))
5152+
5153+
ch := &Challenge{
5154+
ID: "chID",
5155+
Token: "nonce",
5156+
Type: "device-attest-01",
5157+
Value: "serial-number",
5158+
}
5159+
5160+
_, err = doAndroidKeyAttestionFormat(context.Background(), prov, ch, jwk, att)
5161+
require.Error(t, err)
5162+
5163+
var acmeErr *Error
5164+
require.ErrorAs(t, err, &acmeErr)
5165+
assert.Contains(t, acmeErr.Error(), "root certificate not signed by Google")
5166+
}
5167+
51005168
var (
51015169
oidTPMManufacturer = asn1.ObjectIdentifier{2, 23, 133, 2, 1}
51025170
oidTPMModel = asn1.ObjectIdentifier{2, 23, 133, 2, 2}

0 commit comments

Comments
 (0)