Skip to content

Commit

Permalink
fix: Update API signatures for encrypt/decrypt to conform to crypto.D…
Browse files Browse the repository at this point in the history
…ecrypter interface (#96)
  • Loading branch information
andyrzhao committed Sep 20, 2023
1 parent 8331df2 commit c6a4f15
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 84 deletions.
29 changes: 18 additions & 11 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,28 @@ func (c *Connection) Close() error {

func init() {
gob.Register(crypto.SHA256)
gob.Register(crypto.SHA384)
gob.Register(crypto.SHA512)
gob.Register(&rsa.PSSOptions{})
gob.Register(&rsa.OAEPOptions{})
}

// SignArgs contains arguments to a crypto Signer.Sign method.
// SignArgs contains arguments for a Sign API call.
type SignArgs struct {
Digest []byte // The content to sign.
Opts crypto.SignerOpts // Options for signing, such as Hash identifier.
Opts crypto.SignerOpts // Options for signing. Must implement HashFunc().
}

// EncryptArgs contains arguments for an Encrypt API call.
type EncryptArgs struct {
Plaintext []byte
Hash crypto.Hash
Plaintext []byte // The plaintext to encrypt.
Opts any // Options for encryption. Ex: an instance of crypto.Hash.
}

// DecryptArgs contains arguments to for a Decrypt API call.
type DecryptArgs struct {
Ciphertext []byte
Hash crypto.Hash
Ciphertext []byte // The ciphertext to decrypt.
Opts crypto.DecrypterOpts // Options for decryption. Ex: an instance of *rsa.OAEPOptions.
}

// Key implements credential.Credential by holding the executed signer subprocess.
Expand Down Expand Up @@ -110,7 +115,7 @@ func (k *Key) Public() crypto.PublicKey {
return k.publicKey
}

// Sign signs a message digest, using the specified signer options.
// Sign signs a message digest, using the specified signer opts. Implements crypto.Signer interface.
func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed []byte, err error) {
if opts != nil && opts.HashFunc() != 0 && len(digest) != opts.HashFunc().Size() {
return nil, fmt.Errorf("Digest length of %v bytes does not match Hash function size of %v bytes", len(digest), opts.HashFunc().Size())
Expand All @@ -119,13 +124,15 @@ func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed [
return
}

func (k *Key) Encrypt(plaintext []byte) (ciphertext []byte, err error) {
err = k.client.Call(encryptAPI, EncryptArgs{Plaintext: plaintext, Hash: crypto.SHA256}, &ciphertext)
// Encrypt encrypts a plaintext msg into ciphertext, using the specified encrypt opts.
func (k *Key) Encrypt(_ io.Reader, msg []byte, opts any) (ciphertext []byte, err error) {
err = k.client.Call(encryptAPI, EncryptArgs{Plaintext: msg, Opts: opts}, &ciphertext)
return
}

func (k *Key) Decrypt(ciphertext []byte) (plaintext []byte, err error) {
err = k.client.Call(decryptAPI, DecryptArgs{Ciphertext: ciphertext, Hash: crypto.SHA256}, &plaintext)
// Decrypt decrypts a ciphertext msg into plaintext, using the specified decrypter opts. Implements crypto.Decrypter interface.
func (k *Key) Decrypt(_ io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
err = k.client.Call(decryptAPI, DecryptArgs{Ciphertext: msg, Opts: opts}, &plaintext)
return
}

Expand Down
7 changes: 4 additions & 3 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package client
import (
"bytes"
"crypto"
"crypto/rsa"
"encoding/json"
"errors"
"os"
Expand Down Expand Up @@ -103,7 +104,7 @@ func TestClientEncrypt(t *testing.T) {
t.Fatal(err)
}
plaintext := []byte("Plain text to encrypt")
_, err = key.Encrypt(plaintext)
_, err = key.Encrypt(nil, plaintext, crypto.SHA256)
if err != nil {
t.Errorf("Universal Client API encryption: got %v, want nil err", err)
return
Expand All @@ -116,8 +117,8 @@ func TestClientDecrypt(t *testing.T) {
t.Fatal(err)
}
byteSlice := []byte("Plain text to encrypt")
ciphertext, _ := key.Encrypt(byteSlice)
plaintext, err := key.Decrypt(ciphertext)
ciphertext, _ := key.Encrypt(nil, byteSlice, crypto.SHA256)
plaintext, err := key.Decrypt(nil, ciphertext, &rsa.OAEPOptions{Hash: crypto.SHA256})
if err != nil {
t.Errorf("Universal Client API decryption: got %v, want nil err", err)
return
Expand Down
12 changes: 7 additions & 5 deletions darwin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,19 @@ func (sk *SecureKey) Public() crypto.PublicKey {
return sk.key.Public()
}

// Sign signs a message digest, using the specified signer options.
// Sign signs a message digest, using the specified signer opts. Implements crypto.Signer interface.
func (sk *SecureKey) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed []byte, err error) {
return sk.key.Sign(nil, digest, opts)
}

func (sk *SecureKey) Encrypt(plaintext []byte) ([]byte, error) {
return sk.key.Encrypt(plaintext)
// Encrypt encrypts a plaintext msg into ciphertext, using the specified encrypt opts.
func (sk *SecureKey) Encrypt(_ io.Reader, msg []byte, opts any) (ciphertext []byte, err error) {
return sk.key.Encrypt(msg, opts)
}

func (sk *SecureKey) Decrypt(ciphertext []byte) ([]byte, error) {
return sk.key.Decrypt(ciphertext)
// Decrypt decrypts a ciphertext msg into plaintext, using the specified decrypter opts. Implements crypto.Decrypter interface.
func (sk *SecureKey) Decrypt(_ io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
return sk.key.Decrypt(msg, opts)
}

// Close frees up resources associated with the underlying key.
Expand Down
14 changes: 7 additions & 7 deletions darwin/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,36 @@ package darwin

import (
"bytes"
"crypto"
"crypto/rsa"
"testing"

"github.com/googleapis/enterprise-certificate-proxy/internal/signer/darwin/keychain"
)

const TEST_CREDENTIALS = "TestIssuer"

func TestClientEncrypt(t *testing.T) {
secureKey, err := keychain.Cred(TEST_CREDENTIALS)
secureKey, err := NewSecureKey(TEST_CREDENTIALS)
if err != nil {
t.Errorf("Cred: got %v, want nil err", err)
return
}
plaintext := []byte("Plain text to encrypt")
_, err = secureKey.Encrypt(plaintext)
_, err = secureKey.Encrypt(nil, plaintext, crypto.SHA256)
if err != nil {
t.Errorf("Client API encryption: got %v, want nil err", err)
return
}
}

func TestClientDecrypt(t *testing.T) {
secureKey, err := keychain.Cred(TEST_CREDENTIALS)
secureKey, err := NewSecureKey(TEST_CREDENTIALS)
if err != nil {
t.Errorf("Cred: got %v, want nil err", err)
return
}
byteSlice := []byte("Plain text to encrypt")
ciphertext, _ := secureKey.Encrypt(byteSlice)
plaintext, err := secureKey.Decrypt(ciphertext)
ciphertext, _ := secureKey.Encrypt(nil, byteSlice, crypto.SHA256)
plaintext, err := secureKey.Decrypt(nil, ciphertext, &rsa.OAEPOptions{Hash:crypto.SHA256})
if err != nil {
t.Errorf("Client API decryption: got %v, want nil err", err)
return
Expand Down
17 changes: 15 additions & 2 deletions internal/signer/darwin/keychain/keychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,13 @@ func (k *Key) getDecryptAlgorithm() (C.SecKeyAlgorithm, error) {
return k.getRSADecryptAlgorithm()
}

func (k *Key) Encrypt(plaintext []byte) ([]byte, error) {
// Encrypt encrypts a plaintext message digest using the public key. Here, we pass off the encryption to Keychain library.
func (k *Key) Encrypt(plaintext []byte, opts any) ([]byte, error) {
if hash, ok := opts.(crypto.Hash); ok {
k.hash = hash
} else {
return nil, fmt.Errorf("Unsupported encrypt opts: %v", opts)
}
pub := k.publicKeyRef
algorithm, err := k.getEncryptAlgorithm()
if err != nil {
Expand All @@ -554,7 +560,14 @@ func (k *Key) Encrypt(plaintext []byte) ([]byte, error) {
return ciphertext, cfErrorFromRef(cfErr)
}

func (k *Key) Decrypt(ciphertext []byte) ([]byte, error) {
// Decrypt decrypts a ciphertext message digest using the private key. Here, we pass off the decryption to Keychain library.
// Currently, only *rsa.OAEPOptions is supported for opts.
func (k *Key) Decrypt(ciphertext []byte, opts crypto.DecrypterOpts) ([]byte, error) {
if oaepOpts, ok := opts.(*rsa.OAEPOptions); ok {
k.hash = oaepOpts.Hash
} else {
return nil, fmt.Errorf("Unsupported DecrypterOpts: %v", opts)
}
priv := k.privateKeyRef
algorithm, err := k.getDecryptAlgorithm()
if err != nil {
Expand Down
14 changes: 8 additions & 6 deletions internal/signer/darwin/keychain/keychain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package keychain

import (
"bytes"
"crypto"
"crypto/rsa"
"testing"
"unsafe"
)
Expand Down Expand Up @@ -56,7 +58,7 @@ func TestEncrypt(t *testing.T) {
return
}
plaintext := []byte("Plain text to encrypt")
_, err = key.Encrypt(plaintext)
_, err = key.Encrypt(plaintext, crypto.SHA256)
if err != nil {
t.Errorf("Encrypt: got %v, want nil err", err)
return
Expand All @@ -71,7 +73,7 @@ func BenchmarkEncrypt(b *testing.B) {
}
plaintext := []byte("Plain text to encrypt")
for i := 0; i < b.N; i++ {
_, err := key.Encrypt(plaintext)
_, err := key.Encrypt(plaintext, crypto.SHA256)
if err != nil {
b.Errorf("Encrypt: got %v, want nil err", err)
}
Expand All @@ -85,8 +87,8 @@ func TestDecrypt(t *testing.T) {
return
}
byteSlice := []byte("Plain text to encrypt")
ciphertext, _ := key.Encrypt(byteSlice)
plaintext, err := key.Decrypt(ciphertext)
ciphertext, _ := key.Encrypt(byteSlice, crypto.SHA256)
plaintext, err := key.Decrypt(ciphertext, &rsa.OAEPOptions{Hash: crypto.SHA256})
if err != nil {
t.Errorf("Decrypt: got %v, want nil err", err)
return
Expand All @@ -103,9 +105,9 @@ func BenchmarkDecrypt(b *testing.B) {
return
}
byteSlice := []byte("Plain text to encrypt")
ciphertext, _ := key.Encrypt(byteSlice)
ciphertext, _ := key.Encrypt(byteSlice, crypto.SHA256)
for i := 0; i < b.N; i++ {
_, err := key.Decrypt(ciphertext)
_, err := key.Decrypt(ciphertext, &rsa.OAEPOptions{Hash: crypto.SHA256})
if err != nil {
b.Errorf("Decrypt: got %v, want nil err", err)
}
Expand Down
29 changes: 16 additions & 13 deletions internal/signer/darwin/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,25 @@ func init() {
gob.Register(crypto.SHA384)
gob.Register(crypto.SHA512)
gob.Register(&rsa.PSSOptions{})
gob.Register(&rsa.OAEPOptions{})
}

// SignArgs contains arguments to a crypto Signer.Sign method.
// SignArgs contains arguments for a Sign API call.
type SignArgs struct {
Digest []byte // The content to sign.
Opts crypto.SignerOpts // Options for signing, such as Hash identifier.
Opts crypto.SignerOpts // Options for signing. Must implement HashFunc().
}

// EncryptArgs contains arguments for an Encrypt API call.
type EncryptArgs struct {
Plaintext []byte
Hash crypto.Hash
Plaintext []byte // The plaintext to encrypt.
Opts any // Options for encryption. Ex: an instance of crypto.Hash.
}

// DecryptArgs contains arguments to for a Decrypt API call.
type DecryptArgs struct {
Ciphertext []byte
Hash crypto.Hash
Ciphertext []byte // The ciphertext to decrypt.
Opts crypto.DecrypterOpts // Options for decryption. Ex: an instance of *rsa.OAEPOptions.
}

// A EnterpriseCertSigner exports RPC methods for signing.
Expand Down Expand Up @@ -100,21 +103,21 @@ func (k *EnterpriseCertSigner) Public(ignored struct{}, publicKey *[]byte) (err
return
}

// Sign signs a message digest.
// Sign signs a message digest. Stores result in "resp".
func (k *EnterpriseCertSigner) Sign(args SignArgs, resp *[]byte) (err error) {
*resp, err = k.key.Sign(nil, args.Digest, args.Opts)
return
}

func (k *EnterpriseCertSigner) Encrypt(args EncryptArgs, plaintext *[]byte) (err error) {
k.key.WithHash(args.Hash)
*plaintext, err = k.key.Encrypt(args.Plaintext)
// Encrypt encrypts a plaintext message digest. Stores result in "resp".
func (k *EnterpriseCertSigner) Encrypt(args EncryptArgs, resp *[]byte) (err error) {
*resp, err = k.key.Encrypt(args.Plaintext, args.Opts)
return
}

func (k *EnterpriseCertSigner) Decrypt(args DecryptArgs, ciphertext *[]byte) (err error) {
k.key.WithHash(args.Hash)
*ciphertext, err = k.key.Decrypt(args.Ciphertext)
// Decrypt decrypts a ciphertext message digest. Stores result in "resp".
func (k *EnterpriseCertSigner) Decrypt(args DecryptArgs, resp *[]byte) (err error) {
*resp, err = k.key.Decrypt(args.Ciphertext, args.Opts)
return
}

Expand Down
20 changes: 16 additions & 4 deletions internal/signer/linux/pkcs11/pkcs11.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,17 @@ func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte,
return k.signer.Sign(nil, digest, opts)
}

func (k *Key) Encrypt(data []byte) ([]byte, error) {
// Encrypt encrypts a plaintext message digest using the public key. Here, we use standard golang API.
func (k *Key) Encrypt(plaintext []byte, opts any) ([]byte, error) {
if hash, ok := opts.(crypto.Hash); ok {
k.hash = hash
} else {
return nil, fmt.Errorf("Unsupported encrypt opts: %v", opts)
}
publicKey := k.Public()
_, ok := publicKey.(*rsa.PublicKey)
if ok {
return k.encryptRSA(data)
return k.encryptRSA(plaintext)
}
_, ok = publicKey.(*ecdsa.PublicKey)
if ok {
Expand All @@ -175,11 +181,17 @@ func (k *Key) Encrypt(data []byte) ([]byte, error) {
return nil, errors.New("encrypt error: Unsupported key type")
}

func (k *Key) Decrypt(encryptedData []byte) ([]byte, error) {
// Decrypt decrypts a ciphertext message digest using the private key. Here, we pass off the decryption to pkcs11 library.
func (k *Key) Decrypt(msg []byte, opts crypto.DecrypterOpts) ([]byte, error) {
if oaepOpts, ok := opts.(*rsa.OAEPOptions); ok {
k.hash = oaepOpts.Hash
} else {
return nil, fmt.Errorf("Unsupported DecrypterOpts: %v", opts)
}
publicKey := k.Public()
_, ok := publicKey.(*rsa.PublicKey)
if ok {
return k.decryptRSAWithPKCS11(encryptedData)
return k.decryptRSAWithPKCS11(msg)
}
_, ok = publicKey.(*ecdsa.PublicKey)
if ok {
Expand Down
8 changes: 4 additions & 4 deletions internal/signer/linux/pkcs11/pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package pkcs11
import (
"bytes"
"crypto"
"crypto/rsa"
"flag"
"testing"
)
Expand Down Expand Up @@ -88,7 +89,7 @@ func TestEncrypt(t *testing.T) {
defer key.Close()
msg := "Plain text to encrypt"
bMsg := []byte(msg)
_, err := key.Encrypt(bMsg)
_, err := key.Encrypt(bMsg, crypto.SHA1)
if err != nil {
t.Errorf("Encrypt error: %q", err)
}
Expand All @@ -104,12 +105,11 @@ func TestDecrypt(t *testing.T) {
msg := "Plain text to encrypt"
bMsg := []byte(msg)
// Softhsm only supports SHA1
key = key.WithHash(crypto.SHA1)
ciphertext, err := key.Encrypt(bMsg)
ciphertext, err := key.Encrypt(bMsg, crypto.SHA1)
if err != nil {
t.Errorf("Encrypt error: %q", err)
}
decrypted, err := key.Decrypt(ciphertext)
decrypted, err := key.Decrypt(ciphertext, &rsa.OAEPOptions{Hash: crypto.SHA1})
if err != nil {
t.Fatalf("Decrypt error: %v", err)
}
Expand Down

0 comments on commit c6a4f15

Please sign in to comment.