Changes to core, sa, policy
This commit is contained in:
parent
c44962d077
commit
c1c3d1e871
|
@ -29,9 +29,34 @@ func newChallenge(challengeType string, accountKey *jose.JsonWebKey) (Challenge,
|
|||
}, nil
|
||||
}
|
||||
|
||||
//----- BEGIN TO DELETE -----
|
||||
// SimpleHTTPChallenge constructs a random HTTP challenge
|
||||
func SimpleHTTPChallenge(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
chall, err := newChallenge(ChallengeTypeSimpleHTTP, accountKey)
|
||||
tls := true
|
||||
return Challenge{
|
||||
Type: ChallengeTypeSimpleHTTP,
|
||||
Status: StatusPending,
|
||||
Token: NewToken(),
|
||||
TLS: &tls,
|
||||
AccountKey: accountKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DvsniChallenge constructs a random DVSNI challenge
|
||||
func DvsniChallenge(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
return Challenge{
|
||||
Type: ChallengeTypeDVSNI,
|
||||
Status: StatusPending,
|
||||
Token: NewToken(),
|
||||
AccountKey: accountKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//----- END TO DELETE -----
|
||||
|
||||
// HTTPChallenge constructs a random http-00 challenge
|
||||
func HTTPChallenge_00(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
chall, err := newChallenge(ChallengeTypeHTTP_00, accountKey)
|
||||
if err != nil {
|
||||
return Challenge{}, err
|
||||
}
|
||||
|
@ -41,12 +66,12 @@ func SimpleHTTPChallenge(accountKey *jose.JsonWebKey) (Challenge, error) {
|
|||
return chall, nil
|
||||
}
|
||||
|
||||
// DvsniChallenge constructs a random DVSNI challenge
|
||||
func DvsniChallenge(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
return newChallenge(ChallengeTypeDVSNI, accountKey)
|
||||
// DvsniChallenge constructs a random tls-sni-00 challenge
|
||||
func TLSSNIChallenge_00(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
return newChallenge(ChallengeTypeTLSSNI_00, accountKey)
|
||||
}
|
||||
|
||||
// DNSChallenge constructs a random DNS challenge
|
||||
func DNSChallenge(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
return newChallenge(ChallengeTypeDNS, accountKey)
|
||||
func DNSChallenge_00(accountKey *jose.JsonWebKey) (Challenge, error) {
|
||||
return newChallenge(ChallengeTypeDNS_00, accountKey)
|
||||
}
|
||||
|
|
164
core/objects.go
164
core/objects.go
|
@ -90,7 +90,9 @@ const (
|
|||
const (
|
||||
ChallengeTypeSimpleHTTP = "simpleHttp"
|
||||
ChallengeTypeDVSNI = "dvsni"
|
||||
ChallengeTypeDNS = "dns"
|
||||
ChallengeTypeHTTP_00 = "http-00"
|
||||
ChallengeTypeTLSSNI_00 = "tls-sni-00"
|
||||
ChallengeTypeDNS_00 = "dns-00"
|
||||
)
|
||||
|
||||
// The suffix appended to pseudo-domain names in DVSNI challenges
|
||||
|
@ -235,13 +237,16 @@ type Challenge struct {
|
|||
// A URI to which a response can be POSTed
|
||||
URI string `json:"uri"`
|
||||
|
||||
// Used by simpleHttp, dvsni, and dns challenges
|
||||
// Used by simpleHttp, http-00, tls-sni-00, and dns-00 challenges
|
||||
Token string `json:"token,omitempty"`
|
||||
|
||||
// Used by simpleHTTP challenges
|
||||
// Used by simpleHttp challenges
|
||||
TLS *bool `json:"tls,omitempty"`
|
||||
|
||||
// Used by simpleHTTP, dns, and dvsni challenges
|
||||
// Used by dvsni challenges
|
||||
Validation *jose.JsonWebSignature `json:"validation,omitempty"`
|
||||
|
||||
// Used by http-00, tls-sni-00, and dns-00 challenges
|
||||
AuthorizedKey JSONBuffer `json:"authorizedKey,omitempty"`
|
||||
|
||||
// Contains information about URLs used or redirected to and IPs resolved and
|
||||
|
@ -282,14 +287,18 @@ func (ch Challenge) RecordsSane() bool {
|
|||
}
|
||||
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
case ChallengeTypeSimpleHTTP: // TO DELETE
|
||||
fallthrough // TO DELETE
|
||||
case ChallengeTypeHTTP_00:
|
||||
for _, rec := range ch.ValidationRecord {
|
||||
if rec.URL == "" || rec.Hostname == "" || rec.Port == "" || rec.AddressUsed == nil ||
|
||||
len(rec.AddressesResolved) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case ChallengeTypeDVSNI:
|
||||
case ChallengeTypeDVSNI: // TO DELETE
|
||||
fallthrough // TO DELETE
|
||||
case ChallengeTypeTLSSNI_00:
|
||||
if len(ch.ValidationRecord) > 1 {
|
||||
return false
|
||||
}
|
||||
|
@ -300,16 +309,112 @@ func (ch Challenge) RecordsSane() bool {
|
|||
ch.ValidationRecord[0].AddressUsed == nil || len(ch.ValidationRecord[0].AddressesResolved) == 0 {
|
||||
return false
|
||||
}
|
||||
case ChallengeTypeDNS:
|
||||
case ChallengeTypeDNS_00:
|
||||
// Nothing for now
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//----- BEGIN TO DELETE -----
|
||||
// IsLegacy returns true if the challenge is of a legacy type (i.e., one defined
|
||||
// before draft-ietf-acme-acme-00)
|
||||
func (ch Challenge) IsLegacy() bool {
|
||||
return (ch.Type == ChallengeTypeSimpleHTTP) ||
|
||||
(ch.Type == ChallengeTypeDVSNI)
|
||||
}
|
||||
|
||||
// LegacyIsSane performs sanity checks for legacy challenge types, which have
|
||||
// a different structure / logic than current challenges.
|
||||
func (ch Challenge) LegacyIsSane(completed bool) bool {
|
||||
if !ch.IsLegacy() {
|
||||
return false
|
||||
}
|
||||
|
||||
if ch.Status != StatusPending {
|
||||
return false
|
||||
}
|
||||
|
||||
if ch.AccountKey == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
// check extra fields aren't used
|
||||
if ch.Validation != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if completed && ch.TLS == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// check token is present, corrent length, and contains b64 encoded string
|
||||
if ch.Token == "" || len(ch.Token) != 43 {
|
||||
return false
|
||||
}
|
||||
if _, err := B64dec(ch.Token); err != nil {
|
||||
return false
|
||||
}
|
||||
case ChallengeTypeDVSNI:
|
||||
// check extra fields aren't used
|
||||
if ch.TLS != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// check token is present, corrent length, and contains b64 encoded string
|
||||
if ch.Token == "" || len(ch.Token) != 43 {
|
||||
return false
|
||||
}
|
||||
if _, err := B64dec(ch.Token); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// If completed, check that there's a validation object
|
||||
if completed && ch.Validation == nil {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// LegacyMergeResponse copies a subset of client-provided data to the current Challenge.
|
||||
// Note: This method does not update the challenge on the left side of the '.'
|
||||
func (ch Challenge) LegacyMergeResponse(resp Challenge) Challenge {
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
// For simpleHttp, only "tls" is client-provided
|
||||
// If "tls" is not provided, default to "true"
|
||||
if resp.TLS != nil {
|
||||
ch.TLS = resp.TLS
|
||||
} else {
|
||||
ch.TLS = new(bool)
|
||||
*ch.TLS = true
|
||||
}
|
||||
|
||||
case ChallengeTypeDVSNI:
|
||||
// For dvsni and dns, only "validation" is client-provided
|
||||
if resp.Validation != nil {
|
||||
ch.Validation = resp.Validation
|
||||
}
|
||||
}
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
//----- END TO DELETE -----
|
||||
|
||||
// IsSane checks the sanity of a challenge object before issued to the client
|
||||
// (completed = false) and before validation (completed = true).
|
||||
func (ch Challenge) IsSane(completed bool) bool {
|
||||
if ch.IsLegacy() { // TO DELETE
|
||||
return ch.LegacyIsSane(completed) // TO DELETE
|
||||
} // TO DELETE
|
||||
|
||||
if ch.Status != StatusPending {
|
||||
return false
|
||||
}
|
||||
|
@ -347,51 +452,24 @@ func (ch Challenge) IsSane(completed bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(rlb): Remove this whole switch once the TLS option is gone.
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
if completed && ch.TLS == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
case ChallengeTypeDVSNI:
|
||||
// Same as DNS
|
||||
fallthrough
|
||||
case ChallengeTypeDNS:
|
||||
// check extra fields aren't used
|
||||
if ch.TLS != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MergeResponse copies a subset of client-provided data to the current Challenge.
|
||||
// Note: This method does not update the challenge on the left side of the '.'
|
||||
func (ch Challenge) MergeResponse(resp Challenge) Challenge {
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
// For simpleHttp, only "tls" is client-provided
|
||||
// If "tls" is not provided, default to "true"
|
||||
if resp.TLS != nil {
|
||||
ch.TLS = resp.TLS
|
||||
} else {
|
||||
ch.TLS = new(bool)
|
||||
*ch.TLS = true
|
||||
}
|
||||
if ch.IsLegacy() { // TO DELETE
|
||||
return ch.LegacyMergeResponse(resp) // TO DELETE
|
||||
} // TO DELETE
|
||||
|
||||
// For dvsni and dns (as well as simpleHttp), the client echoes back
|
||||
// the "token" value provided in the authorizedKey object. The caller
|
||||
// should use IsSane to verify that the "token" field in the challenge
|
||||
// matches the corresponding field in the authorized key.
|
||||
// The only client-provided field is the token, and all current challenge types
|
||||
// use it.
|
||||
switch ch.Type {
|
||||
case ChallengeTypeHTTP_00:
|
||||
fallthrough
|
||||
case ChallengeTypeDVSNI:
|
||||
case ChallengeTypeTLSSNI_00:
|
||||
fallthrough
|
||||
case ChallengeTypeDNS:
|
||||
case ChallengeTypeDNS_00:
|
||||
ch.Token = resp.Token
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,71 @@ func TestRecordSanityCheck(t *testing.T) {
|
|||
test.Assert(t, !chall.RecordsSane(), "Record should not be sane")
|
||||
}
|
||||
|
||||
//-----BEGIN TO DELETE-----
|
||||
func TestChallengeSanityCheck_Legacy(t *testing.T) {
|
||||
// Make a temporary account key
|
||||
var accountKey *jose.JsonWebKey
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"kty":"RSA",
|
||||
"n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ",
|
||||
"e":"AQAB"
|
||||
}`), &accountKey)
|
||||
test.AssertNotError(t, err, "Error unmarshaling JWK")
|
||||
|
||||
types := []string{ChallengeTypeSimpleHTTP, ChallengeTypeDVSNI}
|
||||
for _, challengeType := range types {
|
||||
chall := Challenge{
|
||||
Type: challengeType,
|
||||
Status: StatusInvalid,
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Status = StatusPending
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Token = ""
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Token = "notlongenough"
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+o!"
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Token = "KQqLsiS5j0CONR_eUXTUSUDNVaHODtc-0pD6ACif7U4"
|
||||
test.Assert(t, chall.IsSane(false), "IsSane should be true")
|
||||
|
||||
// Post-completion tests differ by type
|
||||
if challengeType == ChallengeTypeSimpleHTTP {
|
||||
tls := true
|
||||
chall.TLS = &tls
|
||||
chall.ValidationRecord = []ValidationRecord{ValidationRecord{
|
||||
URL: "",
|
||||
Hostname: "localhost",
|
||||
Port: "80",
|
||||
AddressesResolved: []net.IP{net.IP{127, 0, 0, 1}},
|
||||
AddressUsed: net.IP{127, 0, 0, 1},
|
||||
}}
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
} else if challengeType == ChallengeTypeDVSNI {
|
||||
chall.Validation = new(jose.JsonWebSignature)
|
||||
if challengeType == ChallengeTypeDVSNI {
|
||||
chall.ValidationRecord = []ValidationRecord{ValidationRecord{
|
||||
Hostname: "localhost",
|
||||
Port: "80",
|
||||
AddressesResolved: []net.IP{net.IP{127, 0, 0, 1}},
|
||||
AddressUsed: net.IP{127, 0, 0, 1},
|
||||
}}
|
||||
} else {
|
||||
chall.ValidationRecord = []ValidationRecord{}
|
||||
}
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
}
|
||||
}
|
||||
|
||||
chall := Challenge{Type: "bogus", Status: StatusPending}
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
test.Assert(t, !chall.IsSane(true), "IsSane should be false")
|
||||
}
|
||||
|
||||
//-----END TO DELETE-----
|
||||
|
||||
func TestChallengeSanityCheck(t *testing.T) {
|
||||
// Make a temporary account key
|
||||
var accountKey *jose.JsonWebKey
|
||||
|
@ -108,7 +173,7 @@ func TestChallengeSanityCheck(t *testing.T) {
|
|||
jsonAK, err := json.Marshal(ak)
|
||||
test.AssertNotError(t, err, "Error marshaling authorized key")
|
||||
|
||||
types := []string{ChallengeTypeSimpleHTTP, ChallengeTypeDVSNI, ChallengeTypeDNS}
|
||||
types := []string{ChallengeTypeHTTP_00, ChallengeTypeTLSSNI_00, ChallengeTypeDNS_00}
|
||||
for _, challengeType := range types {
|
||||
chall := Challenge{
|
||||
Type: challengeType,
|
||||
|
@ -123,15 +188,8 @@ func TestChallengeSanityCheck(t *testing.T) {
|
|||
chall.AuthorizedKey = jsonAK
|
||||
test.Assert(t, chall.IsSane(false), "IsSane should be true")
|
||||
|
||||
// Post-completion tests differ by type
|
||||
chall.Token = ak.Token
|
||||
if challengeType == ChallengeTypeSimpleHTTP {
|
||||
tls := true
|
||||
chall.TLS = &tls
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
} else if challengeType == ChallengeTypeDVSNI || challengeType == ChallengeTypeDNS {
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
}
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
}
|
||||
|
||||
chall := Challenge{Type: "bogus", Status: StatusPending}
|
||||
|
|
|
@ -178,6 +178,7 @@ func (pa PolicyAuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error {
|
|||
//
|
||||
// Note: Current implementation is static, but future versions may not be.
|
||||
func (pa PolicyAuthorityImpl) ChallengesFor(identifier core.AcmeIdentifier, accountKey *jose.JsonWebKey) (challenges []core.Challenge, combinations [][]int, err error) {
|
||||
//-----BEGIN TO DELETE-----
|
||||
simpleHTTP, err := core.SimpleHTTPChallenge(accountKey)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -187,8 +188,19 @@ func (pa PolicyAuthorityImpl) ChallengesFor(identifier core.AcmeIdentifier, acco
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
//-----END TO DELETE-----
|
||||
|
||||
challenges = []core.Challenge{simpleHTTP, dvsni}
|
||||
combinations = [][]int{[]int{0}, []int{1}}
|
||||
http00, err := core.HTTPChallenge_00(accountKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tlssni00, err := core.TLSSNIChallenge_00(accountKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
challenges = []core.Challenge{simpleHTTP, dvsni, http00, tlssni00} // TO UPDATE
|
||||
combinations = [][]int{[]int{0}, []int{1}, []int{2}, []int{3}} // TO UPDATE
|
||||
return
|
||||
}
|
||||
|
|
|
@ -186,11 +186,18 @@ func TestChallengesFor(t *testing.T) {
|
|||
t.Errorf("Error generating challenges: %v", err)
|
||||
}
|
||||
|
||||
if len(challenges) != 2 || challenges[0].Type != core.ChallengeTypeSimpleHTTP ||
|
||||
challenges[1].Type != core.ChallengeTypeDVSNI {
|
||||
//-----BEGIN TO UPDATE-----
|
||||
if len(challenges) != 4 ||
|
||||
challenges[0].Type != core.ChallengeTypeSimpleHTTP ||
|
||||
challenges[1].Type != core.ChallengeTypeDVSNI ||
|
||||
challenges[2].Type != core.ChallengeTypeHTTP_00 ||
|
||||
challenges[3].Type != core.ChallengeTypeTLSSNI_00 {
|
||||
t.Error("Incorrect challenges returned")
|
||||
}
|
||||
if len(combinations) != 2 || combinations[0][0] != 0 || combinations[1][0] != 1 {
|
||||
if len(combinations) != 4 ||
|
||||
combinations[0][0] != 0 || combinations[1][0] != 1 ||
|
||||
combinations[2][0] != 2 || combinations[3][0] != 3 {
|
||||
t.Error("Incorrect combinations returned")
|
||||
}
|
||||
//-----END TO UPDATE-----
|
||||
}
|
||||
|
|
13
sa/model.go
13
sa/model.go
|
@ -95,6 +95,12 @@ func challengeToModel(c *core.Challenge, authID string) (*challModel, error) {
|
|||
Token: c.Token,
|
||||
TLS: c.TLS,
|
||||
}
|
||||
if c.Validation != nil {
|
||||
cm.Validation = []byte(c.Validation.FullSerialize())
|
||||
if len(cm.Validation) > mediumBlobSize {
|
||||
return nil, fmt.Errorf("Validation object is too large to store in the database")
|
||||
}
|
||||
}
|
||||
if c.AuthorizedKey != nil {
|
||||
cm.AuthorizedKey = []byte(c.AuthorizedKey)
|
||||
if len(cm.AuthorizedKey) > mediumBlobSize {
|
||||
|
@ -144,6 +150,13 @@ func modelToChallenge(cm *challModel) (core.Challenge, error) {
|
|||
TLS: cm.TLS,
|
||||
AuthorizedKey: core.JSONBuffer(cm.AuthorizedKey),
|
||||
}
|
||||
if len(cm.Validation) > 0 {
|
||||
val, err := jose.ParseSigned(string(cm.Validation))
|
||||
if err != nil {
|
||||
return core.Challenge{}, err
|
||||
}
|
||||
c.Validation = val
|
||||
}
|
||||
if len(cm.Error) > 0 {
|
||||
var problem core.ProblemDetails
|
||||
err := json.Unmarshal(cm.Error, &problem)
|
||||
|
|
Loading…
Reference in New Issue