Merge pull request #150 from rolandshoemaker/tests

VA and WFE tests
This commit is contained in:
jsha 2015-05-05 15:58:44 -07:00
commit b1e3c374c1
4 changed files with 395 additions and 1 deletions

View File

@ -91,6 +91,7 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti
return
} else {
va.log.Notice(fmt.Sprintf("Incorrect token validating SimpleHTTPS for %s %s", hostName, url))
challenge.Status = core.StatusInvalid
}
} else if err != nil {
va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err))

View File

@ -4,3 +4,213 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package va
import (
"testing"
"net"
"net/http"
"fmt"
"strings"
"math/big"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"crypto/sha256"
"time"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/test"
"github.com/letsencrypt/boulder/jose"
)
func bigIntFromB64(b64 string) *big.Int {
bytes, _ := jose.B64dec(b64)
x := big.NewInt(0)
x.SetBytes(bytes)
return x
}
func intFromB64(b64 string) int {
return int(bigIntFromB64(b64).Int64())
}
var n *big.Int = bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw")
var e int = intFromB64("AQAB")
var d *big.Int = bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ")
var p *big.Int = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc")
var q *big.Int = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc")
var TheKey rsa.PrivateKey = rsa.PrivateKey{
PublicKey: rsa.PublicKey{N: n, E: e},
D: d,
Primes: []*big.Int{p, q},
}
var ident core.AcmeIdentifier = core.AcmeIdentifier{Type: core.IdentifierType("dns"), Value: "localhost"}
func simpleSrv(t *testing.T, token string, stopChan, waitChan chan bool) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "404") {
http.NotFound(w, r)
} else if strings.HasSuffix(r.URL.Path, "wrongtoken") {
fmt.Fprintf(w, "wrongtoken")
}
fmt.Fprintf(w, "%s", token)
})
httpsServer := &http.Server{Addr: "localhost:5001"}
conn, err := net.Listen("tcp", httpsServer.Addr)
if err != nil {
waitChan <- true
t.Fatalf("Couldn't listen on %s: %s", httpsServer.Addr, err)
}
go func() {
<-stopChan
conn.Close()
}()
waitChan <- true
t.Fatalf("%s", httpsServer.Serve(conn))
}
func dvsniSrv(t *testing.T, R, S []byte, waitChan chan bool) {
RS := append(R, S...)
z := sha256.Sum256(RS)
zName := fmt.Sprintf("%064x.acme.invalid", z)
template := &x509.Certificate{
SerialNumber: big.NewInt(1337),
Subject: pkix.Name{
Organization: []string{"tests"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, 1),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: []string{zName},
}
certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
cert := &tls.Certificate{
Certificate: [][]byte{certBytes},
PrivateKey: &TheKey,
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{*cert},
ClientAuth: tls.NoClientCert,
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
fmt.Println(clientHello)
return cert, nil
},
NextProtos: []string{"http/1.1"},
}
httpsServer := &http.Server{Addr: "localhost:5001"}
conn, err := net.Listen("tcp", httpsServer.Addr)
if err != nil {
waitChan <- true
t.Fatalf("Couldn't listen on %s: %s", httpsServer.Addr, err)
}
tlsListener := tls.NewListener(conn, tlsConfig)
waitChan <- true
t.Fatalf("%s", httpsServer.Serve(tlsListener))
}
func TestSimpleHttps(t *testing.T) {
va := NewValidationAuthorityImpl(true)
chall := core.Challenge{Path: "test", Token: "THETOKEN"}
invalidChall := va.validateSimpleHTTPS(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
stopChan := make(chan bool, 1)
waitChan := make(chan bool, 1)
go simpleSrv(t, "THETOKEN", stopChan, waitChan)
finChall := va.validateSimpleHTTPS(ident, chall)
test.AssertEquals(t, finChall.Status, core.StatusValid)
chall.Path = "404"
invalidChall = va.validateSimpleHTTPS(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
chall.Path = "wrongtoken"
invalidChall = va.validateSimpleHTTPS(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
chall.Path = ""
invalidChall = va.validateSimpleHTTPS(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
chall.Path = "validish"
invalidChall = va.validateSimpleHTTPS(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
stopChan <- true
}
func TestDvsni(t *testing.T) {
va := NewValidationAuthorityImpl(true)
a := []byte{1,2,3,4,5,6,7,8,9,0}
ba := core.B64enc(a)
chall := core.Challenge{R: ba, S: ba}
invalidChall := va.validateDvsni(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
waitChan := make(chan bool, 1)
go dvsniSrv(t, a, a, waitChan)
<-waitChan
finChall := va.validateDvsni(ident, chall)
test.AssertEquals(t, finChall.Status, core.StatusValid)
chall.R = ba[5:]
invalidChall = va.validateDvsni(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
invalidChall = va.validateSimpleHTTPS(core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
chall.R = ba
chall.S = "!@#"
invalidChall = va.validateDvsni(ident, chall)
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
}
type MockRegistrationAuthority struct{}
func (ra *MockRegistrationAuthority) NewRegistration(reg core.Registration, jwk jose.JsonWebKey) (core.Registration, error) {
return reg, nil
}
func (ra *MockRegistrationAuthority) NewAuthorization(authz core.Authorization, jwk jose.JsonWebKey) (core.Authorization, error) {
return authz, nil
}
func (ra *MockRegistrationAuthority) NewCertificate(req core.CertificateRequest, jwk jose.JsonWebKey) (core.Certificate, error) {
return core.Certificate{}, nil
}
func (ra *MockRegistrationAuthority) UpdateRegistration(reg core.Registration, updated core.Registration) (core.Registration, error) {
return reg, nil
}
func (ra *MockRegistrationAuthority) UpdateAuthorization(authz core.Authorization, foo int, challenge core.Challenge) (core.Authorization, error) {
return authz, nil
}
func (ra *MockRegistrationAuthority) RevokeCertificate(cert x509.Certificate) error {
return nil
}
func (ra *MockRegistrationAuthority) OnValidationUpdate(authz core.Authorization) {
}

View File

@ -178,7 +178,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
body, key, err := verifyPOST(request)
if err != nil {
wfe.sendError(response, fmt.Sprintf("Unable to read/verify body: %v", err), http.StatusBadRequest)
wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest)
return
}

View File

@ -6,8 +6,11 @@
package wfe
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
@ -16,6 +19,8 @@ import (
"strings"
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/jose"
@ -278,3 +283,181 @@ func TestChallenge(t *testing.T) {
t, responseWriter.Body.String(),
"{\"type\":\"dns\",\"uri\":\"/acme/authz/asdf?challenge=foo\"}")
}
func TestRegistration(t *testing.T) {
wfe := NewWebFrontEndImpl()
wfe.RA = &MockRegistrationAuthority{}
wfe.Stats, _ = statsd.NewNoopClient()
responseWriter := httptest.NewRecorder()
// GET instead of POST should be rejected
wfe.NewRegistration(responseWriter, &http.Request{
Method: "GET",
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Method not allowed\"}")
// POST, but no body.
responseWriter.Body.Reset()
wfe.NewRegistration(responseWriter, &http.Request{
Method: "POST",
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Unable to read/verify body\"}")
// POST, but body that isn't valid JWS
responseWriter.Body.Reset()
wfe.NewRegistration(responseWriter, &http.Request{
Method: "POST",
Body: makeBody("hi"),
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Unable to read/verify body\"}")
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
responseWriter.Body.Reset()
wfe.NewRegistration(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "Zm9vCg",
"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Error unmarshaling JSON\"}")
// Same signed body, but payload modified by one byte, breaking signature.
// should fail JWS verification.
responseWriter.Body.Reset()
wfe.NewRegistration(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
}
},
"payload": "xm9vCg",
"signature": "RjUQ679fxJgeAJlxqgvDP_sfGZnJ-1RgWF2qmcbnBWljs6h1qp63pLnJOl13u81bP_bCSjaWkelGG8Ymx_X-aQ"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Unable to read/verify body\"}")
key, _ := rsa.GenerateKey(rand.Reader, 512)
jws, err := jose.Sign(jose.RSAPSSWithSHA256, *key, []byte("{\"contact\":[\"tel:123456789\"]}"))
fmt.Println(err)
requestPayload, _ := json.Marshal(jws)
responseWriter.Body.Reset()
wfe.NewRegistration(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(string(requestPayload)),
})
var reg core.Registration
err = json.Unmarshal([]byte(responseWriter.Body.String()), &reg)
test.AssertNotError(t, err, "Couldn't unmarshal returned registration object")
uu := url.URL(reg.Contact[0])
test.AssertEquals(t, uu.String(), "tel:123456789")
}
func TestAuthorization(t *testing.T) {
wfe := NewWebFrontEndImpl()
wfe.RA = &MockRegistrationAuthority{}
wfe.Stats, _ = statsd.NewNoopClient()
responseWriter := httptest.NewRecorder()
// GET instead of POST should be rejected
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "GET",
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Method not allowed\"}")
// POST, but no body.
responseWriter.Body.Reset()
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "POST",
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Unable to read/verify body\"}")
// POST, but body that isn't valid JWS
responseWriter.Body.Reset()
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "POST",
Body: makeBody("hi"),
})
test.AssertEquals(t, responseWriter.Body.String(), "{\"detail\":\"Unable to read/verify body\"}")
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
responseWriter.Body.Reset()
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "Zm9vCg",
"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Error unmarshaling JSON\"}")
// Same signed body, but payload modified by one byte, breaking signature.
// should fail JWS verification.
responseWriter.Body.Reset()
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
}
},
"payload": "xm9vCg",
"signature": "RjUQ679fxJgeAJlxqgvDP_sfGZnJ-1RgWF2qmcbnBWljs6h1qp63pLnJOl13u81bP_bCSjaWkelGG8Ymx_X-aQ"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Unable to read/verify body\"}")
key, _ := rsa.GenerateKey(rand.Reader, 512)
jws, err := jose.Sign(jose.RSAPSSWithSHA256, *key, []byte("{\"identifier\":{\"type\":\"dns\",\"value\":\"test.com\"}}"))
fmt.Println(err)
requestPayload, _ := json.Marshal(jws)
responseWriter.Body.Reset()
wfe.NewAuthorization(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(string(requestPayload)),
})
}