package api_test import ( "encoding/json" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "os" "strings" "testing" "github.com/docker/notary/cryptoservice" "github.com/docker/notary/signer" "github.com/docker/notary/signer/api" "github.com/docker/notary/trustmanager" "github.com/endophage/gotuf/data" "github.com/miekg/pkcs11" "github.com/stretchr/testify/assert" pb "github.com/docker/notary/proto" ) var ( server *httptest.Server reader io.Reader deleteKeyBaseURL string createKeyBaseURL string keyInfoBaseURL string signBaseURL string passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil } ) func SetupHSMEnv(t *testing.T) (*pkcs11.Ctx, pkcs11.SessionHandle) { var libPath = "/usr/local/lib/softhsm/libsofthsm2.so" if _, err := os.Stat(libPath); err != nil { t.Skipf("Skipping test. Library path: %s does not exist", libPath) } p := pkcs11.New(libPath) if p == nil { t.Fatalf("Failed to init library") } if err := p.Initialize(); err != nil { t.Fatalf("Initialize error %s\n", err.Error()) } slots, err := p.GetSlotList(true) if err != nil { t.Fatalf("Failed to list HSM slots %s", err) } session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) if err != nil { t.Fatalf("Failed to Start Session with HSM %s", err) } if err = p.Login(session, pkcs11.CKU_USER, "1234"); err != nil { t.Fatalf("User PIN %s\n", err.Error()) } return p, session } func setup(cryptoServices signer.CryptoServiceIndex) { server = httptest.NewServer(api.Handlers(cryptoServices)) deleteKeyBaseURL = fmt.Sprintf("%s/delete", server.URL) createKeyBaseURL = fmt.Sprintf("%s/new", server.URL) keyInfoBaseURL = fmt.Sprintf("%s", server.URL) signBaseURL = fmt.Sprintf("%s/sign", server.URL) } func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d" keyID := &pb.KeyID{ID: fakeID} requestJson, _ := json.Marshal(keyID) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", deleteKeyBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 404, res.StatusCode) } func TestDeleteKeyHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) tufKey, _ := cryptoService.Create("", data.ED25519Key) assert.NotNil(t, tufKey) requestJson, _ := json.Marshal(&pb.KeyID{ID: tufKey.ID()}) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", deleteKeyBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 200, res.StatusCode) } func TestKeyInfoHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) tufKey, _ := cryptoService.Create("", data.ED25519Key) assert.NotNil(t, tufKey) keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, tufKey.ID()) request, err := http.NewRequest("GET", keyInfoURL, nil) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var pubKey *pb.PublicKey err = json.Unmarshal(jsonBlob, &pubKey) assert.Nil(t, err) assert.Equal(t, tufKey.ID(), pubKey.KeyInfo.KeyID.ID) assert.Equal(t, 200, res.StatusCode) } func TestKeyInfoHandlerReturns404WithNonexistentKey(t *testing.T) { // We associate both key types with this signing service to bypass the // ID -> keyType logic in the tests keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d" keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, fakeID) request, err := http.NewRequest("GET", keyInfoURL, nil) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 404, res.StatusCode) } func TestHSMCreateKeyHandler(t *testing.T) { ctx, session := SetupHSMEnv(t) defer ctx.Destroy() defer ctx.Finalize() defer ctx.CloseSession(session) defer ctx.Logout(session) cryptoService := api.NewRSAHardwareCryptoService(ctx, session) setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService}) createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.RSAKey) request, err := http.NewRequest("POST", createKeyURL, nil) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var keyInfo *pb.PublicKey err = json.Unmarshal(jsonBlob, &keyInfo) assert.Nil(t, err) assert.Equal(t, 200, res.StatusCode) } func TestSoftwareCreateKeyHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.ED25519Key) request, err := http.NewRequest("POST", createKeyURL, nil) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 200, res.StatusCode) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var keyInfo *pb.PublicKey err = json.Unmarshal(jsonBlob, &keyInfo) assert.Nil(t, err) } func TestHSMSignHandler(t *testing.T) { ctx, session := SetupHSMEnv(t) defer ctx.Destroy() defer ctx.Finalize() defer ctx.CloseSession(session) defer ctx.Logout(session) cryptoService := api.NewRSAHardwareCryptoService(ctx, session) setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService}) tufKey, _ := cryptoService.Create("", data.RSAKey) sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: tufKey.ID()}, Content: make([]byte, 10)} requestJson, _ := json.Marshal(sigRequest) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", signBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var sig *pb.Signature err = json.Unmarshal(jsonBlob, &sig) assert.Nil(t, err) assert.Equal(t, tufKey.ID, sig.KeyInfo.KeyID.ID) assert.Equal(t, 200, res.StatusCode) } func TestSoftwareSignHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) tufKey, err := cryptoService.Create("", data.ED25519Key) assert.Nil(t, err) sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: tufKey.ID()}, Content: make([]byte, 10)} requestJson, _ := json.Marshal(sigRequest) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", signBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 200, res.StatusCode) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var sig *pb.Signature err = json.Unmarshal(jsonBlob, &sig) assert.Nil(t, err) assert.Equal(t, tufKey.ID(), sig.KeyInfo.KeyID.ID) } func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) requestJson := "{\"blob\":\"7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df0717e7d7f4a0a2b55debce630f43c4ac099769c612965e3fda3cd4c0078ee6a460f14fa19307\"}" reader = strings.NewReader(requestJson) request, err := http.NewRequest("POST", signBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var sig *pb.Signature err = json.Unmarshal(jsonBlob, &sig) assert.Equal(t, 400, res.StatusCode) } func TestSignHandlerReturns404WithNonexistentKey(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d" cryptoService.Create("", data.ED25519Key) sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: fakeID}, Content: make([]byte, 10)} requestJson, _ := json.Marshal(sigRequest) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", signBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 404, res.StatusCode) }