Use `NewDetailedError` instead

pull/1495/head
Herman Slatman 9 months ago
parent afdd8d3786
commit c952e9fc9d
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

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

@ -3532,7 +3532,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "12345678", updch.Value) assert.Equal(t, "12345678", updch.Value)
err := NewError(ErrorBadAttestationStatementType, "x5c not present").WithAdditionalErrorDetail() err := NewDetailedError(ErrorBadAttestationStatementType, "x5c not present")
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)
@ -3579,7 +3579,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "serial-number", updch.Value) assert.Equal(t, "serial-number", updch.Value)
err := NewError(ErrorBadAttestationStatementType, "challenge token does not match") err := NewDetailedError(ErrorBadAttestationStatementType, "challenge token does not match")
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)
@ -3630,7 +3630,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
Identifier{Type: "permanent-identifier", Value: "non-matching-value"}, Identifier{Type: "permanent-identifier", Value: "non-matching-value"},
`challenge identifier "non-matching-value" doesn't match any of the attested hardware identifiers ["udid" "serial-number"]`, `challenge identifier "non-matching-value" doesn't match any of the attested hardware identifiers ["udid" "serial-number"]`,
) )
err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match").WithAdditionalErrorDetail().AddSubproblems(subproblem) err := NewDetailedError(ErrorBadAttestationStatementType, "permanent identifier does not match").AddSubproblems(subproblem)
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)
@ -3703,7 +3703,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "12345678", updch.Value) assert.Equal(t, "12345678", updch.Value)
err := NewError(ErrorBadAttestationStatementType, "x5c not present").WithAdditionalErrorDetail() err := NewDetailedError(ErrorBadAttestationStatementType, "x5c not present")
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)
@ -3757,8 +3757,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "12345678", updch.Value) assert.Equal(t, "12345678", updch.Value)
err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match"). err := NewDetailedError(ErrorBadAttestationStatementType, "permanent identifier does not match").
WithAdditionalErrorDetail().
AddSubproblems(NewSubproblemWithIdentifier( AddSubproblems(NewSubproblemWithIdentifier(
ErrorRejectedIdentifierType, ErrorRejectedIdentifierType,
Identifier{Type: "permanent-identifier", Value: "12345678"}, Identifier{Type: "permanent-identifier", Value: "12345678"},
@ -3853,7 +3852,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "12345678", updch.Value) assert.Equal(t, "12345678", updch.Value)
err := NewError(ErrorBadAttestationStatementType, `unsupported attestation object format "bogus-format"`).WithAdditionalErrorDetail() err := NewDetailedError(ErrorBadAttestationStatementType, `unsupported attestation object format "bogus-format"`)
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)

@ -237,7 +237,7 @@ func Test_deviceAttest01ValidateWithTPMSimulator(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "device.id.12345678", updch.Value) assert.Equal(t, "device.id.12345678", updch.Value)
err := NewError(ErrorBadAttestationStatementType, `version "bogus" is not supported`).WithAdditionalErrorDetail() err := NewDetailedError(ErrorBadAttestationStatementType, `version "bogus" is not supported`)
assert.EqualError(t, updch.Error.Err, err.Err.Error()) assert.EqualError(t, updch.Error.Err, err.Err.Error())
assert.Equal(t, err.Type, updch.Error.Type) assert.Equal(t, err.Type, updch.Error.Type)
@ -282,8 +282,7 @@ func Test_deviceAttest01ValidateWithTPMSimulator(t *testing.T) {
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
assert.Equal(t, "device.id.99999999", updch.Value) assert.Equal(t, "device.id.99999999", updch.Value)
err := NewError(ErrorBadAttestationStatementType, `permanent identifier does not match`). err := NewDetailedError(ErrorBadAttestationStatementType, `permanent identifier does not match`).
WithAdditionalErrorDetail().
AddSubproblems(NewSubproblemWithIdentifier( AddSubproblems(NewSubproblemWithIdentifier(
ErrorRejectedIdentifierType, ErrorRejectedIdentifierType,
Identifier{Type: "permanent-identifier", Value: "device.id.99999999"}, Identifier{Type: "permanent-identifier", Value: "device.id.99999999"},

@ -298,20 +298,14 @@ func NewError(pt ProblemType, msg string, args ...interface{}) *Error {
return newError(pt, errors.Errorf(msg, args...)) return newError(pt, errors.Errorf(msg, args...))
} }
// AddSubproblems adds the Subproblems to Error. It // NewDetailedError creates a new Error that includes the error
// returns the Error, allowing for fluent addition. // message in the details, providing more information to the
func (e *Error) AddSubproblems(subproblems ...Subproblem) *Error { // ACME client.
e.Subproblems = append(e.Subproblems, subproblems...) func NewDetailedError(pt ProblemType, msg string, args ...interface{}) *Error {
return e return NewError(pt, msg, args...).withDetail()
} }
// WithAdditionalErrorDetail adds the underlying error func (e *Error) withDetail() *Error {
// to the existing (default) ACME error detail, providing
// more information to the ACME client.
func (e *Error) WithAdditionalErrorDetail() *Error {
// prevent internal server errors from disclosing
// the internal error to the client at all times and
// prevent nil pointers.
if e == nil || e.Status >= 500 || e.Err == nil { if e == nil || e.Status >= 500 || e.Err == nil {
return e return e
} }
@ -320,6 +314,13 @@ func (e *Error) WithAdditionalErrorDetail() *Error {
return e return e
} }
// AddSubproblems adds the Subproblems to Error. It
// returns the Error, allowing for fluent addition.
func (e *Error) AddSubproblems(subproblems ...Subproblem) *Error {
e.Subproblems = append(e.Subproblems, subproblems...)
return e
}
// NewSubproblem creates a new Subproblem. The msg and args // NewSubproblem creates a new Subproblem. The msg and args
// are used to create a new error, which is set as the Detail, allowing // are used to create a new error, which is set as the Detail, allowing
// for more detailed error messages to be returned to the ACME client. // for more detailed error messages to be returned to the ACME client.
@ -383,6 +384,10 @@ func WrapError(typ ProblemType, err error, msg string, args ...interface{}) *Err
} }
} }
func WrapDetailedError(typ ProblemType, err error, msg string, args ...interface{}) *Error {
return WrapError(typ, err, msg, args...).withDetail()
}
// WrapErrorISE shortcut to wrap an internal server error type. // WrapErrorISE shortcut to wrap an internal server error type.
func WrapErrorISE(err error, msg string, args ...interface{}) *Error { func WrapErrorISE(err error, msg string, args ...interface{}) *Error {
return WrapError(ErrorServerInternalType, err, msg, args...) return WrapError(ErrorServerInternalType, err, msg, args...)

@ -22,8 +22,7 @@ func TestError_WithAdditionalErrorDetail(t *testing.T) {
"detail": "The server experienced an internal error", "detail": "The server experienced an internal error",
"type": "urn:ietf:params:acme:error:serverInternal", "type": "urn:ietf:params:acme:error:serverInternal",
}) })
malformedErr := NewError(ErrorMalformedType, "malformed error") malformedErr := NewError(ErrorMalformedType, "malformed error") // will result in Err == nil behavior
malformedErr.Err = nil
malformedJSON := mustJSON(t, map[string]interface{}{ malformedJSON := mustJSON(t, map[string]interface{}{
"detail": "The request message was malformed", "detail": "The request message was malformed",
"type": "urn:ietf:params:acme:error:malformed", "type": "urn:ietf:params:acme:error:malformed",
@ -37,9 +36,9 @@ func TestError_WithAdditionalErrorDetail(t *testing.T) {
err *Error err *Error
want string want string
}{ }{
{"internal", NewError(ErrorServerInternalType, "").WithAdditionalErrorDetail(), internalJSON}, {"internal", NewDetailedError(ErrorServerInternalType, ""), internalJSON},
{"nil err", malformedErr.WithAdditionalErrorDetail(), malformedJSON}, {"nil err", malformedErr, malformedJSON},
{"detailed", NewError(ErrorBadAttestationStatementType, "invalid property").WithAdditionalErrorDetail(), withDetailJSON}, {"detailed", NewDetailedError(ErrorBadAttestationStatementType, "invalid property"), withDetailJSON},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

Loading…
Cancel
Save