|
@@ -1,16 +1,28 @@
|
|
// Copyright 2022 Google LLC.
|
|
// Copyright 2022 Google LLC.
|
|
-// Use of this source code is governed by a BSD-style
|
|
|
|
-// license that can be found in the LICENSE file.
|
|
|
|
|
|
+// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
+// you may not use this file except in compliance with the License.
|
|
|
|
+// You may obtain a copy of the License at
|
|
|
|
+//
|
|
|
|
+// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
+//
|
|
|
|
+// Unless required by applicable law or agreed to in writing, software
|
|
|
|
+// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
+// See the License for the specific language governing permissions and
|
|
|
|
+// limitations under the License.
|
|
|
|
+
|
|
|
|
+// Package client is a cross-platform client for the signer binary (a.k.a."EnterpriseCertSigner").
|
|
//
|
|
//
|
|
-// Client is a cross-platform client for the signer binary (a.k.a."EnterpriseCertSigner").
|
|
|
|
// The signer binary is OS-specific, but exposes a standard set of APIs for the client to use.
|
|
// The signer binary is OS-specific, but exposes a standard set of APIs for the client to use.
|
|
package client
|
|
package client
|
|
|
|
|
|
import (
|
|
import (
|
|
"crypto"
|
|
"crypto"
|
|
|
|
+ "crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509"
|
|
"encoding/gob"
|
|
"encoding/gob"
|
|
|
|
+ "errors"
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
"net/rpc"
|
|
"net/rpc"
|
|
@@ -67,14 +79,16 @@ func (k *Key) CertificateChain() [][]byte {
|
|
// Close closes the RPC connection and kills the signer subprocess.
|
|
// Close closes the RPC connection and kills the signer subprocess.
|
|
// Call this to free up resources when the Key object is no longer needed.
|
|
// Call this to free up resources when the Key object is no longer needed.
|
|
func (k *Key) Close() error {
|
|
func (k *Key) Close() error {
|
|
- if err := k.client.Close(); err != nil {
|
|
|
|
- return fmt.Errorf("failed to close RPC connection: %w", err)
|
|
|
|
- }
|
|
|
|
if err := k.cmd.Process.Kill(); err != nil {
|
|
if err := k.cmd.Process.Kill(); err != nil {
|
|
return fmt.Errorf("failed to kill signer process: %w", err)
|
|
return fmt.Errorf("failed to kill signer process: %w", err)
|
|
}
|
|
}
|
|
- if err := k.cmd.Wait(); err.Error() != "signal: killed" {
|
|
|
|
- return fmt.Errorf("signer process was not killed: %w", err)
|
|
|
|
|
|
+ // Wait for cmd to exit and release resources. Since the process is forcefully killed, this
|
|
|
|
+ // will return a non-nil error (varies by OS), which we will ignore.
|
|
|
|
+ _ = k.cmd.Wait()
|
|
|
|
+ // The Pipes connecting the RPC client should have been closed when the signer subprocess was killed.
|
|
|
|
+ // Calling `k.client.Close()` before `k.cmd.Process.Kill()` or `k.cmd.Wait()` _will_ cause a segfault.
|
|
|
|
+ if err := k.client.Close(); err.Error() != "close |0: file already closed" {
|
|
|
|
+ return fmt.Errorf("failed to close RPC connection: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
@@ -84,12 +98,19 @@ func (k *Key) Public() crypto.PublicKey {
|
|
return k.publicKey
|
|
return k.publicKey
|
|
}
|
|
}
|
|
|
|
|
|
-// Sign signs a message by encrypting a message digest, using the specified signer options.
|
|
|
|
|
|
+// Sign signs a message digest, using the specified signer options.
|
|
func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed []byte, err error) {
|
|
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())
|
|
|
|
+ }
|
|
err = k.client.Call(signAPI, SignArgs{Digest: digest, Opts: opts}, &signed)
|
|
err = k.client.Call(signAPI, SignArgs{Digest: digest, Opts: opts}, &signed)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// ErrCredUnavailable is a sentinel error that indicates ECP Cred is unavailable,
|
|
|
|
+// possibly due to missing config or missing binary path.
|
|
|
|
+var ErrCredUnavailable = errors.New("Cred is unavailable")
|
|
|
|
+
|
|
// Cred spawns a signer subprocess that listens on stdin/stdout to perform certificate
|
|
// Cred spawns a signer subprocess that listens on stdin/stdout to perform certificate
|
|
// related operations, including signing messages with the private key.
|
|
// related operations, including signing messages with the private key.
|
|
//
|
|
//
|
|
@@ -103,6 +124,9 @@ func Cred(configFilePath string) (*Key, error) {
|
|
}
|
|
}
|
|
enterpriseCertSignerPath, err := util.LoadSignerBinaryPath(configFilePath)
|
|
enterpriseCertSignerPath, err := util.LoadSignerBinaryPath(configFilePath)
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
+ if errors.Is(err, util.ErrConfigUnavailable) {
|
|
|
|
+ return nil, ErrCredUnavailable
|
|
|
|
+ }
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
k := &Key{
|
|
k := &Key{
|
|
@@ -147,5 +171,15 @@ func Cred(configFilePath string) (*Key, error) {
|
|
return nil, fmt.Errorf("invalid public key type: %T", publicKey)
|
|
return nil, fmt.Errorf("invalid public key type: %T", publicKey)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ switch pub := k.publicKey.(type) {
|
|
|
|
+ case *rsa.PublicKey:
|
|
|
|
+ if pub.Size() < 256 {
|
|
|
|
+ return nil, fmt.Errorf("RSA modulus size is less than 2048 bits: %v", pub.Size()*8)
|
|
|
|
+ }
|
|
|
|
+ case *ecdsa.PublicKey:
|
|
|
|
+ default:
|
|
|
|
+ return nil, fmt.Errorf("unsupported public key type: %v", pub)
|
|
|
|
+ }
|
|
|
|
+
|
|
return k, nil
|
|
return k, nil
|
|
}
|
|
}
|