@@ -22,19 +22,20 @@ import (
2222 "net"
2323 "net/url"
2424 "reflect"
25+ "slices"
2526 "strconv"
2627 "strings"
2728 "time"
2829
2930 "github.com/coreos/go-oidc/v3/oidc"
3031 "github.com/fxamacker/cbor/v2"
3132 "github.com/google/go-tpm/legacy/tpm2"
33+
3234 "github.com/smallstep/go-attestation/attest"
3335 "go.step.sm/crypto/jose"
3436 "go.step.sm/crypto/keyutil"
3537 "go.step.sm/crypto/pemutil"
3638 "go.step.sm/crypto/x509util"
37- "golang.org/x/exp/slices"
3839
3940 "github.com/smallstep/certificates/acme/wire"
4041 "github.com/smallstep/certificates/authority/provisioner"
@@ -1525,18 +1526,30 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
15251526 data := & stepAttestationData {
15261527 Certificate : leaf ,
15271528 }
1529+
15281530 if data .Fingerprint , err = keyutil .Fingerprint (leaf .PublicKey ); err != nil {
15291531 return nil , WrapErrorISE (err , "error calculating key fingerprint" )
15301532 }
1531- for _ , ext := range leaf .Extensions {
1533+
1534+ if data .SerialNumber , err = searchSerialNumber (leaf ); err != nil {
1535+ return nil , WrapErrorISE (err , "error finding serial number" )
1536+ }
1537+
1538+ return data , nil
1539+ }
1540+
1541+ // searchSerialNumber searches the certificate extensions, looking for a serial
1542+ // number encoded in one of them. It is not guaranteed that a certificate contains
1543+ // an extension carrying a serial number, so the result can be empty.
1544+ func searchSerialNumber (cert * x509.Certificate ) (string , error ) {
1545+ for _ , ext := range cert .Extensions {
15321546 if ext .Id .Equal (oidYubicoSerialNumber ) {
15331547 var serialNumber int
15341548 rest , err := asn1 .Unmarshal (ext .Value , & serialNumber )
15351549 if err != nil || len (rest ) > 0 {
1536- return nil , WrapError (ErrorBadAttestationStatementType , err , "error parsing serial number" )
1550+ return "" , WrapError (ErrorBadAttestationStatementType , err , "error parsing serial number" )
15371551 }
1538- data .SerialNumber = strconv .Itoa (serialNumber )
1539- break
1552+ return strconv .Itoa (serialNumber ), nil
15401553 }
15411554 if ext .Id .Equal (oidStepManagedDevice ) {
15421555 type stepManagedDevice struct {
@@ -1545,14 +1558,13 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
15451558 var md stepManagedDevice
15461559 rest , err := asn1 .Unmarshal (ext .Value , & md )
15471560 if err != nil || len (rest ) > 0 {
1548- return nil , WrapError (ErrorBadAttestationStatementType , err , "error parsing serial number" )
1561+ return "" , WrapError (ErrorBadAttestationStatementType , err , "error parsing serial number" )
15491562 }
1550- data .SerialNumber = md .DeviceID
1551- break
1563+ return md .DeviceID , nil
15521564 }
15531565 }
15541566
1555- return data , nil
1567+ return "" , nil
15561568}
15571569
15581570// serverName determines the SNI HostName to set based on an acme.Challenge
0 commit comments