Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permit 0-length inputs to Sign(Update), etc #82

Merged
merged 3 commits into from
Oct 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions pkcs11.go
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ func (c *Ctx) Encrypt(sh SessionHandle, message []byte) ([]byte, error) {
enc C.CK_BYTE_PTR
enclen C.CK_ULONG
)
e := C.Encrypt(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &enc, &enclen)
e := C.Encrypt(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &enc, &enclen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1222,7 +1222,7 @@ func (c *Ctx) EncryptUpdate(sh SessionHandle, plain []byte) ([]byte, error) {
part C.CK_BYTE_PTR
partlen C.CK_ULONG
)
e := C.EncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&plain[0])), C.CK_ULONG(len(plain)), &part, &partlen)
e := C.EncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(plain), C.CK_ULONG(len(plain)), &part, &partlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand Down Expand Up @@ -1260,7 +1260,7 @@ func (c *Ctx) Decrypt(sh SessionHandle, cipher []byte) ([]byte, error) {
plain C.CK_BYTE_PTR
plainlen C.CK_ULONG
)
e := C.Decrypt(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&cipher[0])), C.CK_ULONG(len(cipher)), &plain, &plainlen)
e := C.Decrypt(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &plain, &plainlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1275,7 +1275,7 @@ func (c *Ctx) DecryptUpdate(sh SessionHandle, cipher []byte) ([]byte, error) {
part C.CK_BYTE_PTR
partlen C.CK_ULONG
)
e := C.DecryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&cipher[0])), C.CK_ULONG(len(cipher)), &part, &partlen)
e := C.DecryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand Down Expand Up @@ -1313,7 +1313,7 @@ func (c *Ctx) Digest(sh SessionHandle, message []byte) ([]byte, error) {
hash C.CK_BYTE_PTR
hashlen C.CK_ULONG
)
e := C.Digest(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &hash, &hashlen)
e := C.Digest(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &hash, &hashlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1324,7 +1324,7 @@ func (c *Ctx) Digest(sh SessionHandle, message []byte) ([]byte, error) {

// DigestUpdate continues a multiple-part message-digesting operation.
func (c *Ctx) DigestUpdate(sh SessionHandle, message []byte) error {
e := C.DigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)))
e := C.DigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)))
if toError(e) != nil {
return toError(e)
}
Expand Down Expand Up @@ -1374,7 +1374,7 @@ func (c *Ctx) Sign(sh SessionHandle, message []byte) ([]byte, error) {
sig C.CK_BYTE_PTR
siglen C.CK_ULONG
)
e := C.Sign(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &sig, &siglen)
e := C.Sign(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &sig, &siglen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1387,7 +1387,7 @@ func (c *Ctx) Sign(sh SessionHandle, message []byte) ([]byte, error) {
// where the signature is (will be) an appendix to the data,
// and plaintext cannot be recovered from the signature.
func (c *Ctx) SignUpdate(sh SessionHandle, message []byte) error {
e := C.SignUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)))
e := C.SignUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)))
return toError(e)
}

Expand Down Expand Up @@ -1420,7 +1420,7 @@ func (c *Ctx) SignRecover(sh SessionHandle, data []byte) ([]byte, error) {
sig C.CK_BYTE_PTR
siglen C.CK_ULONG
)
e := C.SignRecover(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&data[0])), C.CK_ULONG(len(data)), &sig, &siglen)
e := C.SignRecover(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(data), C.CK_ULONG(len(data)), &sig, &siglen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1443,22 +1443,22 @@ func (c *Ctx) VerifyInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) err
// where the signature is an appendix to the data, and plaintext
// cannot be recovered from the signature.
func (c *Ctx) Verify(sh SessionHandle, data []byte, signature []byte) error {
e := C.Verify(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&data[0])), C.CK_ULONG(len(data)), C.CK_BYTE_PTR(unsafe.Pointer(&signature[0])), C.CK_ULONG(len(signature)))
e := C.Verify(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(data), C.CK_ULONG(len(data)), cMessage(signature), C.CK_ULONG(len(signature)))
return toError(e)
}

