2015-06-13 17:13:04 +00:00
package acme
import (
2015-10-23 14:24:02 +00:00
"crypto/rsa"
2015-06-13 19:06:47 +00:00
"crypto/tls"
"encoding/json"
2015-06-13 17:13:04 +00:00
"io/ioutil"
"net/http"
2015-06-13 19:06:47 +00:00
"net/http/httptest"
2015-10-30 12:26:22 +00:00
"regexp"
2015-06-13 17:13:04 +00:00
"testing"
2015-06-13 19:06:47 +00:00
"github.com/square/go-jose"
2015-06-13 17:13:04 +00:00
)
2015-10-30 12:13:05 +00:00
func TestSimpleHTTPNonRootBind ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 128 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
solver := & simpleHTTPChallenge { jws : jws }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : "localhost:4000" , Token : "1" }
// validate error on non-root bind to 443
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "BIND: Expected Solve to return an error but the error was nil." )
} else {
expectedError := "Could not start HTTPS server for challenge -> listen tcp :443: bind: permission denied"
if err . Error ( ) != expectedError {
t . Errorf ( "Expected error %s but instead got %s" , expectedError , err . Error ( ) )
}
}
}
func TestSimpleHTTPShortRSA ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 128 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) , nonces : [ ] string { "test1" , "test2" } }
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : "http://localhost:4000" , Token : "2" }
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
} else {
expectedError := "Could not start HTTPS server for challenge -> startHTTPSServer: Failed to sign message. crypto/rsa: message too long for RSA public key size"
if err . Error ( ) != expectedError {
t . Errorf ( "Expected error %s but instead got %s" , expectedError , err . Error ( ) )
}
}
}
func TestSimpleHTTPConnectionRefusal ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) , nonces : [ ] string { "test1" , "test2" } }
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : "http://localhost:4000" , Token : "3" }
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
} else {
2015-10-30 12:26:22 +00:00
reg := "Failed to post JWS message\\. -> Post http:\\/\\/localhost:4000: dial tcp 127\\.0\\.0\\.1:4000: (getsockopt: )?connection refused"
r , _ := regexp . Compile ( reg )
if r . MatchString ( err . Error ( ) ) {
t . Errorf ( "Expected error to match %s but instead got %s" , reg , err . Error ( ) )
2015-10-30 12:13:05 +00:00
}
2015-06-13 19:06:47 +00:00
}
2015-10-30 12:13:05 +00:00
}
func TestSimpleHTTPUnexpectedServerState ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
2015-10-23 14:24:02 +00:00
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
2015-06-13 19:06:47 +00:00
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2015-09-26 17:45:52 +00:00
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
2015-10-30 12:13:05 +00:00
w . Write ( [ ] byte ( "{\"type\":\"simpleHttp\",\"status\":\"what\",\"uri\":\"http://some.url\",\"token\":\"4\"}" ) )
2015-06-13 19:06:47 +00:00
} ) )
2015-10-30 12:13:05 +00:00
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "4" }
2015-06-13 19:06:47 +00:00
2015-10-30 12:13:05 +00:00
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
} else {
expectedError := "The server returned an unexpected state."
if err . Error ( ) != expectedError {
t . Errorf ( "Expected error %s but instead got %s" , expectedError , err . Error ( ) )
}
2015-06-13 19:06:47 +00:00
}
2015-10-30 12:13:05 +00:00
}
func TestSimpleHTTPChallengeServerUnexpectedDomain ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if r . Method == "POST" {
tr := & http . Transport {
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
}
client := & http . Client { Transport : tr }
req , _ := client . Get ( "https://localhost:23456/.well-known/acme-challenge/" + "5" )
reqBytes , _ := ioutil . ReadAll ( req . Body )
if string ( reqBytes ) != "TEST" {
t . Error ( "Expected simpleHTTP server to return string TEST on unexpected domain." )
}
}
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
w . Write ( [ ] byte ( "{\"type\":\"simpleHttp\",\"status\":\"invalid\",\"uri\":\"http://some.url\",\"token\":\"5\"}" ) )
} ) )
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "5" }
2015-06-13 19:06:47 +00:00
2015-10-30 12:13:05 +00:00
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
2015-06-13 19:06:47 +00:00
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
}
2015-10-30 12:13:05 +00:00
}
2015-06-13 19:06:47 +00:00
2015-10-30 12:13:05 +00:00
func TestSimpleHTTPServerError ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if r . Method == "HEAD" {
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
} else {
w . WriteHeader ( http . StatusInternalServerError )
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
w . Write ( [ ] byte ( "{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new authz :: Syntax error\"}" ) )
}
} ) )
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "6" }
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
} else {
expectedError := "[500] Type: urn:acme:error:unauthorized Detail: Error creating new authz :: Syntax error"
if err . Error ( ) != expectedError {
t . Errorf ( "Expected error |%s| but instead got |%s|" , expectedError , err . Error ( ) )
}
}
}
func TestSimpleHTTPInvalidServerState ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2015-09-26 17:45:52 +00:00
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
2015-10-30 12:13:05 +00:00
w . Write ( [ ] byte ( "{\"type\":\"simpleHttp\",\"status\":\"invalid\",\"uri\":\"http://some.url\",\"token\":\"7\"}" ) )
} ) )
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "7" }
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err == nil {
t . Error ( "UNEXPECTED: Expected Solve to return an error but the error was nil." )
} else {
expectedError := "The server could not validate our request."
if err . Error ( ) != expectedError {
t . Errorf ( "Expected error |%s| but instead got |%s|" , expectedError , err . Error ( ) )
}
2015-06-13 19:06:47 +00:00
}
2015-10-30 12:13:05 +00:00
}
2015-06-13 19:06:47 +00:00
2015-10-30 12:13:05 +00:00
func TestSimpleHTTPValidServerResponse ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2015-09-26 17:45:52 +00:00
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
2015-10-30 12:13:05 +00:00
w . Write ( [ ] byte ( "{\"type\":\"simpleHttp\",\"status\":\"valid\",\"uri\":\"http://some.url\",\"token\":\"8\"}" ) )
} ) )
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "8" }
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err != nil {
2015-06-13 19:06:47 +00:00
t . Errorf ( "VALID: Expected Solve to return no error but the error was -> %v" , err )
}
2015-10-30 12:13:05 +00:00
}
func TestSimpleHTTPValidFull ( t * testing . T ) {
privKey , _ := generatePrivateKey ( rsakey , 512 )
jws := & jws { privKey : privKey . ( * rsa . PrivateKey ) }
ts := httptest . NewServer ( nil )
solver := & simpleHTTPChallenge { jws : jws , optPort : "23456" }
clientChallenge := challenge { Type : "simpleHttp" , Status : "pending" , URI : ts . URL , Token : "9" }
2015-06-13 19:06:47 +00:00
2015-10-18 00:09:19 +00:00
// Validate server on port 23456 which responds appropriately
2015-09-26 17:45:52 +00:00
clientChallenge . Token = "1234567812"
2015-06-13 19:06:47 +00:00
ts . Config . Handler = http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
var request challenge
2015-09-26 17:45:52 +00:00
w . Header ( ) . Add ( "Replay-Nonce" , "12345" )
if r . Method == "HEAD" {
return
}
2015-06-13 19:06:47 +00:00
clientJws , _ := ioutil . ReadAll ( r . Body )
j , err := jose . ParseSigned ( string ( clientJws ) )
if err != nil {
2015-09-26 17:45:52 +00:00
t . Errorf ( "Client sent invalid JWS to the server.\n\t%v" , err )
return
2015-06-13 19:06:47 +00:00
}
2015-10-23 14:24:02 +00:00
output , err := j . Verify ( & privKey . ( * rsa . PrivateKey ) . PublicKey )
2015-06-13 19:06:47 +00:00
if err != nil {
t . Errorf ( "Unable to verify client data -> %v" , err )
}
json . Unmarshal ( output , & request )
transport := & http . Transport { TLSClientConfig : & tls . Config { InsecureSkipVerify : true } }
client := & http . Client { Transport : transport }
2015-10-18 00:09:19 +00:00
reqURL := "https://localhost:23456/.well-known/acme-challenge/" + clientChallenge . Token
2015-06-13 19:06:47 +00:00
t . Logf ( "Request URL is: %s" , reqURL )
req , _ := http . NewRequest ( "GET" , reqURL , nil )
req . Host = "test.domain"
resp , err := client . Do ( req )
if err != nil {
2015-10-18 00:09:19 +00:00
t . Errorf ( "Expected the solver to listen on port 23456 -> %v" , err )
2015-06-13 19:06:47 +00:00
}
2015-10-22 04:16:29 +00:00
defer resp . Body . Close ( )
2015-06-13 19:06:47 +00:00
body , _ := ioutil . ReadAll ( resp . Body )
bodyStr := string ( body )
2015-09-26 17:45:52 +00:00
clientResponse , err := jose . ParseSigned ( bodyStr )
if err != nil {
t . Errorf ( "Client answered with invalid JWS.\n\t%v" , err )
return
}
2015-10-23 14:24:02 +00:00
_ , err = clientResponse . Verify ( & privKey . ( * rsa . PrivateKey ) . PublicKey )
2015-09-26 17:45:52 +00:00
if err != nil {
t . Errorf ( "Unable to verify client data -> %v" , err )
2015-06-13 19:06:47 +00:00
}
2015-09-26 17:45:52 +00:00
valid := challenge { Type : "simpleHttp" , Status : "valid" , URI : ts . URL , Token : "1234567812" }
2015-06-13 19:06:47 +00:00
jsonBytes , _ := json . Marshal ( & valid )
w . Write ( jsonBytes )
} )
2015-10-30 12:13:05 +00:00
if err := solver . Solve ( clientChallenge , "test.domain" ) ; err != nil {
2015-06-13 19:06:47 +00:00
t . Errorf ( "VALID: Expected Solve to return no error but the error was -> %v" , err )
}
}