Add Nix flake for mciasctl and mciasgrpcctl
Vendor dependencies and expose control program binaries via nix build. Uses nixpkgs-unstable for Go 1.26 support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
260
vendor/github.com/go-webauthn/webauthn/protocol/attestation_androidkey.go
generated
vendored
Normal file
260
vendor/github.com/go-webauthn/webauthn/protocol/attestation_androidkey.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-webauthn/webauthn/metadata"
|
||||
"github.com/go-webauthn/webauthn/protocol/webauthncose"
|
||||
)
|
||||
|
||||
// attestationFormatValidationHandlerAndroidKey is the handler for the Android Key Attestation Statement Format.
|
||||
//
|
||||
// An Android key attestation statement consists simply of the Android attestation statement, which is a series of DER
|
||||
// encoded X.509 certificates. See the Android developer documentation. Its syntax is defined as follows:
|
||||
//
|
||||
// $$attStmtType //= (
|
||||
//
|
||||
// fmt: "android-key",
|
||||
// attStmt: androidStmtFormat
|
||||
// )
|
||||
//
|
||||
// androidStmtFormat = {
|
||||
// alg: COSEAlgorithmIdentifier,
|
||||
// sig: bytes,
|
||||
// x5c: [ credCert: bytes, * (caCert: bytes) ]
|
||||
// }
|
||||
//
|
||||
// Specification: §8.4. Android Key Attestation Statement Format
|
||||
//
|
||||
// See: https://www.w3.org/TR/webauthn/#sctn-android-key-attestation
|
||||
func attestationFormatValidationHandlerAndroidKey(att AttestationObject, clientDataHash []byte, _ metadata.Provider) (attestationType string, x5cs []any, err error) {
|
||||
var (
|
||||
alg int64
|
||||
sig []byte
|
||||
ok bool
|
||||
)
|
||||
|
||||
// Given the verification procedure inputs attStmt, authenticatorData and clientDataHash, the verification procedure is as follows:
|
||||
// §8.4.1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract
|
||||
// the contained fields.
|
||||
// Get the alg value - A COSEAlgorithmIdentifier containing the identifier of the algorithm
|
||||
// used to generate the attestation signature.
|
||||
if alg, ok = att.AttStatement[stmtAlgorithm].(int64); !ok {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Error retrieving alg value")
|
||||
}
|
||||
|
||||
// Get the sig value - A byte string containing the attestation signature.
|
||||
if sig, ok = att.AttStatement[stmtSignature].([]byte); !ok {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Error retrieving sig value")
|
||||
}
|
||||
|
||||
// §8.4.2. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash
|
||||
// using the public key in the first certificate in x5c with the algorithm specified in alg.
|
||||
var (
|
||||
x5c []any
|
||||
certs []*x509.Certificate
|
||||
)
|
||||
|
||||
if x5c, certs, err = attStatementParseX5CS(att.AttStatement, stmtX5C); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
return "", nil, ErrInvalidAttestation.WithDetails("No certificates in x5c")
|
||||
}
|
||||
|
||||
credCert := certs[0]
|
||||
|
||||
if _, err = attStatementCertChainVerify(certs, attAndroidKeyHardwareRootsCertPool, true, time.Now().Add(time.Hour*8760).UTC()); err != nil {
|
||||
return "", nil, ErrInvalidAttestation.WithDetails("Error validating x5c cert chain").WithError(err)
|
||||
}
|
||||
|
||||
signatureData := append(att.RawAuthData, clientDataHash...) //nolint:gocritic // This is intentional.
|
||||
|
||||
if sigAlg := webauthncose.SigAlgFromCOSEAlg(webauthncose.COSEAlgorithmIdentifier(alg)); sigAlg == x509.UnknownSignatureAlgorithm {
|
||||
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unsupported COSE alg: %d", alg))
|
||||
} else if err = credCert.CheckSignature(sigAlg, signatureData, sig); err != nil {
|
||||
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v", err)).WithError(err)
|
||||
}
|
||||
|
||||
// Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData.
|
||||
var attPublicKeyData webauthncose.EC2PublicKeyData
|
||||
if attPublicKeyData, err = verifyAttestationECDSAPublicKeyMatch(att, credCert); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
var valid bool
|
||||
if valid, err = attPublicKeyData.Verify(signatureData, sig); err != nil || !valid {
|
||||
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v", err)).WithError(err)
|
||||
}
|
||||
|
||||
// §8.4.3. Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
|
||||
// attCert.Extensions.
|
||||
// As noted in §8.4.1 (https://www.w3.org/TR/webauthn/#key-attstn-cert-requirements) the Android Key Attestation
|
||||
// certificate's android key attestation certificate extension data is identified by the OID
|
||||
// "1.3.6.1.4.1.11129.2.1.17".
|
||||
var attExtBytes []byte
|
||||
|
||||
for _, ext := range credCert.Extensions {
|
||||
if ext.Id.Equal(oidExtensionAndroidKeystore) {
|
||||
attExtBytes = ext.Value
|
||||
}
|
||||
}
|
||||
|
||||
if len(attExtBytes) == 0 {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions missing 1.3.6.1.4.1.11129.2.1.17")
|
||||
}
|
||||
|
||||
decoded := keyDescription{}
|
||||
|
||||
if _, err = asn1.Unmarshal(attExtBytes, &decoded); err != nil {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Unable to parse Android key attestation certificate extensions").WithError(err)
|
||||
}
|
||||
|
||||
// Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
|
||||
if !bytes.Equal(decoded.AttestationChallenge, clientDataHash) {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Attestation challenge not equal to clientDataHash")
|
||||
}
|
||||
|
||||
// The AuthorizationList.allApplications field is not present on either authorization list (softwareEnforced nor teeEnforced), since PublicKeyCredential MUST be scoped to the RP ID.
|
||||
if decoded.SoftwareEnforced.AllApplications != nil || decoded.TeeEnforced.AllApplications != nil {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains all applications field")
|
||||
}
|
||||
|
||||
// For the following, use only the teeEnforced authorization list if the RP wants to accept only keys from a trusted execution environment, otherwise use the union of teeEnforced and softwareEnforced.
|
||||
// The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED (which == 0).
|
||||
if decoded.SoftwareEnforced.Origin != KM_ORIGIN_GENERATED || decoded.TeeEnforced.Origin != KM_ORIGIN_GENERATED {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains authorization list with origin not equal KM_ORIGIN_GENERATED")
|
||||
}
|
||||
|
||||
// The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN (which == 2).
|
||||
if !contains(decoded.SoftwareEnforced.Purpose, KM_PURPOSE_SIGN) && !contains(decoded.TeeEnforced.Purpose, KM_PURPOSE_SIGN) {
|
||||
return "", nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains authorization list with purpose not equal KM_PURPOSE_SIGN")
|
||||
}
|
||||
|
||||
return string(metadata.BasicFull), x5c, err
|
||||
}
|
||||
|
||||
func contains(s []int, e int) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type keyDescription struct {
|
||||
AttestationVersion int
|
||||
AttestationSecurityLevel asn1.Enumerated
|
||||
KeymasterVersion int
|
||||
KeymasterSecurityLevel asn1.Enumerated
|
||||
AttestationChallenge []byte
|
||||
UniqueID []byte
|
||||
SoftwareEnforced authorizationList
|
||||
TeeEnforced authorizationList
|
||||
}
|
||||
|
||||
type authorizationList struct {
|
||||
Purpose []int `asn1:"tag:1,explicit,set,optional"`
|
||||
Algorithm int `asn1:"tag:2,explicit,optional"`
|
||||
KeySize int `asn1:"tag:3,explicit,optional"`
|
||||
Digest []int `asn1:"tag:5,explicit,set,optional"`
|
||||
Padding []int `asn1:"tag:6,explicit,set,optional"`
|
||||
EcCurve int `asn1:"tag:10,explicit,optional"`
|
||||
RsaPublicExponent int `asn1:"tag:200,explicit,optional"`
|
||||
RollbackResistance any `asn1:"tag:303,explicit,optional"`
|
||||
ActiveDateTime int `asn1:"tag:400,explicit,optional"`
|
||||
OriginationExpireDateTime int `asn1:"tag:401,explicit,optional"`
|
||||
UsageExpireDateTime int `asn1:"tag:402,explicit,optional"`
|
||||
NoAuthRequired any `asn1:"tag:503,explicit,optional"`
|
||||
UserAuthType int `asn1:"tag:504,explicit,optional"`
|
||||
AuthTimeout int `asn1:"tag:505,explicit,optional"`
|
||||
AllowWhileOnBody any `asn1:"tag:506,explicit,optional"`
|
||||
TrustedUserPresenceRequired any `asn1:"tag:507,explicit,optional"`
|
||||
TrustedConfirmationRequired any `asn1:"tag:508,explicit,optional"`
|
||||
UnlockedDeviceRequired any `asn1:"tag:509,explicit,optional"`
|
||||
AllApplications any `asn1:"tag:600,explicit,optional"`
|
||||
ApplicationID any `asn1:"tag:601,explicit,optional"`
|
||||
CreationDateTime int `asn1:"tag:701,explicit,optional"`
|
||||
Origin int `asn1:"tag:702,explicit,optional"`
|
||||
RootOfTrust rootOfTrust `asn1:"tag:704,explicit,optional"`
|
||||
OsVersion int `asn1:"tag:705,explicit,optional"`
|
||||
OsPatchLevel int `asn1:"tag:706,explicit,optional"`
|
||||
AttestationApplicationID []byte `asn1:"tag:709,explicit,optional"`
|
||||
AttestationIDBrand []byte `asn1:"tag:710,explicit,optional"`
|
||||
AttestationIDDevice []byte `asn1:"tag:711,explicit,optional"`
|
||||
AttestationIDProduct []byte `asn1:"tag:712,explicit,optional"`
|
||||
AttestationIDSerial []byte `asn1:"tag:713,explicit,optional"`
|
||||
AttestationIDImei []byte `asn1:"tag:714,explicit,optional"`
|
||||
AttestationIDMeid []byte `asn1:"tag:715,explicit,optional"`
|
||||
AttestationIDManufacturer []byte `asn1:"tag:716,explicit,optional"`
|
||||
AttestationIDModel []byte `asn1:"tag:717,explicit,optional"`
|
||||
VendorPatchLevel int `asn1:"tag:718,explicit,optional"`
|
||||
BootPatchLevel int `asn1:"tag:719,explicit,optional"`
|
||||
}
|
||||
|
||||
type rootOfTrust struct {
|
||||
verifiedBootKey []byte //nolint:unused
|
||||
deviceLocked bool //nolint:unused
|
||||
verifiedBootState verifiedBootState //nolint:unused
|
||||
verifiedBootHash []byte //nolint:unused
|
||||
}
|
||||
|
||||
type verifiedBootState int
|
||||
|
||||
const (
|
||||
Verified verifiedBootState = iota
|
||||
SelfSigned
|
||||
Unverified
|
||||
Failed
|
||||
)
|
||||
|
||||
const (
|
||||
// KM_ORIGIN_GENERATED means generated in keymaster. Should not exist outside the TEE.
|
||||
KM_ORIGIN_GENERATED = iota
|
||||
|
||||
// KM_ORIGIN_DERIVED means derived inside keymaster. Likely exists off-device.
|
||||
KM_ORIGIN_DERIVED
|
||||
|
||||
// KM_ORIGIN_IMPORTED means imported into keymaster. Existed as clear text in Android.
|
||||
KM_ORIGIN_IMPORTED
|
||||
|
||||
// KM_ORIGIN_UNKNOWN means keymaster did not record origin. This value can only be seen on keys in a keymaster0
|
||||
// implementation. The keymaster0 adapter uses this value to document the fact that it is unknown whether the key
|
||||
// was generated inside or imported into keymaster.
|
||||
KM_ORIGIN_UNKNOWN
|
||||
)
|
||||
|
||||
const (
|
||||
// KM_PURPOSE_ENCRYPT is usable with RSA, EC and AES keys.
|
||||
KM_PURPOSE_ENCRYPT = iota
|
||||
|
||||
// KM_PURPOSE_DECRYPT is usable with RSA, EC and AES keys.
|
||||
KM_PURPOSE_DECRYPT
|
||||
|
||||
// KM_PURPOSE_SIGN is usable with RSA, EC and HMAC keys.
|
||||
KM_PURPOSE_SIGN
|
||||
|
||||
// KM_PURPOSE_VERIFY is usable with RSA, EC and HMAC keys.
|
||||
KM_PURPOSE_VERIFY
|
||||
|
||||
// KM_PURPOSE_DERIVE_KEY is usable with EC keys.
|
||||
KM_PURPOSE_DERIVE_KEY
|
||||
|
||||
// KM_PURPOSE_WRAP is usable with wrapped keys.
|
||||
KM_PURPOSE_WRAP
|
||||
)
|
||||
|
||||
var (
|
||||
attAndroidKeyHardwareRootsCertPool *x509.CertPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterAttestationFormat(AttestationFormatAndroidKey, attestationFormatValidationHandlerAndroidKey)
|
||||
}
|
||||
Reference in New Issue
Block a user