// VerifyUpdate continues a multiple-part verification
// operation, where the signature is an appendix to the data,
// and plaintext cannot be recovered from the signature.
func (c *Ctx) VerifyUpdate(sh SessionHandle, part []byte) error {
e := C.VerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&part[0])), C.CK_ULONG(len(part)))
e := C.VerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)))
return toError(e)
}

// VerifyFinal finishes a multiple-part verification
// operation, checking the signature.
func (c *Ctx) VerifyFinal(sh SessionHandle, signature []byte) error {
e := C.VerifyFinal(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&signature[0])), C.CK_ULONG(len(signature)))
e := C.VerifyFinal(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(signature), C.CK_ULONG(len(signature)))
return toError(e)
}

Expand All @@ -1478,7 +1478,7 @@ func (c *Ctx) VerifyRecover(sh SessionHandle, signature []byte) ([]byte, error)
data C.CK_BYTE_PTR
datalen C.CK_ULONG
)
e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&signature[0])), C.CK_ULONG(len(signature)), &data, &datalen)
e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(signature), C.CK_ULONG(len(signature)), &data, &datalen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1493,7 +1493,7 @@ func (c *Ctx) DigestEncryptUpdate(sh SessionHandle, part []byte) ([]byte, error)
enc C.CK_BYTE_PTR
enclen C.CK_ULONG
)
e := C.DigestEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&part[0])), C.CK_ULONG(len(part)), &enc, &enclen)
e := C.DigestEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)), &enc, &enclen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1508,7 +1508,7 @@ func (c *Ctx) DecryptDigestUpdate(sh SessionHandle, cipher []byte) ([]byte, erro
part C.CK_BYTE_PTR
partlen C.CK_ULONG
)
e := C.DecryptDigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&cipher[0])), C.CK_ULONG(len(cipher)), &part, &partlen)
e := C.DecryptDigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1523,7 +1523,7 @@ func (c *Ctx) SignEncryptUpdate(sh SessionHandle, part []byte) ([]byte, error) {
enc C.CK_BYTE_PTR
enclen C.CK_ULONG
)
e := C.SignEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&part[0])), C.CK_ULONG(len(part)), &enc, &enclen)
e := C.SignEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)), &enc, &enclen)
if toError(e) != nil {
return nil, toError(e)
}
Expand All @@ -1538,7 +1538,7 @@ func (c *Ctx) DecryptVerifyUpdate(sh SessionHandle, cipher []byte) ([]byte, erro
part C.CK_BYTE_PTR
partlen C.CK_ULONG
)
e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&cipher[0])), C.CK_ULONG(len(cipher)), &part, &partlen)
e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
if toError(e) != nil {
return nil, toError(e)
}
Expand Down
165 changes: 153 additions & 12 deletions pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package pkcs11
// in /usr/lib/softhsm/libsofthsm.so

import (
"bytes"
"fmt"
"log"
"math/big"
Expand Down Expand Up @@ -166,21 +167,31 @@ func TestDigest(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer finishSession(p, session)
t.Run("Simple", func(t *testing.T) {
// Teststring create with: echo -n "this is a string" | sha1sum
testDigest(t, p, session, []byte("this is a string"), "517592df8fec3ad146a79a9af153db2a4d784ec5")
})
t.Run("Empty", func(t *testing.T) {
// sha1sum < /dev/null
testDigest(t, p, session, []byte{}, "da39a3ee5e6b4b0d3255bfef95601890afd80709")
})
}

func testDigest(t *testing.T, p *Ctx, session SessionHandle, input []byte, expected string) {
e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)})
if e != nil {
t.Fatalf("DigestInit: %s\n", e)
}

hash, e := p.Digest(session, []byte("this is a string"))
hash, e := p.Digest(session, input)
if e != nil {
t.Fatalf("digest: %s\n", e)
}
hex := ""
for _, d := range hash {
hex += fmt.Sprintf("%x", d)
hex += fmt.Sprintf("%02x", d)
}
// Teststring create with: echo -n "this is a string" | sha1sum
if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
if hex != expected {
t.Fatalf("wrong digest: %s", hex)
}
}
Expand All @@ -189,25 +200,34 @@ func TestDigestUpdate(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer finishSession(p, session)
t.Run("Simple", func(t *testing.T) {
// Teststring create with: echo -n "this is a string" | sha1sum
testDigestUpdate(t, p, session, [][]byte{[]byte("this is "), []byte("a string")}, "517592df8fec3ad146a79a9af153db2a4d784ec5")
})
t.Run("Empty", func(t *testing.T) {
// sha1sum < /dev/null
testDigestUpdate(t, p, session, [][]byte{[]byte{}}, "da39a3ee5e6b4b0d3255bfef95601890afd80709")
})
}

func testDigestUpdate(t *testing.T, p *Ctx, session SessionHandle, inputs [][]byte, expected string) {
if e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)}); e != nil {
t.Fatalf("DigestInit: %s\n", e)
}
if e := p.DigestUpdate(session, []byte("this is ")); e != nil {
t.Fatalf("DigestUpdate: %s\n", e)
}
if e := p.DigestUpdate(session, []byte("a string")); e != nil {
t.Fatalf("DigestUpdate: %s\n", e)
for _, input := range inputs {
if e := p.DigestUpdate(session, input); e != nil {
t.Fatalf("DigestUpdate: %s\n", e)
}
}
hash, e := p.DigestFinal(session)
if e != nil {
t.Fatalf("DigestFinal: %s\n", e)
}
hex := ""
for _, d := range hash {
hex += fmt.Sprintf("%x", d)
hex += fmt.Sprintf("%02x", d)
}
// Teststring create with: echo -n "this is a string" | sha1sum
if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
if hex != expected {
t.Fatalf("wrong digest: %s", hex)
}
}
Expand Down Expand Up @@ -333,6 +353,127 @@ func TestDestroyObject(t *testing.T) {

}

func TestSymmetricEncryption(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer finishSession(p, session)
if info, err := p.GetInfo(); err != nil {
t.Errorf("GetInfo: %v", err)
return
} else if info.ManufacturerID == "SoftHSM" && info.LibraryVersion.Major < 2 {
t.Skipf("AES not implemented on SoftHSM")
}
tokenLabel := "TestGenerateKey"
keyTemplate := []*Attribute{
NewAttribute(CKA_TOKEN, false),
NewAttribute(CKA_ENCRYPT, true),
NewAttribute(CKA_DECRYPT, true),
NewAttribute(CKA_LABEL, tokenLabel),
NewAttribute(CKA_SENSITIVE, true),
NewAttribute(CKA_EXTRACTABLE, true),
NewAttribute(CKA_VALUE_LEN, 16),
}
key, err := p.GenerateKey(session,
[]*Mechanism{NewMechanism(CKM_AES_KEY_GEN, nil)},
keyTemplate)
if err != nil {
t.Fatalf("failed to generate keypair: %s\n", err)
}
t.Run("Encrypt", func(t *testing.T) {
t.Run("ECB", func(t *testing.T) {
testEncrypt(t, p, session, key, CKM_AES_ECB, make([]byte, 32), nil)
})
t.Run("CBC", func(t *testing.T) {
testEncrypt(t, p, session, key, CKM_AES_CBC, make([]byte, 32), make([]byte, 16))
})
t.Run("CBC-PAD", func(t *testing.T) {
testEncrypt(t, p, session, key, CKM_AES_CBC_PAD, make([]byte, 31), make([]byte, 16))
})
/* Broken in SoftHSMv2
t.Run("Empty", func(t *testing.T) {
testEncrypt(t, p, session, key, CKM_AES_CBC, []byte{}, make([]byte, 16))
})
*/
})
t.Run("EncryptUpdate", func(t *testing.T) {
t.Run("ECB", func(t *testing.T) {
testEncryptUpdate(t, p, session, key, CKM_AES_ECB, [][]byte{make([]byte, 32), make([]byte, 16)}, nil)
})
t.Run("CBC", func(t *testing.T) {
testEncryptUpdate(t, p, session, key, CKM_AES_CBC, [][]byte{make([]byte, 32), make([]byte, 16)}, make([]byte, 16))
})
t.Run("CBC-PAD", func(t *testing.T) {
testEncryptUpdate(t, p, session, key, CKM_AES_CBC_PAD, [][]byte{make([]byte, 11), make([]byte, 20)}, make([]byte, 16))
})
/* Broken in SoftHSMv2
t.Run("Empty", func(t *testing.T) {
testEncryptUpdate(t, p, session, key, CKM_AES_CBC, [][]byte{make([]byte, 31), []byte{}}, make([]byte, 16))
})
*/
})
}

func testEncrypt(t *testing.T, p *Ctx, session SessionHandle, key ObjectHandle, mech uint, plaintext []byte, iv []byte) {
var err error
if err = p.EncryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
t.Fatalf("EncryptInit: %s\n", err)
}
var ciphertext []byte
if ciphertext, err = p.Encrypt(session, plaintext); err != nil {
t.Fatalf("Encrypt: %s\n", err)
}
if err = p.DecryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
t.Fatalf("DecryptInit: %s\n", err)
}
var decrypted []byte
if decrypted, err = p.Decrypt(session, ciphertext); err != nil {
t.Fatalf("Decrypt: %s\n", err)
}
if bytes.Compare(plaintext, decrypted) != 0 {
t.Fatalf("Plaintext mismatch")
}
}

func testEncryptUpdate(t *testing.T, p *Ctx, session SessionHandle, key ObjectHandle, mech uint, plaintexts [][]byte, iv []byte) {
var err error
if err = p.EncryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
t.Fatalf("EncryptInit: %s\n", err)
}
var ciphertexts [][]byte
var output, plaintext []byte
for _, input := range plaintexts {
plaintext = append(plaintext, input...)
if output, err = p.EncryptUpdate(session, input); err != nil {
t.Fatalf("EncryptUpdate: %s\n", err)
}
ciphertexts = append(ciphertexts, output)
}
if output, err = p.EncryptFinal(session); err != nil {
t.Fatalf("EncryptFinal: %s\n", err)
}
ciphertexts = append(ciphertexts, output)
if err = p.DecryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
t.Fatalf("DecryptInit: %s\n", err)
}
var decrypted []byte
for _, input := range ciphertexts {
if len(input) == 0 { // Broken in SoftHSMv2
continue
}
if output, err = p.DecryptUpdate(session, input); err != nil {
t.Fatalf("DecryptUpdate: %s\n", err)
}
decrypted = append(decrypted, output...)
}
if output, err = p.DecryptFinal(session); err != nil {
t.Fatalf("DecryptFinal: %s\n", err)
}
decrypted = append(decrypted, output...)
if bytes.Compare(plaintext, decrypted) != 0 {
t.Fatalf("Plaintext mismatch")
}
}

// ExampleSign shows how to sign some data with a private key.
// Note: error correction is not implemented in this example.
func ExampleCtx_Sign() {
Expand Down
13 changes: 13 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,16 @@ type MechanismInfo struct {
MaxKeySize uint
Flags uint
}

// stubData is a persistent nonempty byte array used by cMessage.
var stubData = []byte{0}

// cMessage returns the pointer/length pair corresponding to data.
func cMessage(data []byte) (dataPtr C.CK_BYTE_PTR) {
l := len(data)
if l == 0 {
// &data[0] is forbidden in this case, so use a nontrivial array instead.
data = stubData
}
return C.CK_BYTE_PTR(unsafe.Pointer(&data[0]))
}