@ -384,7 +384,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
prov := MustProvisionerFromContext ( ctx )
if ! prov . IsAttestationFormatEnabled ( ctx , provisioner . ACMEAttestationFormat ( format ) ) {
if format != "apple" && format != "step" && format != "tpm" {
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "unsupported attestation object format %q" , format ) . WithAdditionalErrorDetail ( ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "unsupported attestation object format %q" , format ) )
}
return storeError ( ctx , db , ch , true ,
@ -409,7 +409,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
if len ( data . Nonce ) != 0 {
sum := sha256 . Sum256 ( [ ] byte ( ch . Token ) )
if subtle . ConstantTimeCompare ( data . Nonce , sum [ : ] ) != 1 {
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "challenge token does not match" ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "challenge token does not match" ) )
}
}
@ -423,7 +423,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
Identifier { Type : "permanent-identifier" , Value : ch . Value } ,
"challenge identifier %q doesn't match any of the attested hardware identifiers %q" , ch . Value , [ ] string { data . UDID , data . SerialNumber } ,
)
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . WithAdditionalErrorDetail ( ) . AddSubproblems ( subproblem ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . AddSubproblems ( subproblem ) )
}
// Update attestation key fingerprint to compare against the CSR
@ -451,7 +451,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
Identifier { Type : "permanent-identifier" , Value : ch . Value } ,
"challenge identifier %q doesn't match the attested hardware identifier %q" , ch . Value , data . SerialNumber ,
)
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . WithAdditionalErrorDetail ( ) . AddSubproblems ( subproblem ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . AddSubproblems ( subproblem ) )
}
// Update attestation key fingerprint to compare against the CSR
@ -481,13 +481,13 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
Identifier { Type : "permanent-identifier" , Value : ch . Value } ,
"challenge identifier %q doesn't match any of the attested hardware identifiers %q" , ch . Value , data . PermanentIdentifiers ,
)
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . WithAdditionalErrorDetail ( ) . AddSubproblems ( subproblem ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "permanent identifier does not match" ) . AddSubproblems ( subproblem ) )
}
// Update attestation key fingerprint to compare against the CSR
az . Fingerprint = data . Fingerprint
default :
return storeError ( ctx , db , ch , true , New Error( ErrorBadAttestationStatementType , "unsupported attestation object format %q" , format ) . WithAdditionalErrorDetail ( ) )
return storeError ( ctx , db , ch , true , New Detailed Error( ErrorBadAttestationStatementType , "unsupported attestation object format %q" , format ) )
}
// Update and store the challenge.
@ -533,38 +533,38 @@ const (
func doTPMAttestationFormat ( _ context . Context , prov Provisioner , ch * Challenge , jwk * jose . JSONWebKey , att * attestationObject ) ( * tpmAttestationData , error ) {
ver , ok := att . AttStatement [ "ver" ] . ( string )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "ver not present" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "ver not present" )
}
if ver != "2.0" {
return nil , New Error( ErrorBadAttestationStatementType , "version %q is not supported" , ver ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "version %q is not supported" , ver )
}
x5c , ok := att . AttStatement [ "x5c" ] . ( [ ] interface { } )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c not present" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c not present" )
}
if len ( x5c ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is empty" )
}
akCertBytes , ok := x5c [ 0 ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
akCert , err := x509 . ParseCertificate ( akCertBytes )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates := x509 . NewCertPool ( )
for _ , v := range x5c [ 1 : ] {
intCertBytes , vok := v . ( [ ] byte )
if ! vok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
intCert , err := x509 . ParseCertificate ( intCertBytes )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates . AddCert ( intCert )
}
@ -602,19 +602,19 @@ func doTPMAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
KeyUsages : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageAny } ,
} )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is not valid" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is not valid" )
}
// validate additional AK certificate requirements
if err := validateAKCertificate ( akCert ) ; err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "AK certificate is not valid" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "AK certificate is not valid" )
}
// TODO(hs): implement revocation check; Verify() doesn't perform CRL check nor OCSP lookup.
sans , err := x509util . ParseSubjectAlternativeNames ( akCert )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "failed parsing AK certificate Subject Alternative Names" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "failed parsing AK certificate Subject Alternative Names" )
}
permanentIdentifiers := make ( [ ] string , len ( sans . PermanentIdentifiers ) )
@ -625,37 +625,37 @@ func doTPMAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
// extract and validate pubArea, sig, certInfo and alg properties from the request body
pubArea , ok := att . AttStatement [ "pubArea" ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "invalid pubArea in attestation statement" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "invalid pubArea in attestation statement" )
}
if len ( pubArea ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "pubArea is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "pubArea is empty" )
}
sig , ok := att . AttStatement [ "sig" ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "invalid sig in attestation statement" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "invalid sig in attestation statement" )
}
if len ( sig ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "sig is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "sig is empty" )
}
certInfo , ok := att . AttStatement [ "certInfo" ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "invalid certInfo in attestation statement" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "invalid certInfo in attestation statement" )
}
if len ( certInfo ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "certInfo is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "certInfo is empty" )
}
alg , ok := att . AttStatement [ "alg" ] . ( int64 )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "invalid alg in attestation statement" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "invalid alg in attestation statement" )
}
// only RS256 and ES256 are allowed
coseAlg := coseAlgorithmIdentifier ( alg )
if coseAlg != coseAlgRS256 && coseAlg != coseAlgES256 {
return nil , New Error( ErrorBadAttestationStatementType , "invalid alg %d in attestation statement" , alg ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "invalid alg %d in attestation statement" , alg )
}
// set the hash algorithm to use to SHA256
@ -673,13 +673,13 @@ func doTPMAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
Hash : hash ,
}
if err = certificationParameters . Verify ( verifyOpts ) ; err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "invalid certification parameters" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "invalid certification parameters" )
}
// decode the "certInfo" data. This won't fail, as it's also done as part of Verify().
tpmCertInfo , err := tpm2 . DecodeAttestationData ( certInfo )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "failed decoding attestation data" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "failed decoding attestation data" )
}
keyAuth , err := KeyAuthorization ( ch . Token , jwk )
@ -691,18 +691,18 @@ func doTPMAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
// verify the WebAuthn object contains the expect key authorization digest, which is carried
// within the encoded `certInfo` property of the attestation statement.
if subtle . ConstantTimeCompare ( hashedKeyAuth [ : ] , [ ] byte ( tpmCertInfo . ExtraData ) ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "key authorization invalid" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "key authorization invalid" )
}
// decode the (attested) public key and determine its fingerprint. This won't fail, as it's also done as part of Verify().
pub , err := tpm2 . DecodePublic ( pubArea )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "failed decoding pubArea" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "failed decoding pubArea" )
}
publicKey , err := pub . Key ( )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "failed getting public key" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "failed getting public key" )
}
data := & tpmAttestationData {
@ -848,30 +848,30 @@ func doAppleAttestationFormat(_ context.Context, prov Provisioner, _ *Challenge,
x5c , ok := att . AttStatement [ "x5c" ] . ( [ ] interface { } )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c not present" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c not present" )
}
if len ( x5c ) == 0 {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is empty" )
}
der , ok := x5c [ 0 ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
leaf , err := x509 . ParseCertificate ( der )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates := x509 . NewCertPool ( )
for _ , v := range x5c [ 1 : ] {
der , ok = v . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
cert , err := x509 . ParseCertificate ( der )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates . AddCert ( cert )
}
@ -882,7 +882,7 @@ func doAppleAttestationFormat(_ context.Context, prov Provisioner, _ *Challenge,
CurrentTime : time . Now ( ) . Truncate ( time . Second ) ,
KeyUsages : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageAny } ,
} ) ; err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is not valid" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is not valid" )
}
data := & appleAttestationData {
@ -954,28 +954,28 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
// Extract x5c and verify certificate
x5c , ok := att . AttStatement [ "x5c" ] . ( [ ] interface { } )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c not present" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c not present" )
}
if len ( x5c ) == 0 {
return nil , New Error( ErrorRejectedIdentifierType , "x5c is empty" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorRejectedIdentifierType , "x5c is empty" )
}
der , ok := x5c [ 0 ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
leaf , err := x509 . ParseCertificate ( der )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates := x509 . NewCertPool ( )
for _ , v := range x5c [ 1 : ] {
der , ok = v . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "x5c is malformed" )
}
cert , err := x509 . ParseCertificate ( der )
if err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is malformed" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is malformed" )
}
intermediates . AddCert ( cert )
}
@ -985,7 +985,7 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
CurrentTime : time . Now ( ) . Truncate ( time . Second ) ,
KeyUsages : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageAny } ,
} ) ; err != nil {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "x5c is not valid" ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "x5c is not valid" )
}
// Verify proof of possession of private key validating the key
@ -995,10 +995,10 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
var sig [ ] byte
csig , ok := att . AttStatement [ "sig" ] . ( [ ] byte )
if ! ok {
return nil , New Error( ErrorBadAttestationStatementType , "sig not present" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "sig not present" )
}
if err := cbor . Unmarshal ( csig , & sig ) ; err != nil {
return nil , New Error( ErrorBadAttestationStatementType , "sig is malformed" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "sig is malformed" )
}
keyAuth , err := KeyAuthorization ( ch . Token , jwk )
if err != nil {
@ -1008,23 +1008,23 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
switch pub := leaf . PublicKey . ( type ) {
case * ecdsa . PublicKey :
if pub . Curve != elliptic . P256 ( ) {
return nil , Wrap Error( ErrorBadAttestationStatementType , err , "unsupported elliptic curve %s" , pub . Curve ) . WithAdditionalErrorDetail ( )
return nil , Wrap Detailed Error( ErrorBadAttestationStatementType , err , "unsupported elliptic curve %s" , pub . Curve )
}
sum := sha256 . Sum256 ( [ ] byte ( keyAuth ) )
if ! ecdsa . VerifyASN1 ( pub , sum [ : ] , sig ) {
return nil , New Error( ErrorBadAttestationStatementType , "failed to validate signature" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "failed to validate signature" )
}
case * rsa . PublicKey :
sum := sha256 . Sum256 ( [ ] byte ( keyAuth ) )
if err := rsa . VerifyPKCS1v15 ( pub , crypto . SHA256 , sum [ : ] , sig ) ; err != nil {
return nil , New Error( ErrorBadAttestationStatementType , "failed to validate signature" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "failed to validate signature" )
}
case ed25519 . PublicKey :
if ! ed25519 . Verify ( pub , [ ] byte ( keyAuth ) , sig ) {
return nil , New Error( ErrorBadAttestationStatementType , "failed to validate signature" ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "failed to validate signature" )
}
default :
return nil , New Error( ErrorBadAttestationStatementType , "unsupported public key type %T" , pub ) . WithAdditionalErrorDetail ( )
return nil , New Detailed Error( ErrorBadAttestationStatementType , "unsupported public key type %T" , pub )
}
// Parse attestation data: