Skip to content

Commit

Permalink
Move DigitallySigned to its own proto package. (#379)
Browse files Browse the repository at this point in the history
* Move DigitallySigned to its own proto package.

This allows external libraries to depend on it and enforces
better decoupling in the code base.

* reviewer comments

* mv proto/signature to crypto/sigpb

* rename hashAlgo to hash
  • Loading branch information
gdbelvin authored Feb 17, 2017
1 parent 242ba24 commit b167513
Show file tree
Hide file tree
Showing 28 changed files with 476 additions and 355 deletions.
40 changes: 0 additions & 40 deletions crypto/hashers.go

This file was deleted.

28 changes: 14 additions & 14 deletions crypto/key_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"io/ioutil"

"github.com/golang/glog"
"github.com/google/trillian"
"github.com/google/trillian/crypto/sigpb"
)

// KeyManager loads and holds our private and public keys. Should support ECDSA and RSA keys.
Expand All @@ -38,9 +38,9 @@ type KeyManager interface {
// manager.
Signer() (crypto.Signer, error)
// SignatureAlgorithm returns the value that identifies the signature algorithm.
SignatureAlgorithm() trillian.SignatureAlgorithm
SignatureAlgorithm() sigpb.DigitallySigned_SignatureAlgorithm
// HashAlgorithm returns the type of hash that will be used for signing with this key.
HashAlgorithm() trillian.HashAlgorithm
HashAlgorithm() crypto.Hash
// GetPublicKey returns the public key previously loaded. It is an error to call this
// before a public key has been loaded
GetPublicKey() (crypto.PublicKey, error)
Expand All @@ -54,7 +54,7 @@ type KeyManager interface {
// PEM file.
type PEMKeyManager struct {
serverPrivateKey crypto.PrivateKey
signatureAlgorithm trillian.SignatureAlgorithm
signatureAlgorithm sigpb.DigitallySigned_SignatureAlgorithm
serverPublicKey crypto.PublicKey
rawPublicKey []byte
}
Expand All @@ -71,15 +71,15 @@ func (k PEMKeyManager) NewPEMKeyManager(key crypto.PrivateKey) *PEMKeyManager {
}

// SignatureAlgorithm identifies the signature algorithm used by this key manager.
func (k PEMKeyManager) SignatureAlgorithm() trillian.SignatureAlgorithm {
func (k PEMKeyManager) SignatureAlgorithm() sigpb.DigitallySigned_SignatureAlgorithm {
return k.signatureAlgorithm
}

// HashAlgorithm identifies the hash algorithm used to sign objects.
func (k PEMKeyManager) HashAlgorithm() trillian.HashAlgorithm {
func (k PEMKeyManager) HashAlgorithm() crypto.Hash {
// TODO: Save the hash algorithm in the key serialization.
// Return a default hash algorithm for now.
return trillian.HashAlgorithm_SHA256
return crypto.SHA256
}

// LoadPrivateKey loads a private key from a PEM encoded string, decrypting it if necessary
Expand Down Expand Up @@ -172,29 +172,29 @@ func (k PEMKeyManager) GetRawPublicKey() ([]byte, error) {
return k.rawPublicKey, nil
}

func parsePrivateKey(key []byte) (crypto.PrivateKey, trillian.SignatureAlgorithm, error) {
func parsePrivateKey(key []byte) (crypto.PrivateKey, sigpb.DigitallySigned_SignatureAlgorithm, error) {
// Our two ways of reading keys are ParsePKCS1PrivateKey and ParsePKCS8PrivateKey.
// And ParseECPrivateKey. Our three ways of parsing keys are ... I'll come in again.
if key, err := x509.ParsePKCS1PrivateKey(key); err == nil {
return key, trillian.SignatureAlgorithm_RSA, nil
return key, sigpb.DigitallySigned_RSA, nil
}
if key, err := x509.ParsePKCS8PrivateKey(key); err == nil {
switch key := key.(type) {
case *ecdsa.PrivateKey:
return key, trillian.SignatureAlgorithm_ECDSA, nil
return key, sigpb.DigitallySigned_ECDSA, nil
case *rsa.PrivateKey:
return key, trillian.SignatureAlgorithm_RSA, nil
return key, sigpb.DigitallySigned_RSA, nil
default:
return nil, trillian.SignatureAlgorithm_ANONYMOUS, fmt.Errorf("unknown private key type: %T", key)
return nil, sigpb.DigitallySigned_ANONYMOUS, fmt.Errorf("unknown private key type: %T", key)
}
}
var err error
if key, err := x509.ParseECPrivateKey(key); err == nil {
return key, trillian.SignatureAlgorithm_ECDSA, nil
return key, sigpb.DigitallySigned_ECDSA, nil
}

glog.Warningf("error parsing EC key: %s", err)
return nil, trillian.SignatureAlgorithm_ANONYMOUS, errors.New("could not parse private key")
return nil, sigpb.DigitallySigned_ANONYMOUS, errors.New("could not parse private key")
}

// LoadPasswordProtectedPrivateKey initializes and returns a new KeyManager using a PEM encoded
Expand Down
10 changes: 5 additions & 5 deletions crypto/mock_key_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package crypto
import (
crypto "crypto"
gomock "github.com/golang/mock/gomock"
trillian "github.com/google/trillian"
sigpb "github.com/google/trillian/crypto/sigpb"
)

// Mock of KeyManager interface
Expand Down Expand Up @@ -52,19 +52,19 @@ func (_mr *_MockKeyManagerRecorder) GetRawPublicKey() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetRawPublicKey")
}

func (_m *MockKeyManager) HashAlgorithm() trillian.HashAlgorithm {
func (_m *MockKeyManager) HashAlgorithm() crypto.Hash {
ret := _m.ctrl.Call(_m, "HashAlgorithm")
ret0, _ := ret[0].(trillian.HashAlgorithm)
ret0, _ := ret[0].(crypto.Hash)
return ret0
}

func (_mr *_MockKeyManagerRecorder) HashAlgorithm() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "HashAlgorithm")
}

func (_m *MockKeyManager) SignatureAlgorithm() trillian.SignatureAlgorithm {
func (_m *MockKeyManager) SignatureAlgorithm() sigpb.DigitallySigned_SignatureAlgorithm {
ret := _m.ctrl.Call(_m, "SignatureAlgorithm")
ret0, _ := ret[0].(trillian.SignatureAlgorithm)
ret0, _ := ret[0].(sigpb.DigitallySigned_SignatureAlgorithm)
return ret0
}

Expand Down
49 changes: 32 additions & 17 deletions crypto/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/benlaurie/objecthash/go/objecthash"
"github.com/golang/glog"
"github.com/google/trillian"
"github.com/google/trillian/crypto/sigpb"
)

// Constants used as map keys when building input for ObjectHash. They must not be changed
Expand All @@ -34,48 +35,62 @@ const (
mapKeyTreeSize string = "TreeSize"
)

var (
signerHashLookup = map[sigpb.DigitallySigned_HashAlgorithm]crypto.Hash{
sigpb.DigitallySigned_SHA256: crypto.SHA256,
}
reverseSignerHashLookup = map[crypto.Hash]sigpb.DigitallySigned_HashAlgorithm{
crypto.SHA256: sigpb.DigitallySigned_SHA256,
}
)

// Signer is responsible for signing log-related data and producing the appropriate
// application specific signature objects.
type Signer struct {
hasher crypto.Hash
hash crypto.Hash
signer crypto.Signer
sigAlgorithm trillian.SignatureAlgorithm
sigAlgorithm sigpb.DigitallySigned_SignatureAlgorithm
}

// NewSigner creates a new Signer wrapping up a hasher and a signer. For the moment
// we only support SHA256 hashing and either ECDSA or RSA signing but this is not enforced
// here.
func NewSigner(hashAlgo trillian.HashAlgorithm, sigAlgo trillian.SignatureAlgorithm, signer crypto.Signer) *Signer {
h, ok := hashLookup[hashAlgo]
func NewSigner(hash crypto.Hash, sigAlgo sigpb.DigitallySigned_SignatureAlgorithm, signer crypto.Signer) *Signer {
_, ok := reverseSignerHashLookup[hash]
if !ok {
// TODO(gbelvin): return error from Signer.
panic("unsupported hash algorithm")
}

return &Signer{h, signer, sigAlgo}
return &Signer{
hash: hash,
signer: signer,
sigAlgorithm: sigAlgo,
}
}

// Sign obtains a signature after first hashing the input data.
func (s Signer) Sign(data []byte) (trillian.DigitallySigned, error) {
h := s.hasher.New()
func (s Signer) Sign(data []byte) (sigpb.DigitallySigned, error) {
h := s.hash.New()
h.Write(data)
digest := h.Sum(nil)

if len(digest) != s.hasher.Size() {
return trillian.DigitallySigned{}, fmt.Errorf("hasher returned unexpected digest length: %d, %d",
len(digest), s.hasher.Size())
if len(digest) != s.hash.Size() {
return sigpb.DigitallySigned{}, fmt.Errorf("hasher returned unexpected digest length: %d, %d",
len(digest), s.hash.Size())
}

sig, err := s.signer.Sign(rand.Reader, digest, s.hasher)
sig, err := s.signer.Sign(rand.Reader, digest, s.hash)

if err != nil {
return trillian.DigitallySigned{}, err
return sigpb.DigitallySigned{}, err
}

return trillian.DigitallySigned{
return sigpb.DigitallySigned{
SignatureAlgorithm: s.sigAlgorithm,
HashAlgorithm: reverseHashLookup[s.hasher],
Signature: sig}, nil
HashAlgorithm: reverseSignerHashLookup[s.hash],
Signature: sig,
}, nil
}

func (s Signer) hashRoot(root trillian.SignedLogRoot) []byte {
Expand All @@ -95,13 +110,13 @@ func (s Signer) hashRoot(root trillian.SignedLogRoot) []byte {

// SignLogRoot updates a log root to include a signature from the crypto signer this object
// was created with. Signatures use objecthash on a fixed JSON format of the root.
func (s Signer) SignLogRoot(root trillian.SignedLogRoot) (trillian.DigitallySigned, error) {
func (s Signer) SignLogRoot(root trillian.SignedLogRoot) (sigpb.DigitallySigned, error) {
objectHash := s.hashRoot(root)
signature, err := s.Sign(objectHash[:])

if err != nil {
glog.Warningf("Signer failed to sign root: %v", err)
return trillian.DigitallySigned{}, err
return sigpb.DigitallySigned{}, err
}

return signature, nil
Expand Down
14 changes: 8 additions & 6 deletions crypto/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/golang/mock/gomock"
"github.com/google/trillian"
"github.com/google/trillian/crypto/sigpb"
"github.com/google/trillian/testonly"
)

Expand Down Expand Up @@ -67,10 +68,10 @@ func TestSigner(t *testing.T) {
t.Fatalf("Failed to sign: %s", err)
}

if got, want := sig.HashAlgorithm, trillian.HashAlgorithm_SHA256; got != want {
if got, want := sig.HashAlgorithm, sigpb.DigitallySigned_SHA256; got != want {
t.Fatalf("Hash alg incorrect, got %v expected %d", got, want)
}
if got, want := sig.SignatureAlgorithm, trillian.SignatureAlgorithm_RSA; got != want {
if got, want := sig.SignatureAlgorithm, sigpb.DigitallySigned_RSA; got != want {
t.Fatalf("Sig alg incorrect, got %v expected %v", got, want)
}
if got, want := []byte(result), sig.Signature; !bytes.Equal(got, want) {
Expand Down Expand Up @@ -141,14 +142,15 @@ func TestSignLogRoot(t *testing.T) {
t.Fatalf("Got %v, but expected unmodified signed root %v", root, expected)
}
// And signature is correct
expectedSignature := trillian.DigitallySigned{SignatureAlgorithm: trillian.SignatureAlgorithm_RSA,
HashAlgorithm: trillian.HashAlgorithm_SHA256,
Signature: []byte("echo")}
expectedSignature := sigpb.DigitallySigned{
SignatureAlgorithm: sigpb.DigitallySigned_RSA,
HashAlgorithm: sigpb.DigitallySigned_SHA256,
Signature: []byte("echo")}
if !reflect.DeepEqual(signature, expectedSignature) {
t.Fatalf("Got %v, but expected %v", signature, expectedSignature)
}
}

func createTestSigner(mock *MockSigner) *Signer {
return NewSigner(trillian.HashAlgorithm_SHA256, trillian.SignatureAlgorithm_RSA, mock)
return NewSigner(crypto.SHA256, sigpb.DigitallySigned_RSA, mock)
}
17 changes: 17 additions & 0 deletions crypto/sigpb/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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
//
// http://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 sigpb

//go:generate protoc -I=. --go_out=:. sigpb.proto
Loading

0 comments on commit b167513

Please sign in to comment.