Merge pull request #42 from docker/signing-methods

Signing methods
This commit is contained in:
Diogo Mónica 2015-07-10 15:21:21 -07:00
commit cab1006568
15 changed files with 176 additions and 363 deletions

2
Godeps/Godeps.json generated
View File

@ -47,7 +47,7 @@
},
{
"ImportPath": "github.com/endophage/gotuf",
"Rev": "88765abdd5ec33be6be1efa18c71d7f43c7c4983"
"Rev": "af7152a51a0663dc47768e591ffdfa1f60e5308a"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",

View File

@ -57,12 +57,12 @@ func (c *Client) Update() error {
func (c *Client) update() error {
err := c.downloadTimestamp()
if err != nil {
logrus.Errorf("Client Update (Timestamp): ", err.Error())
logrus.Errorf("Client Update (Timestamp): %s", err.Error())
return err
}
err = c.downloadSnapshot()
if err != nil {
logrus.Errorf("Client Update (Snapshot): ", err.Error())
logrus.Errorf("Client Update (Snapshot): %s", err.Error())
return err
}
err = c.checkRoot()
@ -71,7 +71,7 @@ func (c *Client) update() error {
}
err = c.downloadTargets("targets")
if err != nil {
logrus.Errorf("Client Update (Targets): ", err.Error())
logrus.Errorf("Client Update (Targets): %s", err.Error())
return err
}
return nil

View File

@ -1,150 +0,0 @@
package client
import (
"encoding/json"
"io/ioutil"
"testing"
"github.com/Sirupsen/logrus"
tuf "github.com/endophage/gotuf"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/keys"
"github.com/endophage/gotuf/store"
)
func TestClientUpdate(t *testing.T) {
pypiRoot := `{
"signatures": [
{
"keyid": "92db731bf30e31c13e775360453f0adc8bfd3107f5000b99431c4bdbcebb31ed",
"method": "PyCrypto-PKCS#1 PSS",
"sig": "31d1fa4712eaf591b367740f69ba7577fb5565863639d7e89145abe159f047d5f848e7696fbaf16f5c4214e1f2295d14e3078f5c0a6e2cbc015c13f8557836a039208970b436bb13b921f86f3e4d4518ce2f731bd7a55083d45634f206dc92e886daeb15e65a513f6451575811e9e44b5573d6999d4b69f86a27f01d0d9a868a535a1f0f6534bd9e555d4df95f019ea6859c83fca30e95e0d1c2ce1dcb2b19c91facd98a7cae9f4c81b5ff4c12980f333e38eac99f4561a8f9e5342382443f165cf0af840d5c61b62698b27413d7f5e1bdba714b98759bcfc7c3d65c567459ce093c66c88ebae836665bcdf2efc1e6921bd792406c0529dab2678a922e5fa6ef39e92f89d3072a22bd755a49b2e2e2c801ce73006fd8a7f56595fae01af3e2a49a3bce4ce9fcaec43e1aae5b0c0fc807bc4caca9fae7c34ff026a417262b5435cfd6cbf17b70aae041eae30a6bd6a857db88566c89f2c02171674b9195f22a84ec5839a4e46e2e63fbb5d4821ae66237ea261846c7d5a341d5f4ab2e12412d4e"
}
],
"signed": {
"_type": "Root",
"expires": "2014-08-31 00:49:33 UTC",
"keys": {
"07f12f6c470e60d49fe6a60cd893dfba870db387083d50fe4fc43c6171a0be59": {
"keytype": "rsa",
"keyval": {
"private": "",
"public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAtb4vuMJF1kOc1deExdqV\nZ3o9W6zulfRDX6u8Exd3NEL0/9bCYluqpshFzgmDYpCsuS79Ve5S8WgXAnEXyWOf\nHL/FnAVqFSnmgmYr52waMYV57BsNMY8pXxOnJm1opUbt0PdnF2D2yfLLY8IgZ/0m\niJ+gWojwEBMRlTOnHFx+l/UVvZAhpVsua6C8C2ThFLrXlmmtkg5BAIm1kWPsZ0nB\n3Cczcwh8zE0l2ytVi458AuRGKwi1TMIGXl064ekjsWexH1zCuoEcv/CKob0BkL4c\nLFUKe6WD6XlEp/xnMh6lNG8LT+UKzSb1hPkTtt23RntFB4Qx7UUT8LoO79bv6coE\njeEqHltmeohHpVmTLWsbTdaX7W3clWPUErGh5kAO0SJu1EM94p5nGWlZ+kwASxtO\nz1qR8AqQ02HBBQU+cY24CNnOwKDq/Kgsg1Aw7bqglvtUwBUQkuuCuisjHI0bSMFr\ntGIhlswxItfQ709/OMrN44Vw/H/Z50UzGFtRlu1h07/vAgMBAAE=\n-----END PUBLIC KEY-----"
}
},
"2d9f41a1b79429e9d950a687fe00da0bb4fd751da98aeade1524b8d28968eb89": {
"keytype": "rsa",
"keyval": {
"private": "",
"public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmjt8CIbohZxZQ1+I8CQn\nvugLsGXq1G2OiMZvbN281q1/E1futll3+EnfxPLP0SBlyYxixma/ozoMj94lPyOX\nBbiF/U1WJ0Wp5D1kpT0Jzt9Ar0bkRxWoPhubeJ7D4k8Br2m7aG+wchfozdbMmUwK\n/MiAZ1fmpKQAr1ek3/hJiN/dURw+mQEdgXwgA4raDy4Ty3AkG7SDCG1cYoYYMJa3\nKg82AWISQQEHUO1MwRVBon2B5d2UriUEzsYYi+2whDOekchjgyd2xdcRvdCBbdGv\nJBCtzVZpd52lCwqAMJUyDGre6Mb6NmKC2nuk+TYEujqRQK97LnjmPwI42b4cBHqv\nDtg7Z8K3rEQIyD+VvrcWlu+1cE8drjh3y+r7oTtjRPr1M8xaCWn/dh8huSJmaFlk\nmWKDX9KI3/5pxFjgpry20eBRYkZHJWwByc9GVvwhRsIF61QdKvA6uGqkFHy9YQBW\nnzWTPo/4UTUwcpfjneoyMOVKx2K05ZePa5UNAgJVDgFfAgMBAAE=\n-----END PUBLIC KEY-----"
}
},
"92db731bf30e31c13e775360453f0adc8bfd3107f5000b99431c4bdbcebb31ed": {
"keytype": "rsa",
"keyval": {
"private": "",
"public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEArvqUPYb6JJROPJQglPTj\n5uDrsxQKl34Mo+3pSlBVuD6puE4lDnG649a2YksJy+C8ZIPJgokn5w+C3alh+dMe\nzbdWHHxrY1h9CLpYz5cbMlE16303ubkt1rvwDqEezG0HDBzPaKj4oP9YJ9x7wbsq\ndvFcy+Qc3wWd7UWcieo6E0ihbJkYcY8chRXVLg1rL7EfZ+e3bq5+ojA2ECM5JqzZ\nzgDpqCv5hTCYYZp72MZcG7dfSPAHrcSGIrwg7whzz2UsEtCOpsJTuCl96FPN7kAu\n4w/WyM3+SPzzr4/RQXuY1SrLCFD8ebM2zHt/3ATLhPnGmyG5I0RGYoegFaZ2AViw\nlqZDOYnBtgDvKP0zakMtFMbkh2XuNBUBO7Sjs0YcZMjLkh9gYUHL1yWS3Aqus1Lw\nlI0gHS22oyGObVBWkZEgk/Foy08sECLGao+5VvhmGpfVuiz9OKFUmtPVjWzRE4ng\niekEu4drSxpH41inLGSvdByDWLpcTvWQI9nkgclh3AT/AgMBAAE=\n-----END PUBLIC KEY-----"
}
},
"e1ccde549849bb6aa548412e79c407c93f303f3a3ca0ab1e5923ff7d5c4de769": {
"keytype": "rsa",
"keyval": {
"private": "",
"public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAqnNAzjIb73u2Nk+r2AdU\nnK+xvKDcZgIzjSzRVjtRJgu3MVffzPcGIsjv2RS8/LLl8nSf48V+tWZf/PnTzkMn\n1iJhbdOQTt6bABiizm5dLP9Jm/AIMTTUpbu+fpFbV8vNH+qM5/Z0WNOptQnOEfNs\n84MNh919lJjHc5VPTw86h68Mkn7W5RChZqnwEv75M1XfWdAnUGeLfZh6BKxFMnaB\nciYxieUvc1bnCtqsdaDE2Ab86WXM7cmNCqyLAh4JkTV+RcMzqEnZCAm68TkO6gM/\n5g4A7fbnn9Jc4O3fJvu80fYOg63nS6hAFldmN74oYu2h5PV75xyTIEERoXqJbSaj\n1Agj/98khIZGsSVjoQ5Mi7ETcSbsH7rqWdHFIu+dbKw8vlnqWnEQYjXA8CIOY3X7\nB6/u5FBS5AdxjO6AR/MuaqpWdDTZwXwgZ9c4wqO4Re4z73sGM1PugUK4dbXIGwVe\nRtzv7cSFOB7OPmj53miJWt2ILACb+Dpnxt9h6TzT1C0JAgMBAAE=\n-----END PUBLIC KEY-----"
}
}
},
"roles": {
"release": {
"keyids": [
"e1ccde549849bb6aa548412e79c407c93f303f3a3ca0ab1e5923ff7d5c4de769"
],
"threshold": 1
},
"root": {
"keyids": [
"92db731bf30e31c13e775360453f0adc8bfd3107f5000b99431c4bdbcebb31ed"
],
"threshold": 1
},
"targets": {
"keyids": [
"07f12f6c470e60d49fe6a60cd893dfba870db387083d50fe4fc43c6171a0be59"
],
"threshold": 1
},
"timestamp": {
"keyids": [
"2d9f41a1b79429e9d950a687fe00da0bb4fd751da98aeade1524b8d28968eb89"
],
"threshold": 1
}
},
"version": 1
}
}`
data.SetTUFTypes(
map[string]string{
"snapshot": "Release",
},
)
data.SetValidRoles(
map[string]string{
"snapshot": "release",
},
)
s := &data.Signed{}
err := json.Unmarshal([]byte(pypiRoot), s)
if err != nil {
t.Fatal(err)
}
kdb := keys.NewDB()
logrus.SetLevel(logrus.DebugLevel)
// Being able to set the second argument, signer, to nil is a great
// test as we shouldn't need to instantiate a signer just for reading
// a repo.
repo := tuf.NewTufRepo(kdb, nil)
repo.SetRoot(s)
remote, err := store.NewHTTPStore(
"http://mirror1.poly.edu/test-pypi/",
"metadata",
"txt",
"targets",
)
cached := store.NewFileCacheStore(remote, "/tmp/tuf")
if err != nil {
t.Fatal(err)
}
client := Client{
local: repo,
remote: cached,
keysDB: kdb,
}
err = client.Update()
if err != nil {
t.Fatal(err)
}
testTarget := "packages/2.3/T/TracHTTPAuth/TracHTTPAuth-1.0.1-py2.3.egg"
expectedHash := "dbcaa6dc0035a636234f9b457d24bf1aeecac0a29b4da97a3b32692f2729f9db"
expectedSize := int64(8140)
m := client.TargetMeta(testTarget)
if m == nil {
t.Fatal("Failed to find existing target")
}
if m.Hashes["sha256"].String() != expectedHash {
t.Fatal("Target hash incorrect.\nExpected:", expectedHash, "\nReceived:", m.Hashes["sha256"].String())
}
if m.Length != expectedSize {
t.Fatal("Target size incorrect.\nExpected:", expectedSize, "\nReceived:", m.Length)
}
err = client.DownloadTarget(ioutil.Discard, testTarget, m)
if err != nil {
t.Fatal(err)
}
}

View File

@ -0,0 +1,45 @@
package data
import (
"bytes"
. "gopkg.in/check.v1"
)
type TypesSuite struct{}
var _ = Suite(&TypesSuite{})
func (TypesSuite) TestGenerateFileMetaDefault(c *C) {
// default is sha512
r := bytes.NewReader([]byte("foo"))
meta, err := NewFileMeta(r, "sha512")
c.Assert(err, IsNil)
c.Assert(meta.Length, Equals, int64(3))
hashes := meta.Hashes
c.Assert(hashes, HasLen, 1)
hash, ok := hashes["sha512"]
if !ok {
c.Fatal("missing sha512 hash")
}
c.Assert(hash.String(), DeepEquals, "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7")
}
func (TypesSuite) TestGenerateFileMetaExplicit(c *C) {
r := bytes.NewReader([]byte("foo"))
meta, err := NewFileMeta(r, "sha256", "sha512")
c.Assert(err, IsNil)
c.Assert(meta.Length, Equals, int64(3))
hashes := meta.Hashes
c.Assert(hashes, HasLen, 2)
for name, val := range map[string]string{
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"sha512": "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7",
} {
hash, ok := hashes[name]
if !ok {
c.Fatalf("missing %s hash", name)
}
c.Assert(hash.String(), DeepEquals, val)
}
}

View File

@ -37,7 +37,7 @@ func (trust *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, er
sig := ed25519.Sign(&priv, toSign)
signatures = append(signatures, data.Signature{
KeyID: kID,
Method: "ed25519",
Method: "ED25519",
Signature: sig[:],
})
}
@ -50,8 +50,8 @@ func (trust *Ed25519) Create(role string) (*data.PublicKey, error) {
if err != nil {
return nil, err
}
public := data.NewPublicKey("ed25519", pub[:])
private := data.NewPrivateKey("ed25519", pub[:], priv[:])
public := data.NewPublicKey("ED25519", pub[:])
private := data.NewPrivateKey("ED25519", pub[:], priv[:])
trust.addKey(private)
return public, nil
}

View File

@ -41,7 +41,7 @@ var _ CryptoService = &MockCryptoService{}
// Test signing and ensure the expected signature is added
func TestBasicSign(t *testing.T) {
testKey, _ := pem.Decode([]byte(testKeyPEM1))
k := data.NewPublicKey("rsa", testKey.Bytes)
k := data.NewPublicKey("RSA", testKey.Bytes)
signer := Signer{&MockCryptoService{
testKey: *k,
}}
@ -68,7 +68,7 @@ func TestBasicSign(t *testing.T) {
// should be cleaning previous signatures by the KeyID when asked to sign again)
func TestReSign(t *testing.T) {
testKey, _ := pem.Decode([]byte(testKeyPEM1))
k := data.NewPublicKey("rsa", testKey.Bytes)
k := data.NewPublicKey("RSA", testKey.Bytes)
signer := Signer{&MockCryptoService{
testKey: *k,
}}
@ -92,11 +92,11 @@ func TestMultiSign(t *testing.T) {
testData := data.Signed{}
testKey, _ := pem.Decode([]byte(testKeyPEM1))
key := data.NewPublicKey("rsa", testKey.Bytes)
key := data.NewPublicKey("RSA", testKey.Bytes)
signer.Sign(&testData, key)
testKey, _ = pem.Decode([]byte(testKeyPEM2))
key = data.NewPublicKey("rsa", testKey.Bytes)
key = data.NewPublicKey("RSA", testKey.Bytes)
signer.Sign(&testData, key)
if len(testData.Signatures) != 2 {
@ -114,7 +114,7 @@ func TestMultiSign(t *testing.T) {
func TestCreate(t *testing.T) {
testKey, _ := pem.Decode([]byte(testKeyPEM1))
k := data.NewPublicKey("rsa", testKey.Bytes)
k := data.NewPublicKey("RSA", testKey.Bytes)
signer := Signer{&MockCryptoService{
testKey: *k,
}}

View File

@ -17,10 +17,10 @@ import (
// can be injected into a verificationService. For testing and configuration
// purposes, it will not be used by default.
var Verifiers = map[string]Verifier{
"ed25519": Ed25519Verifier{},
"rsa": RSAVerifier{},
"rsassa-pkcs1-v1_5-sign": RSAPemVerifier{}, // RSASSA-PKCS1-V1_5-SIGN
"pycrypto-pkcs#1 pss": RSAPSSVerifier{},
"ed25519": Ed25519Verifier{},
"rsassa-pss": RSAPSSVerifier{},
"rsassa-pss-x509": RSAPSSX509Verifier{},
"pycrypto-pkcs#1 pss": RSAPyCryptoVerifier{},
}
// RegisterVerifier provides a convenience function for init() functions
@ -61,83 +61,79 @@ func (v Ed25519Verifier) Verify(key data.Key, sig []byte, msg []byte) error {
return nil
}
type RSAVerifier struct{}
func (v RSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
//keyReader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(key.Public()))
//keyBytes, _ := ioutil.ReadAll(keyReader)
//pub, err := x509.ParsePKIXPublicKey(keyBytes)
pub, err := x509.ParsePKIXPublicKey(key.Public())
if err != nil {
logrus.Infof("Failed to parse public key: %s\n", err)
return ErrInvalid
}
rsaPub, ok := pub.(*rsa.PublicKey)
func verifyPSS(key interface{}, digest, sig []byte) error {
rsaPub, ok := key.(*rsa.PublicKey)
if !ok {
logrus.Infof("Value returned from ParsePKIXPublicKey was not an RSA public key")
logrus.Infof("Value was not an RSA public key")
return ErrInvalid
}
if err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig); err != nil {
opts := rsa.PSSOptions{SaltLength: sha256.Size, Hash: crypto.SHA256}
if err := rsa.VerifyPSS(rsaPub, crypto.SHA256, digest[:], sig, &opts); err != nil {
logrus.Infof("Failed verification: %s", err)
return ErrInvalid
}
return nil
}
type RSAPemVerifier struct{}
func (v RSAPemVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
k, _ := pem.Decode([]byte(key.Public()))
cert, err := x509.ParseCertificate(k.Bytes)
if err != nil {
logrus.Errorf("Failed to parse public key: %s\n", err.Error())
return ErrInvalid
}
rsaPub, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
logrus.Infof("Value returned from ParsePKIXPublicKey was not an RSA public key")
return ErrInvalid
}
if err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig); err != nil {
logrus.Errorf("Failed verification: %s", err.Error())
return ErrInvalid
}
return nil
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPSSVerifier struct{}
// Verify does the actual check.
// N.B. We have not been able to make this work in a way that is compatible
// with PyCrypto.
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
pub, err := x509.ParsePKIXPublicKey(key.Public())
if err != nil {
logrus.Infof("Failed to parse public key: %s\n", err)
return ErrInvalid
}
return verifyPSS(pub, digest[:], sig)
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPyCryptoVerifier struct{}
// Verify does the actual check.
// N.B. We have not been able to make this work in a way that is compatible
// with PyCrypto.
func (v RSAPyCryptoVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
k, _ := pem.Decode([]byte(key.Public()))
if k == nil {
logrus.Infof("Failed to decode PEM-encoded x509 certificate")
return ErrInvalid
}
pub, err := x509.ParsePKIXPublicKey(k.Bytes)
if err != nil {
logrus.Infof("Failed to parse public key: %s\n", err)
return ErrInvalid
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
logrus.Infof("Value returned from ParsePKIXPublicKey was not an RSA public key")
return verifyPSS(pub, digest[:], sig)
}
// RSAPSSPEMVerifier checks RSASSA-PSS signatures, extracting the public key
// from an X509 certificate.
type RSAPSSX509Verifier struct{}
// Verify does the actual check.
func (v RSAPSSX509Verifier) Verify(key data.Key, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
k, _ := pem.Decode([]byte(key.Public()))
if k == nil {
logrus.Infof("Failed to decode PEM-encoded x509 certificate")
return ErrInvalid
}
cert, err := x509.ParseCertificate(k.Bytes)
if err != nil {
logrus.Infof("Failed to parse x509 certificate: %s\n", err)
return ErrInvalid
}
opts := rsa.PSSOptions{SaltLength: sha256.Size, Hash: crypto.SHA256}
if err = rsa.VerifyPSS(rsaPub, crypto.SHA256, digest[:], sig, &opts); err != nil {
logrus.Infof("Failed verification: %s", err)
return ErrInvalid
}
return nil
return verifyPSS(cert.PublicKey, digest[:], sig)
}

View File

@ -3,7 +3,6 @@ package store
import (
"encoding/hex"
"encoding/json"
"encoding/pem"
"net/http"
"strings"
"testing"
@ -27,7 +26,7 @@ func TestGetMeta(t *testing.T) {
"txt",
"targets",
"key",
&TestRoundTripper{},
&http.Transport{},
)
if err != nil {
t.Fatal(err)
@ -42,8 +41,7 @@ func TestGetMeta(t *testing.T) {
t.Fatal(err)
}
rootPem := "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEArvqUPYb6JJROPJQglPTj\n5uDrsxQKl34Mo+3pSlBVuD6puE4lDnG649a2YksJy+C8ZIPJgokn5w+C3alh+dMe\nzbdWHHxrY1h9CLpYz5cbMlE16303ubkt1rvwDqEezG0HDBzPaKj4oP9YJ9x7wbsq\ndvFcy+Qc3wWd7UWcieo6E0ihbJkYcY8chRXVLg1rL7EfZ+e3bq5+ojA2ECM5JqzZ\nzgDpqCv5hTCYYZp72MZcG7dfSPAHrcSGIrwg7whzz2UsEtCOpsJTuCl96FPN7kAu\n4w/WyM3+SPzzr4/RQXuY1SrLCFD8ebM2zHt/3ATLhPnGmyG5I0RGYoegFaZ2AViw\nlqZDOYnBtgDvKP0zakMtFMbkh2XuNBUBO7Sjs0YcZMjLkh9gYUHL1yWS3Aqus1Lw\nlI0gHS22oyGObVBWkZEgk/Foy08sECLGao+5VvhmGpfVuiz9OKFUmtPVjWzRE4ng\niekEu4drSxpH41inLGSvdByDWLpcTvWQI9nkgclh3AT/AgMBAAE=\n-----END PUBLIC KEY-----"
testKey, _ := pem.Decode([]byte(rootPem))
k := data.NewPublicKey("rsa", testKey.Bytes)
k := data.NewPublicKey("RSA", []byte(rootPem))
sigBytes, err := hex.DecodeString(p.Signatures[0].Signature.String())
if err != nil {
@ -70,8 +68,7 @@ func TestPyCryptoRSAPSSCompat(t *testing.T) {
//privPem := "-----BEGIN RSA PRIVATE KEY-----\nMIIG4wIBAAKCAYEAnKuXZeefa2LmgxaL5NsMzKOHNe+x/nL6ik+lDBCTV6OdcwAh\nHQS+PONGhrChIUVR6Vth3hUCrreLzPO73Oo5VSCuRJ53UronENl6lsa5mFKP8StY\nLvIDITNvkoT3j52BJIjyNUK9UKY9As2TNqDfBEPIRp28ev/NViwGOEkBu2UAbwCI\ndnDXm8JQErCZA0Ydm7PKGgjLbFsFGrVzqXHK6pdzJXlhr9yap3UpgQ/iO9JtoEYB\n2EXsnSrPc9JRjR30bNHHtnVql3fvinXrAEwq3xmN4p+R4VGzfdQN+8Kl/IPjqWB5\n35twhFYEG/B7Ze8IwbygBjK3co/KnOPqMUrMBI8ztvPiogz+MvXb8WvarZ6TMTh8\nifZI96r7zzqyzjR1hJulEy3IsMGvz8XS2J0X7sXoaqszEtXdq5ef5zKVxkiyIQZc\nbPgmpHLq4MgfdryuVVc/RPASoRIXG4lKaTJj1ANMFPxDQpHudCLxwCzjCb+sVa20\nHBRPTnzo8LSZkI6jAgMBAAECggGAdzyI7z/HLt2IfoAsXDLynNRgVYZluzgawiU3\ngeUjnnGhpSKWERXJC2IWDPBk0YOGgcnQxErNTdfXiFZ/xfRlSgqjVwob2lRe4w4B\npLr+CZXcgznv1VrPUvdolOSp3R2Mahfn7u0qVDUQ/g8jWVI6KW7FACmQhzQkPM8o\ntLGrpcmK+PA465uaHKtYccEB02ILqrK8v++tknv7eIZczrsSKlS1h/HHjSaidYxP\n2DAUiF7wnChrwwQEvuEUHhwVgQcoDMBoow0zwHdbFiFO2ZT54H2oiJWLhpR/x6RK\ngM1seqoPH2sYErPJACMcYsMtF4Tx7b5c4WSj3vDCGb+jeqnNS6nFC3aMnv75mUS2\nYDPU1heJFd8pNHVf0RDejLZZUiJSnXf3vpOxt9Xv2+4He0jeMfLV7zX0mO2Ni3MJ\nx6PiVy4xerHImOuuHzSla5crOq2ECiAxd1wEOFDRD2LRHzfhpk1ghiA5xA1qwc7Z\neRnkVfoy6PPZ4lZakZTm0p8YCQURAoHBAMUIC/7vnayLae7POmgy+np/ty7iMfyd\nV1eO6LTO21KAaGGlhaY26WD/5LcG2FUgc5jKKahprGrmiNLzLUeQPckJmuijSEVM\nl/4DlRvCo867l7fLaVqYzsQBBdeGIFNiT+FBOd8atff87ZBEfH/rXbDi7METD/VR\n4TdblnCsKYAXEJUdkw3IK7SUGERiQZIwKXrH/Map4ibDrljJ71iCgEureU0DBwcg\nwLftmjGMISoLscdRxeubX5uf/yxtHBJeRwKBwQDLjzHhb4gNGdBHUl4hZPAGCq1V\nLX/GpfoOVObW64Lud+tI6N9GNua5/vWduL7MWWOzDTMZysganhKwsJCY5SqAA9p0\nb6ohusf9i1nUnOa2F2j+weuYPXrTYm+ZrESBBdaEJPuj3R5YHVujrBA9Xe0kVOe3\nne151A+0xJOI3tX9CttIaQAsXR7cMDinkDITw6i7X4olRMPCSixHLW97cDsVDRGt\necO1d4dP3OGscN+vKCoL6tDKDotzWHYPwjH47sUCgcEAoVI8WCiipbKkMnaTsNsE\ngKXvO0DSgq3k5HjLCbdQldUzIbgfnH7bSKNcBYtiNxjR7OihgRW8qO5GWsnmafCs\n1dy6a/2835id3cnbHRaZflvUFhVDFn2E1bCsstFLyFn3Y0w/cO9yzC/X5sZcVXRF\nit3R0Selakv3JZckru4XMJwx5JWJYMBjIIAc+miknWg3niL+UT6pPun65xG3mXWI\nS+yC7c4rw+dKQ44UMLs2MDHRBoxqi8T0W/x9NkfDszpjAoHAclH7S4ZdvC3RIR0L\nLGoJuvroGbwx1JiGdOINuooNwGuswge2zTIsJi0gN/H3hcB2E6rIFiYid4BrMrwW\nmSeq1LZVS6siu0qw4p4OVy+/CmjfWKQD8j4k6u6PipiK6IMk1JYIlSCr2AS04JjT\njgNgGVVtxVt2cUM9huIXkXjEaRZdzK7boA60NCkIyGJdHWh3LLQdW4zg/A64C0lj\nIMoJBGuQkAKgfRuh7KI6Q6Qom7BM3OCFXdUJUEBQHc2MTyeZAoHAJdBQGBn1RFZ+\nn75AnbTMZJ6Twp2fVjzWUz/+rnXFlo87ynA18MR2BzaDST4Bvda29UBFGb32Mux9\nOHukqLgIE5jDuqWjy4B5eCoxZf/OvwlgXkX9+gprGR3axn/PZBFPbFB4ZmjbWLzn\nbocn7FJCXf+Cm0cMmv1jIIxej19MUU/duq9iq4RkHY2LG+KrSEQIUVmImCftXdN3\n/qNP5JetY0eH6C+KRc8JqDB0nvbqZNOgYXOfYXo/5Gk8XIHTFihm\n-----END RSA PRIVATE KEY-----"
testStr := "The quick brown fox jumps over the lazy dog."
sigHex := "4e05ee9e435653549ac4eddbc43e1a6868636e8ea6dbec2564435afcb0de47e0824cddbd88776ddb20728c53ecc90b5d543d5c37575fda8bd0317025fc07de62ee8084b1a75203b1a23d1ef4ac285da3d1fc63317d5b2cf1aafa3e522acedd366ccd5fe4a7f02a42922237426ca3dc154c57408638b9bfaf0d0213855d4e9ee621db204151bcb13d4dbb18f930ec601469c992c84b14e9e0b6f91ac9517bb3b749dd117e1cbac2e4acb0e549f44558a2005898a226d5b6c8b9291d7abae0d9e0a16858b89662a085f74a202deb867acab792bdbd2c36731217caea8b17bd210c29b890472f11e5afdd1dd7b69004db070e04201778f2c49f5758643881403d45a58d08f51b5c63910c6185892f0b590f191d760b669eff2464456f130239bba94acf54a0cb98f6939ff84ae26a37f9b890be259d9b5d636f6eb367b53e895227d7d79a3a88afd6d28c198ee80f6527437c5fbf63accb81709925c4e03d1c9eaee86f58e4bd1c669d6af042dbd412de0d13b98b1111e2fadbe34b45de52125e9a"
testKey, _ := pem.Decode([]byte(pubPem))
k := data.NewPublicKey("rsa", testKey.Bytes)
k := data.NewPublicKey("RSA", []byte(pubPem))
sigBytes, err := hex.DecodeString(sigHex)
if err != nil {
@ -91,11 +88,11 @@ func TestPyNaCled25519Compat(t *testing.T) {
sigHex := "166e7013e48f26dccb4e68fe4cf558d1cd3af902f8395534336a7f8b4c56588694aa3ac671767246298a59d5ef4224f02c854f41bfcfe70241db4be1546d6a00"
pub, _ := hex.DecodeString(pubHex)
k := data.NewPublicKey("ed25519", pub)
k := data.NewPublicKey("ED25519", pub)
sigBytes, _ := hex.DecodeString(sigHex)
err := signed.Verifiers["ed25519"].Verify(k, sigBytes, []byte(testStr))
err := signed.Verifiers["ED25519"].Verify(k, sigBytes, []byte(testStr))
if err != nil {
t.Fatal(err)
}

View File

@ -1,21 +0,0 @@
package store
import "bytes"
import "reflect"
import "testing"
func testMeta(t *testing.T, s *MetadataStore) {
storeName := reflect.TypeOf(s).Name()
testData := []byte{}
err := s.SetMeta(testName, testData)
if err != nil {
t.Fatal(err)
}
out, err := s.GetMeta(testName, len(testData))
if err != nil {
t.Fatal(err)
}
if bytes.Compare(testData, out) != 0 {
t.Fatalf("%s mangled data", storeName)
}
}

View File

@ -23,12 +23,16 @@ func SampleMeta() data.FileMeta {
}
func GetSqliteDB() *sql.DB {
os.Mkdir("/tmp/sqlite", 0755)
conn, err := sql.Open("sqlite3", fmt.Sprintf("/tmp/sqlite/file%d.db", counter))
if err != nil {
panic("can't connect to db")
}
counter++
tx, _ := conn.Begin()
tx, err := conn.Begin()
if err != nil {
panic("can't begin db transaction")
}
tx.Exec("CREATE TABLE keys (id int auto_increment, namespace varchar(255) not null, role varchar(255) not null, key text not null, primary key (id));")
tx.Exec("CREATE TABLE filehashes(namespace varchar(255) not null, path varchar(255) not null, alg varchar(10) not null, hash varchar(128) not null, primary key (namespace, path, alg));")
tx.Exec("CREATE TABLE filemeta(namespace varchar(255) not null, path varchar(255) not null, size int not null, custom text default null, primary key (namespace, path));")

View File

@ -177,16 +177,13 @@ func (tr *TufRepo) UpdateDelegations(role *data.Role, keys []data.Key, before st
// also relies on the keysDB having already been populated with the keys and
// roles.
func (tr *TufRepo) InitRepo(consistent bool) error {
err := tr.InitRoot(consistent)
if err != nil {
if err := tr.InitRoot(consistent); err != nil {
return err
}
tr.InitTargets()
if err != nil {
if err := tr.InitTargets(); err != nil {
return err
}
tr.InitSnapshot()
if err != nil {
if err := tr.InitSnapshot(); err != nil {
return err
}
return tr.InitTimestamp()

View File

@ -1,7 +1,6 @@
package utils
import (
"bytes"
"encoding/hex"
"testing"
@ -16,40 +15,6 @@ type UtilSuite struct{}
var _ = Suite(&UtilSuite{})
func (UtilSuite) TestGenerateFileMetaDefault(c *C) {
// default is sha512
r := bytes.NewReader([]byte("foo"))
meta, err := GenerateFileMeta(r)
c.Assert(err, IsNil)
c.Assert(meta.Length, Equals, int64(3))
hashes := meta.Hashes
c.Assert(hashes, HasLen, 1)
hash, ok := hashes["sha512"]
if !ok {
c.Fatal("missing sha512 hash")
}
c.Assert(hash.String(), DeepEquals, "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7")
}
func (UtilSuite) TestGenerateFileMetaExplicit(c *C) {
r := bytes.NewReader([]byte("foo"))
meta, err := GenerateFileMeta(r, "sha256", "sha512")
c.Assert(err, IsNil)
c.Assert(meta.Length, Equals, int64(3))
hashes := meta.Hashes
c.Assert(hashes, HasLen, 2)
for name, val := range map[string]string{
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"sha512": "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7",
} {
hash, ok := hashes[name]
if !ok {
c.Fatalf("missing %s hash", name)
}
c.Assert(hash.String(), DeepEquals, val)
}
}
func (UtilSuite) TestFileMetaEqual(c *C) {
type test struct {
name string

View File

@ -6,9 +6,9 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
@ -17,16 +17,9 @@ import (
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
// operate on
type CryptoService struct {
gun string
keyStore *trustmanager.KeyFileStore
}
// RootCryptoService implements Sign and Create and operates on a rootKeyStore,
// taking in a passphrase and calling decrypt when signing.
type RootCryptoService struct {
// TODO(diogo): support multiple passphrases per key
passphrase string
rootKeyStore *trustmanager.KeyFileStore
gun string
passphrase string
keyStore *trustmanager.KeyFileStore
}
// NewCryptoService returns an instance of CryptoService
@ -34,11 +27,6 @@ func NewCryptoService(gun string, keyStore *trustmanager.KeyFileStore) *CryptoSe
return &CryptoService{gun: gun, keyStore: keyStore}
}
// NewRootCryptoService returns an instance of CryptoService
func NewRootCryptoService(rootKeyStore *trustmanager.KeyFileStore, passphrase string) *RootCryptoService {
return &RootCryptoService{rootKeyStore: rootKeyStore, passphrase: passphrase}
}
// Create is used to generate keys for targets, snapshots and timestamps
func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
@ -52,7 +40,14 @@ func (ccs *CryptoService) Create(role string) (*data.PublicKey, error) {
return data.PublicKeyFromPrivate(*privKey), nil
}
// Sign returns the signatures for data with the given keyIDs
// SetPassphrase tells the cryptoservice the passphrase. Use only if the key needs
// to be decrypted.
func (ccs *CryptoService) SetPassphrase(passphrase string) {
ccs.passphrase = passphrase
}
// Sign returns the signatures for data with the given root Key ID, falling back
// if not rootKeyID is found
func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
// Create hasher and hash data
hash := crypto.SHA256
@ -60,11 +55,27 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
signatures := make([]data.Signature, 0, len(keyIDs))
for _, fingerprint := range keyIDs {
// Get the PrivateKey filename
privKeyFilename := filepath.Join(ccs.gun, fingerprint)
// ccs.gun will be empty if this is the root key
keyName := filepath.Join(ccs.gun, fingerprint)
var privKey *data.PrivateKey
var err error
var method string
// Read PrivateKey from file
privKey, err := ccs.keyStore.GetKey(privKeyFilename)
if ccs.passphrase != "" {
// This is a root key
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
method = "RSASSA-PSS-X509"
} else {
privKey, err = ccs.keyStore.GetKey(keyName)
method = "RSASSA-PSS"
}
if err != nil {
// Note that GetDecryptedKey always fails on InitRepo.
// InitRepo gets a signer that doesn't have access to
// the root keys. Continuing here is safe because we
// end up not returning any signatures.
continue
}
@ -76,44 +87,7 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
// Append signatures to result array
signatures = append(signatures, data.Signature{
KeyID: fingerprint,
Method: "RSA",
Signature: sig[:],
})
}
return signatures, nil
}
// Create in a root crypto service is not implemented
func (rcs *RootCryptoService) Create(role string) (*data.PublicKey, error) {
return nil, errors.New("create on a root key filestore is not implemented")
}
// Sign returns the signatures for data with the given root Key ID, falling back
// if not rootKeyID is found
// TODO(diogo): This code has 1 line change from the Sign from Crypto service. DRY it up.
func (rcs *RootCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
// Create hasher and hash data
hash := crypto.SHA256
hashed := sha256.Sum256(payload)
signatures := make([]data.Signature, 0, len(keyIDs))
for _, fingerprint := range keyIDs {
// Read PrivateKey from file
privKey, err := rcs.rootKeyStore.GetDecryptedKey(fingerprint, rcs.passphrase)
if err != nil {
// TODO(diogo): This error should be returned to the user in someway
continue
}
sig, err := sign(privKey, hash, hashed[:])
if err != nil {
return nil, err
}
// Append signatures to result array
signatures = append(signatures, data.Signature{
KeyID: fingerprint,
Method: "RSASSA-PKCS1-V1_5-SIGN",
Method: method,
Signature: sig[:],
})
}
@ -123,7 +97,7 @@ func (rcs *RootCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sign
func sign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
// TODO(diogo): Implement support for ECDSA.
if privKey.Cipher() != "RSA" {
if strings.ToLower(privKey.Cipher()) != "rsa" {
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
}
@ -134,7 +108,7 @@ func sign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, er
}
// Use the RSA key to sign the data
sig, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivKey, hash, hashed[:])
sig, err := rsa.SignPSS(rand.Reader, rsaPrivKey, hash, hashed[:], &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
if err != nil {
return nil, err
}

View File

@ -62,7 +62,7 @@ type UnlockedSigner struct {
// repository.
type NotaryRepository struct {
baseDir string
Gun string
gun string
baseURL string
tufRepoPath string
caStore trustmanager.X509Store
@ -111,10 +111,11 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
return nil, err
}
fmt.Println("creating non-root cryptoservice")
signer := signed.NewSigner(NewCryptoService(gun, privKeyStore))
nRepo := &NotaryRepository{
Gun: gun,
gun: gun,
baseDir: baseDir,
baseURL: baseURL,
tufRepoPath: filepath.Join(baseDir, tufDir, gun),
@ -133,7 +134,7 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
// Initialize creates a new repository by using rootKey as the root Key for the
// TUF repository.
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
rootCert, err := uSigner.GenerateCertificate(r.Gun)
rootCert, err := uSigner.GenerateCertificate(r.gun)
if err != nil {
return err
}
@ -144,7 +145,7 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
return err
}
remote, err := getRemoteStore(r.baseURL, r.Gun, r.roundTrip)
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
rawTSKey, err := remote.GetKey("timestamp")
if err != nil {
return err
@ -377,7 +378,7 @@ func (r *NotaryRepository) Publish(getPass passwordRetriever) error {
return err
}
remote, err := getRemoteStore(r.baseURL, r.Gun, r.roundTrip)
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
if err != nil {
return err
}
@ -554,14 +555,14 @@ func (r *NotaryRepository) validateRoot(root *data.Signed) error {
// Cert.Raw. It's included to prevent breaking logic with changes of how the
// ID gets computed.
_, err = r.certificateStore.GetCertificateByFingerprint(leafID)
if err == nil && leafCert.Subject.CommonName == r.Gun {
if err == nil && leafCert.Subject.CommonName == r.gun {
certs[fingerprint] = rootSigned.Keys[fingerprint]
}
// Check to see if this leafCertificate has a chain to one of the Root CAs
// of our CA Store.
certList := []*x509.Certificate{leafCert}
err = trustmanager.Verify(r.caStore, r.Gun, certList)
err = trustmanager.Verify(r.caStore, r.gun, certList)
if err == nil {
certs[fingerprint] = rootSigned.Keys[fingerprint]
}
@ -577,7 +578,7 @@ func (r *NotaryRepository) validateRoot(root *data.Signed) error {
}
func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
remote, err := getRemoteStore(r.baseURL, r.Gun, r.roundTrip)
remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
if err != nil {
return nil, err
}
@ -638,7 +639,12 @@ func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*Unlocke
// This signer will be used for all of the normal TUF operations, except for
// when a root key is needed.
signer := signed.NewSigner(NewRootCryptoService(r.rootKeyStore, passphrase))
// Passing an empty GUN because root keys aren't associated with a GUN.
fmt.Println("creating root cryptoservice with passphrase", passphrase)
ccs := NewCryptoService("", r.rootKeyStore)
ccs.SetPassphrase(passphrase)
signer := signed.NewSigner(ccs)
return &UnlockedSigner{
privKey: privKey,

View File

@ -46,23 +46,23 @@ func TestGetTimestampKey(t *testing.T) {
//_, _, err := s.GetTimestampKey("gun")
//assert.IsType(t, &ErrNoKey{}, err, "Expected err to be ErrNoKey")
s.SetTimestampKey("gun", "rsa", []byte("test"))
s.SetTimestampKey("gun", "RSA", []byte("test"))
c, k, err := s.GetTimestampKey("gun")
assert.Nil(t, err, "Expected error to be nil")
assert.Equal(t, "rsa", c, "Expected cipher rsa, received %s", c)
assert.Equal(t, "RSA", c, "Expected cipher rsa, received %s", c)
assert.Equal(t, []byte("test"), k, "Key data was wrong")
}
func TestSetTimestampKey(t *testing.T) {
s := NewMemStorage()
s.SetTimestampKey("gun", "rsa", []byte("test"))
s.SetTimestampKey("gun", "RSA", []byte("test"))
err := s.SetTimestampKey("gun", "rsa", []byte("test2"))
err := s.SetTimestampKey("gun", "RSA", []byte("test2"))
assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists")
k := s.tsKeys["gun"]
assert.Equal(t, "rsa", k.cipher, "Expected cipher to be rsa, received %s", k.cipher)
assert.Equal(t, "RSA", k.cipher, "Expected cipher to be rsa, received %s", k.cipher)
assert.Equal(t, []byte("test"), k.public, "Public key did not match expected")
}