go-mockid/mockid/mockid_test.go

347 lines
6.8 KiB
Go

package mockid
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
mathrand "math/rand"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"git.coolaj86.com/coolaj86/go-mockid/mockid/api"
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs"
"git.rootprojects.org/root/keypairs"
//keypairs "github.com/big-squid/go-keypairs"
//"github.com/big-squid/go-keypairs/keyfetch/uncached"
)
var srv *httptest.Server
type TestReader struct{}
func (TestReader) Read(p []byte) (n int, err error) {
return mathrand.Read(p)
}
var testrnd = TestReader{}
func init() {
api.RandomReader = testrnd
rndsrc = testrnd
}
func TestMain(m *testing.M) {
mathrand.Seed(0) // Predictable results
os.Setenv("SALT", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
jwksPrefix := "public-jwks"
err := os.MkdirAll(jwksPrefix, 0755)
if nil != err {
fmt.Fprintf(os.Stderr, "couldn't write %q: %s", jwksPrefix, err)
os.Exit(1)
}
privkey, _ := ecdsa.GenerateKey(elliptic.P256(), rndsrc)
mux := Route(jwksPrefix, privkey)
srv = httptest.NewServer(mux)
//fs := http.FileServer(http.Dir("public"))
//http.Handle("/", fs)
os.Exit(m.Run())
}
func TestGenerateJWK(t *testing.T) {
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
//fmt.Println("URL:", srv.URL, urlstr)
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
data, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
jwk := map[string]string{}
err = json.Unmarshal(data, &jwk)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
if "" == jwk["d"] {
t.Fatal("Missing key 'd' from supposed private key")
}
key, err := keypairs.ParsePrivateKey(data)
if nil != err {
t.Error(err)
return
}
switch key.(type) {
case *rsa.PrivateKey:
// no-op
//log.Println("is RSA")
case *ecdsa.PrivateKey:
// no-op
//log.Println("is EC")
default:
t.Fatal(errors.New("impossible key type"))
}
//fmt.Printf("%#v\n", jwk)
}
func TestGenWithSeed(t *testing.T) {
// Key A
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"test"}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
dataA, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
// See https://github.com/square/go-jose/issues/189
for i := 0; i < 8; i++ {
// Key B
client = srv.Client()
urlstr, _ = url.Parse(srv.URL + "/private.jwk.json")
res, err = client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"test"}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
dataB, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
if '{' != dataA[0] || len(dataA) < 100 || string(dataA) != string(dataB) {
log.Println(string(dataA))
log.Println(string(dataB))
t.Error(errors.New("keys with identical seeds should be identical"))
return
}
}
}
func TestGenWithRand(t *testing.T) {
// Key A
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/private.jwk.json")
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":""}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
dataA, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
// Key B
client = srv.Client()
urlstr, _ = url.Parse(srv.URL + "/private.jwk.json")
res, err = client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":""}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
dataB, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
if string(dataA) == string(dataB) {
t.Error(errors.New("keys with identical seeds should yield identical keys"))
return
}
}
func TestGeneratePEM(t *testing.T) {
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/priv.pem")
//fmt.Println("URL:", srv.URL, urlstr)
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
data, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
key, err := xkeypairs.ParsePEMPrivateKey(data)
if nil != err {
t.Error(err)
return
}
switch key.(type) {
case *rsa.PrivateKey:
// no-op
//log.Println("is RSA")
case *ecdsa.PrivateKey:
// no-op
//log.Println("is EC")
default:
t.Fatal(errors.New("impossible key type"))
}
}
func TestPublicJWKWithKey(t *testing.T) {
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/public.jwk.json")
//fmt.Println("URL:", srv.URL, urlstr)
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"key":"{\"crv\":\"P-256\",\"d\":\"s0YhjGUJpp6OvyuNS_4igrc7ddDZy5N2ANxoQm7E5sc\",\"kty\":\"EC\",\"x\":\"hPsE4OMhpd2TvrhjDgr1BhF-L1n4O-gPm1flwTh5kzo\",\"y\":\"BWZ1naEJuNOdnQ4HmbHavqdLKxoj77Fu8mkJPjSuh54\"}"}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
data, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
jwk := map[string]string{}
err = json.Unmarshal(data, &jwk)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
if "" != jwk["d"] {
t.Fatal("Has private key 'd' from supposed public key")
}
if "hPsE4OMhpd2TvrhjDgr1BhF-L1n4O-gPm1flwTh5kzo" != jwk["x"] {
t.Fatal("Missing public key 'x' or 'e' from supposed public key")
}
key, err := keypairs.ParsePublicKey(data)
if nil != err {
t.Error(err)
return
}
switch key.Key().(type) {
case *ecdsa.PublicKey:
// no-op
//log.Println("is EC")
default:
t.Fatal(errors.New("impossible key type"))
}
}
func TestPublicPEMWithSeed(t *testing.T) {
client := srv.Client()
urlstr, _ := url.Parse(srv.URL + "/pub.pem")
//fmt.Println("URL:", srv.URL, urlstr)
res, err := client.Do(&http.Request{
Method: "POST",
URL: urlstr,
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"test"}`))),
})
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
data, err := ioutil.ReadAll(res.Body)
if nil != err {
//t.Fatal(err)
t.Error(err)
return
}
key, err := keypairs.ParsePublicKey(data)
if nil != err {
t.Error(err)
return
}
switch key.Key().(type) {
case *rsa.PublicKey:
// no-op
//log.Println("is RSA")
case *ecdsa.PublicKey:
// no-op
//log.Println("is EC")
default:
t.Fatal(errors.New("impossible key type"))
}
}