// Copyright 2016, 2017 Thales e-Security, Inc // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package crypto11 import ( "encoding/json" "fmt" "log" "math/rand" "os" "testing" "time" "github.com/miekg/pkcs11" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestKeysPersistAcrossContexts(t *testing.T) { // Verify that close and re-open works. ctx, err := ConfigureFromFile("config") require.NoError(t, err) id := randomBytes() _, err = ctx.GenerateRSAKeyPair(id, rsaSize) if err != nil { _ = ctx.Close() t.Fatal(err) } require.NoError(t, ctx.Close()) ctx, err = ConfigureFromFile("config") require.NoError(t, err) key2, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) testRsaSigning(t, key2, false) _ = key2.Delete() require.NoError(t, ctx.Close()) } func configureWithPin(t *testing.T) (*Context, error) { cfg, err := getConfig("config") if err != nil { t.Fatal(err) } if cfg.Pin == "" { t.Fatal("invalid configuration. configuration must have PIN non empty.") } ctx, err := Configure(cfg) if err != nil { t.Fatal("failed to configure service:", err) } return ctx, nil } func getConfig(configLocation string) (ctx *Config, err error) { file, err := os.Open(configLocation) if err != nil { log.Printf("Could not open config file: %s", configLocation) return nil, err } defer func() { err = file.Close() }() configDecoder := json.NewDecoder(file) config := &Config{} err = configDecoder.Decode(config) if err != nil { log.Printf("Could decode config file: %s", err.Error()) return nil, err } return config, nil } func TestKeyPairDelete(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, 2048) require.NoError(t, err) // Check we can find it _, err = ctx.FindKeyPair(id, nil) require.NoError(t, err) err = key.Delete() require.NoError(t, err) k, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) require.Nil(t, k) } func TestKeyDelete(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateSecretKey(id, 128, CipherAES) require.NoError(t, err) // Check we can find it _, err = ctx.FindKey(id, nil) require.NoError(t, err) err = key.Delete() require.NoError(t, err) k, err := ctx.FindKey(id, nil) require.NoError(t, err) require.Nil(t, k) } func TestAmbiguousTokenConfig(t *testing.T) { slotNum := 1 tests := []struct { config *Config err string }{ { config: &Config{TokenSerial: "serial", TokenLabel: "label"}, err: "config must specify exactly one way to select a token: token label, token serial number given", }, { config: &Config{TokenSerial: "serial", SlotNumber: &slotNum}, err: "config must specify exactly one way to select a token: slot number, token serial number given", }, { config: &Config{SlotNumber: &slotNum, TokenLabel: "label"}, err: "config must specify exactly one way to select a token: slot number, token label given", }, { config: &Config{}, err: "config must specify exactly one way to select a token: none given", }, } for i, test := range tests { t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { _, err := Configure(test.config) if assert.Error(t, err) { assert.Equal(t, test.err, err.Error()) } }) } } func TestSelectBySlot(t *testing.T) { config, err := loadConfigFromFile("config") require.NoError(t, err) // Look up slot number for label ctx, err := Configure(config) require.NoError(t, err) slotNumber := int(ctx.slot) t.Logf("Using slot %d", slotNumber) err = ctx.Close() require.NoError(t, err) slotConfig := &Config{ SlotNumber: &slotNumber, Pin: config.Pin, Path: config.Path, } ctx, err = Configure(slotConfig) require.NoError(t, err) slotNumber2 := int(ctx.slot) err = ctx.Close() require.NoError(t, err) assert.Equal(t, slotNumber, slotNumber2) } func TestSelectByNonExistingSlot(t *testing.T) { config, err := loadConfigFromFile("config") require.NoError(t, err) rand.Seed(time.Now().UnixNano()) randomSlot := int(rand.Uint32()) config.TokenLabel = "" config.TokenSerial = "" config.SlotNumber = &randomSlot // Look up slot number for label _, err = Configure(config) require.Equal(t, errTokenNotFound, err) } func TestAccessSameLibraryTwice(t *testing.T) { ctx1, err := ConfigureFromFile("config") require.NoError(t, err) ctx2, err := ConfigureFromFile("config") require.NoError(t, err) // Close the first context, which shouldn't render the second // context unusable err = ctx1.Close() require.NoError(t, err) // Try to find a non-existant key. We are just checking that we can // use the underlying P11 lib. _, err = ctx2.FindKey(randomBytes(), nil) require.NoError(t, err) err = ctx2.Close() require.NoError(t, err) // Check we can open this again and use it without error ctx3, err := ConfigureFromFile("config") require.NoError(t, err) // Try to find a non-existant key. We are just checking that we can // use the underlying P11 lib. _, err = ctx3.FindKey(randomBytes(), nil) require.NoError(t, err) err = ctx3.Close() require.NoError(t, err) } func TestNoLogin(t *testing.T) { // To test that no login is respected, we attempt to perform an operation on our // SoftHSM HSM without logging in and check for the error. cfg, err := getConfig("config") require.NoError(t, err) cfg.LoginNotSupported = true ctx, err := Configure(cfg) require.NoError(t, err) _, err = ctx.GenerateSecretKey(randomBytes(), 256, CipherAES) require.Error(t, err) p11Err, ok := err.(pkcs11.Error) require.True(t, ok) assert.Equal(t, pkcs11.Error(pkcs11.CKR_USER_NOT_LOGGED_IN), p11Err) } func TestInvalidMaxSessions(t *testing.T) { cfg, err := getConfig("config") require.NoError(t, err) cfg.MaxSessions = 1 _, err = Configure(cfg) require.Error(t, err) } // randomBytes returns 32 random bytes. func randomBytes() []byte { result := make([]byte, 32) rand.Read(result) return result } func init() { rand.Seed(time.Now().UnixNano()) }