forked from TrueCloudLab/certificates
Add new method ACMEClient.ValidateWithPayload
This new method will be used to validate to validate the device attestation payload.
This commit is contained in:
parent
f1c63bc38d
commit
ebce40e9b6
2 changed files with 109 additions and 3 deletions
|
@ -243,6 +243,19 @@ func (c *ACMEClient) ValidateChallenge(url string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateWithPayload will attempt to validate the challenge at the given url
|
||||||
|
// with the given attestation payload.
|
||||||
|
func (c *ACMEClient) ValidateWithPayload(url string, payload []byte) error {
|
||||||
|
resp, err := c.post(payload, url, withKid(c))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
return readACMEError(resp.Body)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetAuthz returns the Authz at the given path.
|
// GetAuthz returns the Authz at the given path.
|
||||||
func (c *ACMEClient) GetAuthz(url string) (*acme.Authorization, error) {
|
func (c *ACMEClient) GetAuthz(url string) (*acme.Authorization, error) {
|
||||||
resp, err := c.post(nil, url, withKid(c))
|
resp, err := c.post(nil, url, withKid(c))
|
||||||
|
|
|
@ -12,13 +12,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.step.sm/crypto/jose"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
acmeAPI "github.com/smallstep/certificates/acme/api"
|
acmeAPI "github.com/smallstep/certificates/acme/api"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
"go.step.sm/crypto/jose"
|
||||||
|
"go.step.sm/crypto/pemutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewACMEClient(t *testing.T) {
|
func TestNewACMEClient(t *testing.T) {
|
||||||
|
@ -980,6 +979,100 @@ func TestACMEClient_ValidateChallenge(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestACMEClient_ValidateWithPayload(t *testing.T) {
|
||||||
|
key, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||||
|
|
||||||
|
t.Log(req.RequestURI)
|
||||||
|
w.Header().Set("Replay-Nonce", "nonce")
|
||||||
|
switch req.RequestURI {
|
||||||
|
case "/nonce":
|
||||||
|
render.JSONStatus(w, []byte{}, 200)
|
||||||
|
return
|
||||||
|
case "/fail-nonce":
|
||||||
|
render.JSONStatus(w, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate jws request protected headers and body
|
||||||
|
body, err := io.ReadAll(req.Body)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
jws, err := jose.ParseJWS(string(body))
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
hdr := jws.Signatures[0].Protected
|
||||||
|
assert.Equals(t, hdr.Nonce, "nonce")
|
||||||
|
|
||||||
|
_, ok := hdr.ExtraHeaders["url"].(string)
|
||||||
|
assert.Fatal(t, ok)
|
||||||
|
assert.Equals(t, hdr.KeyID, "kid")
|
||||||
|
|
||||||
|
payload, err := jws.Verify(key.Public())
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
assert.Equals(t, payload, []byte("the-payload"))
|
||||||
|
|
||||||
|
switch req.RequestURI {
|
||||||
|
case "/ok":
|
||||||
|
render.JSONStatus(w, acme.Challenge{
|
||||||
|
Type: "device-attestation-01",
|
||||||
|
Status: "valid",
|
||||||
|
Token: "foo",
|
||||||
|
}, 200)
|
||||||
|
case "/fail":
|
||||||
|
render.JSONStatus(w, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
client *http.Client
|
||||||
|
dirLoc string
|
||||||
|
dir *acmeAPI.Directory
|
||||||
|
acc *acme.Account
|
||||||
|
Key *jose.JSONWebKey
|
||||||
|
kid string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
url string
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{srv.Client(), srv.URL, &acmeAPI.Directory{
|
||||||
|
NewNonce: srv.URL + "/nonce",
|
||||||
|
}, nil, key, "kid"}, args{srv.URL + "/ok", []byte("the-payload")}, false},
|
||||||
|
{"fail nonce", fields{srv.Client(), srv.URL, &acmeAPI.Directory{
|
||||||
|
NewNonce: srv.URL + "/fail-nonce",
|
||||||
|
}, nil, key, "kid"}, args{srv.URL + "/ok", []byte("the-payload")}, true},
|
||||||
|
{"fail payload", fields{srv.Client(), srv.URL, &acmeAPI.Directory{
|
||||||
|
NewNonce: srv.URL + "/nonce",
|
||||||
|
}, nil, key, "kid"}, args{srv.URL + "/fail", []byte("the-payload")}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &ACMEClient{
|
||||||
|
client: tt.fields.client,
|
||||||
|
dirLoc: tt.fields.dirLoc,
|
||||||
|
dir: tt.fields.dir,
|
||||||
|
acc: tt.fields.acc,
|
||||||
|
Key: tt.fields.Key,
|
||||||
|
kid: tt.fields.kid,
|
||||||
|
}
|
||||||
|
if err := c.ValidateWithPayload(tt.args.url, tt.args.payload); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ACMEClient.ValidateWithPayload() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestACMEClient_FinalizeOrder(t *testing.T) {
|
func TestACMEClient_FinalizeOrder(t *testing.T) {
|
||||||
type test struct {
|
type test struct {
|
||||||
r1, r2 interface{}
|
r1, r2 interface{}
|
||||||
|
|
Loading…
Reference in a new issue