Skip to content

Commit

Permalink
Create verifier (#314)
Browse files Browse the repository at this point in the history
* Create verifier

In KeyManager:
* Adds support for keys that are not password protected.
* Supports getting the public key for private keys that are loaded.

* Reviewer comments

* fix govet

* Better error comments

* Revert back to indented success path
  • Loading branch information
gdbelvin authored Jan 24, 2017
1 parent 236d862 commit a7e8e00
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/loglb
/protoc
*.swp
*.swo
15 changes: 12 additions & 3 deletions crypto/key_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,13 @@ func (k *PEMKeyManager) LoadPrivateKey(pemEncodedKey, password string) error {
return errors.New("extra data found after PEM decoding")
}

der, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
return err
der := block.Bytes
if password != "" {
pwdDer, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
return err
}
der = pwdDer
}

key, algo, err := parsePrivateKey(der)
Expand All @@ -92,6 +96,11 @@ func (k *PEMKeyManager) LoadPrivateKey(pemEncodedKey, password string) error {

k.serverPrivateKey = key
k.signatureAlgorithm = algo
signer, err := k.Signer()
if err != nil {
return err
}
k.serverPublicKey = signer.Public()
return nil
}

Expand Down
84 changes: 84 additions & 0 deletions crypto/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 crypto

import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"encoding/asn1"
"errors"
"fmt"
"math/big"

"github.com/google/trillian"
)

// ErrVerify occurs whenever signature verification fails.
var ErrVerify = errors.New("signature verification failed")

// Verify cryptographically verifies the output of Signer.
func Verify(pub crypto.PublicKey, data []byte, sig trillian.DigitallySigned) error {
sigAlgo := sig.SignatureAlgorithm

// Recompute digest
hasher, err := NewHasher(sig.HashAlgorithm)
if err != nil {
return err
}
digest := hasher.Digest(data)

// Verify signature algo type
switch key := pub.(type) {
case *ecdsa.PublicKey:
if sigAlgo != trillian.SignatureAlgorithm_ECDSA {
return fmt.Errorf("signature algorithm does not match public key")
}
return verifyECDSA(key, digest, sig.Signature)
case *rsa.PublicKey:
if sigAlgo != trillian.SignatureAlgorithm_RSA {
return fmt.Errorf("signature algorithm does not match public key")
}
return verifyRSA(key, digest, sig.Signature, hasher.Hash, hasher)
default:
return fmt.Errorf("unknown private key type: %T", key)
}
}

func verifyRSA(pub *rsa.PublicKey, hashed, sig []byte, hasher crypto.Hash, opts crypto.SignerOpts) error {
if pssOpts, ok := opts.(*rsa.PSSOptions); ok {
return rsa.VerifyPSS(pub, hasher, hashed, sig, pssOpts)
}
return rsa.VerifyPKCS1v15(pub, hasher, hashed, sig)
}

func verifyECDSA(pub *ecdsa.PublicKey, hashed, sig []byte) error {
var ecdsaSig struct {
R, S *big.Int
}
rest, err := asn1.Unmarshal(sig, &ecdsaSig)
if err != nil {
return ErrVerify
}
if len(rest) != 0 {
return ErrVerify
}

if !ecdsa.Verify(pub, hashed, ecdsaSig.R, ecdsaSig.S) {
return ErrVerify
}
return nil

}
78 changes: 78 additions & 0 deletions crypto/verifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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 crypto

import (
"testing"

"github.com/google/trillian"
"github.com/google/trillian/testonly"
)

const (
// openssl ecparam -name prime256v1 -genkey -out p256-key.pem
privPEM = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGbhE2+z8d5lHzb0gmkS78d86gm5gHUtXCpXveFbK3pcoAoGCCqGSM49
AwEHoUQDQgAEUxX42oxJ5voiNfbjoz8UgsGqh1bD1NXK9m8VivPmQSoYUdVFgNav
csFaQhohkiCEthY51Ga6Xa+ggn+eTZtf9Q==
-----END EC PRIVATE KEY-----`
)

func TestSignVerify(t *testing.T) {
for _, test := range []struct {
PEM string
password string
HashAlgo trillian.HashAlgorithm
SigAlgo trillian.SignatureAlgorithm
}{
{privPEM, "", trillian.HashAlgorithm_SHA256, trillian.SignatureAlgorithm_ECDSA},
{testonly.DemoPrivateKey, testonly.DemoPrivateKeyPass,
trillian.HashAlgorithm_SHA256, trillian.SignatureAlgorithm_ECDSA},
} {

km := NewPEMKeyManager()
if err := km.LoadPrivateKey(test.PEM, test.password); err != nil {
t.Errorf("LoadPrivateKey(_, %v)=%v, want nil", test.password, err)
continue
}
kmsigner, err := km.Signer()
if err != nil {
t.Errorf("Signer()=(_,%v), want (_,nil)", err)
continue
}
hasher, err := NewHasher(test.HashAlgo)
if err != nil {
t.Errorf("NewHasher(%v)=(_,%v), want (_,nil)", test.HashAlgo, err)
continue
}
signer := NewSigner(hasher, test.SigAlgo, kmsigner)

// Sign and Verify.
msg := []byte("foo")
signed, err := signer.Sign(msg)
if err != nil {
t.Errorf("Sign()=(_,%v), want (_,nil)", err)
continue
}
pub, err := km.GetPublicKey()
if err != nil {
t.Errorf("GetPublicKey()=(_,%v), want (_,nil)", err)
continue
}
if err := Verify(pub, msg, signed); err != nil {
t.Errorf("Verify(,,)=%v, want nil", err)
}
}
}

0 comments on commit a7e8e00

Please sign in to comment.