forked from TrueCloudLab/certificates
Validate proof of possession signature
On the step format, validate proof of possession of the private key validating the signature in the attestation statement.
This commit is contained in:
parent
45af68b244
commit
54d92095ac
2 changed files with 61 additions and 7 deletions
|
@ -3,6 +3,10 @@ package acme
|
|||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
|
@ -322,7 +326,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
|
|||
if err := json.Unmarshal(payload, &p); err != nil {
|
||||
return WrapErrorISE(err, "error unmarshalling JSON")
|
||||
}
|
||||
|
||||
fmt.Println(string(payload))
|
||||
if p.Error != "" {
|
||||
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
|
||||
"payload contained error: %v", p.Error))
|
||||
|
@ -338,6 +342,9 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
|
|||
return WrapErrorISE(err, "error unmarshalling CBOR")
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(att)
|
||||
fmt.Println(string(b))
|
||||
|
||||
switch att.Format {
|
||||
case "apple":
|
||||
data, err := doAppleAttestationFormat(ctx, ch, db, &att)
|
||||
|
@ -369,11 +376,11 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
|
|||
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "permanent identifier does not match"))
|
||||
}
|
||||
case "step":
|
||||
data, err := doStepAttestationFormat(ctx, ch, db, &att)
|
||||
data, err := doStepAttestationFormat(ctx, ch, jwk, &att)
|
||||
if err != nil {
|
||||
var acmeError *Error
|
||||
if errors.As(err, &acmeError) {
|
||||
fmt.Println(acmeError)
|
||||
fmt.Printf("debug: %#v\n", acmeError)
|
||||
if acmeError.Status == 500 {
|
||||
return acmeError
|
||||
}
|
||||
|
@ -534,7 +541,7 @@ type stepAttestationData struct {
|
|||
SerialNumber string
|
||||
}
|
||||
|
||||
func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *AttestationObject) (*stepAttestationData, error) {
|
||||
func doStepAttestationFormat(ctx context.Context, ch *Challenge, jwk *jose.JSONWebKey, att *AttestationObject) (*stepAttestationData, error) {
|
||||
root, err := pemutil.ParseCertificate([]byte(yubicoPIVRootCA))
|
||||
if err != nil {
|
||||
return nil, WrapErrorISE(err, "error parsing root ca")
|
||||
|
@ -542,6 +549,7 @@ func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *Att
|
|||
roots := x509.NewCertPool()
|
||||
roots.AddCert(root)
|
||||
|
||||
// Extract x5c and verify certificate
|
||||
x5c, ok := att.AttStatement["x5c"].([]interface{})
|
||||
if !ok {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "x5c not present")
|
||||
|
@ -549,7 +557,6 @@ func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *Att
|
|||
if len(x5c) == 0 {
|
||||
return nil, NewError(ErrorRejectedIdentifierType, "x5c is empty")
|
||||
}
|
||||
|
||||
der, ok := x5c[0].([]byte)
|
||||
if !ok {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "x5c is malformed")
|
||||
|
@ -558,7 +565,6 @@ func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *Att
|
|||
if err != nil {
|
||||
return nil, WrapError(ErrorBadAttestationStatement, err, "x5c is malformed")
|
||||
}
|
||||
|
||||
intermediates := x509.NewCertPool()
|
||||
for _, v := range x5c[1:] {
|
||||
der, ok = v.([]byte)
|
||||
|
@ -571,7 +577,6 @@ func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *Att
|
|||
}
|
||||
intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
if _, err := leaf.Verify(x509.VerifyOptions{
|
||||
Intermediates: intermediates,
|
||||
Roots: roots,
|
||||
|
@ -581,6 +586,46 @@ func doStepAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *Att
|
|||
return nil, WrapError(ErrorBadAttestationStatement, err, "x5c is not valid")
|
||||
}
|
||||
|
||||
// Verify proof of possession of private key validating the key
|
||||
// authorization. Per recommendation at
|
||||
// https://w3c.github.io/webauthn/#sctn-signature-attestation-types the
|
||||
// signature is CBOR-encoded.
|
||||
var sig []byte
|
||||
csig, ok := att.AttStatement["sig"].([]byte)
|
||||
if !ok {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "sig not present")
|
||||
}
|
||||
if err := cbor.Unmarshal(csig, &sig); err != nil {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "sig is malformed")
|
||||
}
|
||||
keyAuth, err := KeyAuthorization(ch.Token, jwk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pub := leaf.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if pub.Curve != elliptic.P256() {
|
||||
return nil, WrapError(ErrorBadAttestationStatement, err, "unsupported elliptic curve %s", pub.Curve)
|
||||
}
|
||||
sum := sha256.Sum256([]byte(keyAuth))
|
||||
if !ecdsa.VerifyASN1(pub, sum[:], sig) {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "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, NewError(ErrorBadAttestationStatement, "failed to validate signature")
|
||||
}
|
||||
case ed25519.PublicKey:
|
||||
if !ed25519.Verify(pub, []byte(keyAuth), sig) {
|
||||
return nil, NewError(ErrorBadAttestationStatement, "failed to validate signature")
|
||||
}
|
||||
default:
|
||||
return nil, NewError(ErrorBadAttestationStatement, "unsupported public key type %T", pub)
|
||||
}
|
||||
|
||||
// Parse attestation data
|
||||
data := &stepAttestationData{
|
||||
Certificate: leaf,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -203,6 +205,13 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
|
|||
return WrapErrorISE(err, "error signing certificate for order %s", o.ID)
|
||||
}
|
||||
|
||||
pem.Encode(os.Stdout, &pem.Block{
|
||||
Type: "CERTIFICATE REQUEST", Bytes: csr.Raw,
|
||||
})
|
||||
pem.Encode(os.Stdout, &pem.Block{
|
||||
Type: "CERTIFICATE", Bytes: certChain[0].Raw,
|
||||
})
|
||||
|
||||
cert := &Certificate{
|
||||
AccountID: o.AccountID,
|
||||
OrderID: o.ID,
|
||||
|
|
Loading…
Reference in a new issue