CSR Service (part 2): cert signing rpc (#950)

* CSR Service:

+ implement certificate sign rpc method
+ implement certificate signer client/server
+ refactor `AuthorizationDB#Create`
+ refactor `NewTestIdentity`
+ add `AuthorizationDB#Claim`
+ add `Token#Equal`
+ fix `Authorizations#Marshal` when marshaling identities and certificates
+ tweak `Authorization#String` format
+ cert debugging improvements (jsondiff)
+ receive context arg in `NewTestIdentity`
+ misc. fixes
This commit is contained in:
Bryan White 2019-01-02 12:39:17 -05:00 committed by GitHub
parent b712fbcbb0
commit 249244536a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1180 additions and 380 deletions

View File

@ -57,7 +57,7 @@ goimports-fix: ## Applies goimports to every go file (excluding vendored files)
.PHONY: goimports-st .PHONY: goimports-st
goimports-st: ## Applies goimports to every go file in `git status` (ignores untracked files) goimports-st: ## Applies goimports to every go file in `git status` (ignores untracked files)
git status --porcelain -uno|grep .go|sed -E 's,\w+\s+,,g'|xargs -I {} goimports -w -local storj.io {} git status --porcelain -uno|grep .go|grep -v "^D"|sed -E 's,\w+\s+(.+->\s+)?,,g'|xargs -I {} goimports -w -local storj.io {}
.PHONY: proto .PHONY: proto
proto: ## Rebuild protobuf files proto: ## Rebuild protobuf files

3
go.mod
View File

@ -42,7 +42,7 @@ require (
github.com/go-redis/redis v6.14.1+incompatible github.com/go-redis/redis v6.14.1+incompatible
github.com/gogo/protobuf v1.1.2-0.20181116123445-07eab6a8298c github.com/gogo/protobuf v1.1.2-0.20181116123445-07eab6a8298c
github.com/golang-migrate/migrate/v3 v3.5.2 github.com/golang-migrate/migrate/v3 v3.5.2
github.com/golang/mock v1.1.1 github.com/golang/mock v1.2.0
github.com/golang/protobuf v1.2.0 github.com/golang/protobuf v1.2.0
github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-cmp v0.2.0 github.com/google/go-cmp v0.2.0
@ -81,6 +81,7 @@ require (
github.com/nats-io/nats v1.6.0 // indirect github.com/nats-io/nats v1.6.0 // indirect
github.com/nats-io/nats-streaming-server v0.11.0 // indirect github.com/nats-io/nats-streaming-server v0.11.0 // indirect
github.com/nats-io/nuid v1.0.0 // indirect github.com/nats-io/nuid v1.0.0 // indirect
github.com/nsf/jsondiff v0.0.0-20160203110537-7de28ed2b6e3
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
github.com/pierrec/lz4 v2.0.5+incompatible // indirect github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/pkg/profile v1.2.1 // indirect github.com/pkg/profile v1.2.1 // indirect

10
go.sum
View File

@ -105,6 +105,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -179,8 +181,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kshvakov/clickhouse v1.3.4/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= github.com/kshvakov/clickhouse v1.3.4/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/loov/hrtime v0.0.0-20180911122900-a9e82bc6c180 h1:kLwg5eA/kaWQ/RwANTH7Gg+VdxmdjbcSWyaS/1VQGkA=
github.com/loov/hrtime v0.0.0-20180911122900-a9e82bc6c180/go.mod h1:2871C3urfEJnq/bpTYjFdMOdgxVd8otLLEL6vMNy/Iw=
github.com/loov/hrtime v0.0.0-20181214195526-37a208e8344e h1:UC+nLCm+w3WL+ibAW/wsWbQC3KAz7LLawR2hgX0eR9s= github.com/loov/hrtime v0.0.0-20181214195526-37a208e8344e h1:UC+nLCm+w3WL+ibAW/wsWbQC3KAz7LLawR2hgX0eR9s=
github.com/loov/hrtime v0.0.0-20181214195526-37a208e8344e/go.mod h1:2871C3urfEJnq/bpTYjFdMOdgxVd8otLLEL6vMNy/Iw= github.com/loov/hrtime v0.0.0-20181214195526-37a208e8344e/go.mod h1:2871C3urfEJnq/bpTYjFdMOdgxVd8otLLEL6vMNy/Iw=
github.com/loov/plot v0.0.0-20180510142208-e59891ae1271 h1:51ToN6N0TDtCruf681gufYuEhO9qFHQzM3RFTS/n6XE= github.com/loov/plot v0.0.0-20180510142208-e59891ae1271 h1:51ToN6N0TDtCruf681gufYuEhO9qFHQzM3RFTS/n6XE=
@ -189,8 +189,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5 h1:0x4qcEHDpruK6ML/m/YSlFUUu0UpRD3I2PHsNCuGnyA= github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5 h1:0x4qcEHDpruK6ML/m/YSlFUUu0UpRD3I2PHsNCuGnyA=
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
@ -241,6 +239,8 @@ github.com/nats-io/nats-streaming-server v0.11.0 h1:6d32ASBeZJQOoams2GJuviQyf5GV
github.com/nats-io/nats-streaming-server v0.11.0/go.mod h1:RyqtDJZvMZO66YmyjIYdIvS69zu/wDAkyNWa8PIUa5c= github.com/nats-io/nats-streaming-server v0.11.0/go.mod h1:RyqtDJZvMZO66YmyjIYdIvS69zu/wDAkyNWa8PIUa5c=
github.com/nats-io/nuid v1.0.0 h1:44QGdhbiANq8ZCbUkdn6W5bqtg+mHuDE4wOUuxxndFs= github.com/nats-io/nuid v1.0.0 h1:44QGdhbiANq8ZCbUkdn6W5bqtg+mHuDE4wOUuxxndFs=
github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nsf/jsondiff v0.0.0-20160203110537-7de28ed2b6e3 h1:OqFSgO6CJ8heZRAbXLpT+ojX+jnnGij4qZwUz/SJJ9I=
github.com/nsf/jsondiff v0.0.0-20160203110537-7de28ed2b6e3/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
@ -322,8 +322,6 @@ github.com/yuin/gopher-lua v0.0.0-20180918061612-799fa34954fb h1:Jmfk7z2f/+gxVFA
github.com/yuin/gopher-lua v0.0.0-20180918061612-799fa34954fb/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/yuin/gopher-lua v0.0.0-20180918061612-799fa34954fb/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
github.com/zeebo/admission v0.0.0-20180821192747-f24f2a94a40c h1:WoYvMZp+keiJz+ZogLAhwsUZvWe81W+mCnpfdgEUOl4= github.com/zeebo/admission v0.0.0-20180821192747-f24f2a94a40c h1:WoYvMZp+keiJz+ZogLAhwsUZvWe81W+mCnpfdgEUOl4=
github.com/zeebo/admission v0.0.0-20180821192747-f24f2a94a40c/go.mod h1:Aq7yiXoKLFIDzh4eR6EG4owIO9alpttZ0XJ5c/z/QrE= github.com/zeebo/admission v0.0.0-20180821192747-f24f2a94a40c/go.mod h1:Aq7yiXoKLFIDzh4eR6EG4owIO9alpttZ0XJ5c/z/QrE=
github.com/zeebo/errs v1.0.0 h1:uPx2/S2dQn9npw2Y9AKTae12AHkHSqk1WiaRiBNguFA=
github.com/zeebo/errs v1.0.0/go.mod h1:Yj8dHrUQwls1bF3dr/vcSIu+qf4mI7idnTcHfoACc6I=
github.com/zeebo/errs v1.1.0 h1:4dNyQKsWPyBDqLzZUpx+QMP0Qil9STQPdBsKk6+O2qA= github.com/zeebo/errs v1.1.0 h1:4dNyQKsWPyBDqLzZUpx+QMP0Qil9STQPdBsKk6+O2qA=
github.com/zeebo/errs v1.1.0/go.mod h1:Yj8dHrUQwls1bF3dr/vcSIu+qf4mI7idnTcHfoACc6I= github.com/zeebo/errs v1.1.0/go.mod h1:Yj8dHrUQwls1bF3dr/vcSIu+qf4mI7idnTcHfoACc6I=
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc= github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=

View File

@ -0,0 +1,96 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package debugging
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/json"
"fmt"
"math/big"
"github.com/nsf/jsondiff"
)
var (
diffOpts = jsondiff.DefaultConsoleOptions()
)
// DebugCert is a subset of the most relevant fields from an x509.Certificate for debugging
type DebugCert struct {
Cert *x509.Certificate
}
// NewDebugCert converts an *x509.Certificate into a DebugCert
func NewDebugCert(cert x509.Certificate) DebugCert {
return DebugCert{
Cert: &cert,
}
}
// PrintJSON uses a json marshaler to pretty-print arbitrary data for debugging
// with special considerations for certain, specific types
func PrintJSON(data interface{}, label string) {
var (
jsonBytes []byte
err error
)
switch d := data.(type) {
case x509.Certificate:
data = NewDebugCert(d)
case *x509.Certificate:
data = NewDebugCert(*d)
case ecdsa.PublicKey:
data = struct {
X *big.Int
Y *big.Int
}{
d.X, d.Y,
}
case *ecdsa.PrivateKey:
data = struct {
X *big.Int
Y *big.Int
D *big.Int
}{
d.X, d.Y, d.D,
}
}
jsonBytes, err = json.MarshalIndent(data, "", "\t\t")
if label != "" {
fmt.Println(label + ": ---================================================================---")
}
if err != nil {
fmt.Printf("ERROR: %s", err.Error())
}
fmt.Println(string(jsonBytes))
fmt.Println("")
}
// Cmp is used to compare 2 DebugCerts against each other and print the diff
func (c DebugCert) Cmp(c2 DebugCert, label string) error {
fmt.Println("diff " + label + " ---================================================================---")
cJSON, err := c.JSON()
if err != nil {
return err
}
c2JSON, err := c2.JSON()
if err != nil {
return err
}
diffType, diff := jsondiff.Compare(cJSON, c2JSON, &diffOpts)
fmt.Printf("Difference type: %s\n======\n%s", diffType, diff)
return nil
}
// JSON serializes the certificate to JSON
func (c DebugCert) JSON() ([]byte, error) {
return json.Marshal(c.Cert)
}

View File

@ -11,8 +11,8 @@ import (
// NewTestIdentity is a helper function to generate new node identities with // NewTestIdentity is a helper function to generate new node identities with
// correct difficulty and concurrency // correct difficulty and concurrency
func NewTestIdentity() (*provider.FullIdentity, error) { func NewTestIdentity(ctx context.Context) (*provider.FullIdentity, error) {
ca, err := provider.NewCA(context.Background(), provider.NewCAOptions{ ca, err := provider.NewCA(ctx, provider.NewCAOptions{
Difficulty: 12, Difficulty: 12,
Concurrency: 4, Concurrency: 4,
}) })

View File

@ -4,102 +4,43 @@
package testpeertls package testpeertls
import ( import (
"bytes" "crypto"
"crypto/ecdsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/json" "storj.io/storj/pkg/peertls"
"fmt"
"math/big"
) )
// DebugCert is a subset of the most relevant fields from an x509.Certificate for debugging // NewCertChain creates a valid peertls certificate chain (and respective keys) of the desired length.
type DebugCert struct { // NB: keys are in the reverse order compared to certs (i.e. first key belongs to last cert)!
Raw []byte func NewCertChain(length int) (keys []crypto.PrivateKey, certs []*x509.Certificate, _ error) {
RawTBSCertificate []byte for i := 0; i < length; i++ {
Signature []byte key, err := peertls.NewKey()
PublicKeyX *big.Int if err != nil {
PublicKeyY *big.Int return nil, nil, err
Extensions []pkix.Extension
} }
keys = append(keys, key)
// NewCertDebug converts an *x509.Certificate into a DebugCert var template *x509.Certificate
func NewCertDebug(cert x509.Certificate) DebugCert { if i == length-1 {
pubKey := cert.PublicKey.(*ecdsa.PublicKey) template, err = peertls.CATemplate()
c := DebugCert{ } else {
Raw: make([]byte, len(cert.Raw)), template, err = peertls.LeafTemplate()
RawTBSCertificate: make([]byte, len(cert.RawTBSCertificate)),
Signature: make([]byte, len(cert.Signature)),
PublicKeyX: pubKey.X,
PublicKeyY: pubKey.Y,
Extensions: []pkix.Extension{},
}
copy(c.Raw, cert.Raw)
copy(c.RawTBSCertificate, cert.RawTBSCertificate)
copy(c.Signature, cert.Signature)
for _, e := range cert.ExtraExtensions {
ext := pkix.Extension{Id: e.Id, Value: make([]byte, len(e.Value))}
copy(ext.Value, e.Value)
c.Extensions = append(c.Extensions, ext)
}
return c
}
// Cmp is used to compare 2 DebugCerts against each other and print the diff
func (c DebugCert) Cmp(c2 DebugCert, label string) {
fmt.Println("diff " + label + " ---================================================================---")
cmpBytes := func(a, b []byte) {
PrintJSON(bytes.Compare(a, b), "")
}
cmpBytes(c.Raw, c2.Raw)
cmpBytes(c.RawTBSCertificate, c2.RawTBSCertificate)
cmpBytes(c.Signature, c2.Signature)
c.PublicKeyX.Cmp(c2.PublicKeyX)
c.PublicKeyY.Cmp(c2.PublicKeyY)
}
// PrintJSON uses a json marshaler to pretty-print arbitrary data for debugging
// with special considerations for certain, specific types
func PrintJSON(data interface{}, label string) {
var (
jsonBytes []byte
err error
)
switch d := data.(type) {
case x509.Certificate:
data = NewCertDebug(d)
case *x509.Certificate:
data = NewCertDebug(*d)
case ecdsa.PublicKey:
data = struct {
X *big.Int
Y *big.Int
}{
d.X, d.Y,
}
case *ecdsa.PrivateKey:
data = struct {
X *big.Int
Y *big.Int
D *big.Int
}{
d.X, d.Y, d.D,
}
}
jsonBytes, err = json.MarshalIndent(data, "", "\t\t")
if label != "" {
fmt.Println(label + ": ---================================================================---")
} }
if err != nil { if err != nil {
fmt.Printf("ERROR: %s", err.Error()) return nil, nil, err
} }
fmt.Println(string(jsonBytes)) var cert *x509.Certificate
fmt.Println("") if i == 0 {
cert, err = peertls.NewCert(key, nil, template, nil)
} else {
cert, err = peertls.NewCert(key, keys[i-1], template, certs[i-1:][0])
}
if err != nil {
return nil, nil, err
}
certs = append([]*x509.Certificate{cert}, certs...)
}
return keys, certs, nil
} }

View File

@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/zap" "go.uber.org/zap"
testidentity "storj.io/storj/internal/identity"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/pkg/bwagreement" "storj.io/storj/pkg/bwagreement"
"storj.io/storj/pkg/bwagreement/test" "storj.io/storj/pkg/bwagreement/test"
"storj.io/storj/pkg/overlay" "storj.io/storj/pkg/overlay"
@ -58,7 +58,7 @@ func TestQueryWithBw(t *testing.T) {
tally := newTally(zap.NewNop(), db.Accounting(), bwDb, pointerdb, overlayServer, 0, time.Second) tally := newTally(zap.NewNop(), db.Accounting(), bwDb, pointerdb, overlayServer, 0, time.Second)
//get a private key //get a private key
fiC, err := testidentity.NewTestIdentity() fiC, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
k, ok := fiC.Key.(*ecdsa.PrivateKey) k, ok := fiC.Key.(*ecdsa.PrivateKey)
assert.True(t, ok) assert.True(t, ok)

View File

@ -16,7 +16,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
testidentity "storj.io/storj/internal/identity" "storj.io/storj/internal/testidentity"
"storj.io/storj/internal/teststorj" "storj.io/storj/internal/teststorj"
"storj.io/storj/pkg/auth" "storj.io/storj/pkg/auth"
"storj.io/storj/pkg/overlay" "storj.io/storj/pkg/overlay"

View File

@ -11,7 +11,7 @@ import (
"github.com/gtank/cryptopasta" "github.com/gtank/cryptopasta"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"storj.io/storj/internal/identity" "storj.io/storj/internal/testidentity"
) )
func TestGenerateSignature(t *testing.T) { func TestGenerateSignature(t *testing.T) {

View File

@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.uber.org/zap" "go.uber.org/zap"
testidentity "storj.io/storj/internal/identity"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/pkg/bwagreement" "storj.io/storj/pkg/bwagreement"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"
"storj.io/storj/satellite" "storj.io/storj/satellite"
@ -41,7 +41,7 @@ func TestBandwidthAgreements(t *testing.T) {
} }
func generateKeys(ctx context.Context, t *testing.T) (satellitePubKey *ecdsa.PublicKey, satellitePrivKey *ecdsa.PrivateKey, uplinkPrivKey *ecdsa.PrivateKey) { func generateKeys(ctx context.Context, t *testing.T) (satellitePubKey *ecdsa.PublicKey, satellitePrivKey *ecdsa.PrivateKey, uplinkPrivKey *ecdsa.PrivateKey) {
fiS, err := testidentity.NewTestIdentity() fiS, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
satellitePubKey, ok := fiS.Leaf.PublicKey.(*ecdsa.PublicKey) satellitePubKey, ok := fiS.Leaf.PublicKey.(*ecdsa.PublicKey)
@ -50,7 +50,7 @@ func generateKeys(ctx context.Context, t *testing.T) (satellitePubKey *ecdsa.Pub
satellitePrivKey, ok = fiS.Key.(*ecdsa.PrivateKey) satellitePrivKey, ok = fiS.Key.(*ecdsa.PrivateKey)
assert.True(t, ok) assert.True(t, ok)
fiU, err := testidentity.NewTestIdentity() fiU, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
uplinkPrivKey, ok = fiU.Key.(*ecdsa.PrivateKey) uplinkPrivKey, ok = fiU.Key.(*ecdsa.PrivateKey)

View File

@ -6,21 +6,27 @@ package certificates
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/x509"
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"time"
"github.com/btcsuite/btcutil/base58" "github.com/btcsuite/btcutil/base58"
"github.com/zeebo/errs" "github.com/zeebo/errs"
"go.uber.org/zap" "go.uber.org/zap"
"gopkg.in/spacemonkeygo/monkit.v2" "google.golang.org/grpc/peer"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"
"storj.io/storj/pkg/peertls" "storj.io/storj/pkg/peertls"
"storj.io/storj/pkg/provider" "storj.io/storj/pkg/provider"
"storj.io/storj/pkg/transport"
"storj.io/storj/pkg/utils" "storj.io/storj/pkg/utils"
"storj.io/storj/storage" "storj.io/storj/storage"
"storj.io/storj/storage/boltdb" "storj.io/storj/storage/boltdb"
@ -30,6 +36,9 @@ import (
const ( const (
// AuthorizationsBucket is the bucket used with a bolt-backed authorizations DB. // AuthorizationsBucket is the bucket used with a bolt-backed authorizations DB.
AuthorizationsBucket = "authorizations" AuthorizationsBucket = "authorizations"
// MaxClaimDelaySeconds is the max duration in seconds in the past or
// future that a claim timestamp is allowed to have and still be valid.
MaxClaimDelaySeconds = 15
tokenDataLength = 64 // 2^(64*8) =~ 1.34E+154 tokenDataLength = 64 // 2^(64*8) =~ 1.34E+154
tokenDelimiter = ":" tokenDelimiter = ":"
tokenVersion = 0 tokenVersion = 0
@ -47,15 +56,26 @@ var (
ErrAuthorizationCount = ErrAuthorizationDB.New("cannot add less than one authorizations") ErrAuthorizationCount = ErrAuthorizationDB.New("cannot add less than one authorizations")
) )
// CertSignerConfig is a config struct for use with a certificate signing service // CertSigningConfig is a config struct for use with a certificate signing service client
type CertSigningConfig struct {
AuthToken string `help:"authorization token to use to claim a certificate signing request (only applicable for the alpha network)"`
Address string `help:"address of the certificate signing rpc service"`
}
// CertSignerConfig is a config struct for use with a certificate signing service server
type CertSignerConfig struct { type CertSignerConfig struct {
Overwrite bool `help:"if true, overwrites config AND authorization db is truncated" default:"false"` Overwrite bool `help:"if true, overwrites config AND authorization db is truncated" default:"false"`
AuthorizationDBURL string `help:"url to the certificate signing authorization database" default:"bolt://$CONFDIR/authorizations.db"` AuthorizationDBURL string `help:"url to the certificate signing authorization database" default:"bolt://$CONFDIR/authorizations.db"`
MinDifficulty uint `help:"minimum difficulty of the requester's identity required to claim an authorization"`
CA provider.FullCAConfig
} }
// CertificateSigner implements pb.CertificatesServer // CertificateSigner implements pb.CertificatesServer
type CertificateSigner struct { type CertificateSigner struct {
Log *zap.Logger Logger *zap.Logger
Signer *provider.FullCertificateAuthority
AuthDB *AuthorizationDB
MinDifficulty uint16
} }
// AuthorizationDB stores authorizations which may be claimed in exchange for a // AuthorizationDB stores authorizations which may be claimed in exchange for a
@ -82,22 +102,52 @@ type Token struct {
Data [tokenDataLength]byte Data [tokenDataLength]byte
} }
// ClaimOpts hold parameters for claiming an authorization
type ClaimOpts struct {
Req *pb.SigningRequest
Peer *peer.Peer
ChainBytes [][]byte
MinDifficulty uint16
}
// Claim holds information about the circumstances under which an authorization // Claim holds information about the circumstances under which an authorization
// token was claimed. // token was claimed.
type Claim struct { type Claim struct {
IP string Addr string
Timestamp int64 Timestamp int64
Identity *provider.PeerIdentity Identity *provider.PeerIdentity
SignedCert *x509.Certificate SignedChainBytes [][]byte
} }
// NewServer creates a new certificate signing grpc server // Client implements pb.CertificateClient
func NewServer(log *zap.Logger) pb.CertificatesServer { type Client struct {
srv := CertificateSigner{ client pb.CertificatesClient
Log: log,
} }
return &srv func init() {
gob.Register(&ecdsa.PublicKey{})
gob.Register(elliptic.P256())
}
// NewClient creates a new certificate signing grpc client
func NewClient(ctx context.Context, ident *provider.FullIdentity, address string) (*Client, error) {
tc := transport.NewClient(ident)
conn, err := tc.DialAddress(ctx, address)
if err != nil {
return nil, err
}
return &Client{
client: pb.NewCertificatesClient(conn),
}, nil
}
// NewClientFrom creates a new certificate signing grpc client from an existing
// grpc cert signing client
func NewClientFrom(client pb.CertificatesClient) (*Client, error) {
return &Client{
client: client,
}, nil
} }
// NewAuthorization creates a new, unclaimed authorization with a random token value // NewAuthorization creates a new, unclaimed authorization with a random token value
@ -141,6 +191,20 @@ func ParseToken(tokenString string) (*Token, error) {
return t, nil return t, nil
} }
// Sign claims an authorization using the token string and returns a signed
// copy of the client's CA certificate
func (c Client) Sign(ctx context.Context, tokenStr string) ([][]byte, error) {
res, err := c.client.Sign(ctx, &pb.SigningRequest{
AuthToken: tokenStr,
Timestamp: time.Now().Unix(),
})
if err != nil {
return nil, err
}
return res.Chain, nil
}
// NewAuthDB creates or opens the authorization database specified by the config // NewAuthDB creates or opens the authorization database specified by the config
func (c CertSignerConfig) NewAuthDB() (*AuthorizationDB, error) { func (c CertSignerConfig) NewAuthDB() (*AuthorizationDB, error) {
// TODO: refactor db selection logic? // TODO: refactor db selection logic?
@ -186,7 +250,25 @@ func (c CertSignerConfig) NewAuthDB() (*AuthorizationDB, error) {
func (c CertSignerConfig) Run(ctx context.Context, server *provider.Provider) (err error) { func (c CertSignerConfig) Run(ctx context.Context, server *provider.Provider) (err error) {
defer mon.Task()(&ctx)(&err) defer mon.Task()(&ctx)(&err)
srv := NewServer(zap.L()) authDB, err := c.NewAuthDB()
if err != nil {
return err
}
defer func() {
err = errs.Combine(err, authDB.Close())
}()
signer, err := c.CA.Load()
if err != nil {
return err
}
srv := &CertificateSigner{
Logger: zap.L(),
Signer: signer,
AuthDB: authDB,
MinDifficulty: uint16(c.MinDifficulty),
}
pb.RegisterCertificatesServer(server.GRPC(), srv) pb.RegisterCertificatesServer(server.GRPC(), srv)
return server.Run(ctx) return server.Run(ctx)
@ -194,10 +276,42 @@ func (c CertSignerConfig) Run(ctx context.Context, server *provider.Provider) (e
// Sign signs a valid certificate signing request's cert. // Sign signs a valid certificate signing request's cert.
func (c CertificateSigner) Sign(ctx context.Context, req *pb.SigningRequest) (*pb.SigningResponse, error) { func (c CertificateSigner) Sign(ctx context.Context, req *pb.SigningRequest) (*pb.SigningResponse, error) {
// lookup authtoken grpcPeer, ok := peer.FromContext(ctx)
// sign cert if !ok {
// send response // TODO: better error
return &pb.SigningResponse{}, nil return nil, errs.New("unable to get peer from context")
}
peerIdent, err := identity.PeerIdentityFromPeer(grpcPeer)
if err != nil {
return nil, err
}
signedPeerCA, err := c.Signer.Sign(peerIdent.CA)
if err != nil {
return nil, err
}
signedChainBytes := append(
[][]byte{
signedPeerCA.Raw,
c.Signer.Cert.Raw,
},
c.Signer.RestChainRaw()...,
)
err = c.AuthDB.Claim(&ClaimOpts{
Req: req,
Peer: grpcPeer,
ChainBytes: signedChainBytes,
MinDifficulty: c.MinDifficulty,
})
if err != nil {
return nil, err
}
return &pb.SigningResponse{
Chain: signedChainBytes,
}, nil
} }
// Close closes the authorization database's underlying store. // Close closes the authorization database's underlying store.
@ -214,11 +328,6 @@ func (a *AuthorizationDB) Create(userID string, count int) (Authorizations, erro
return nil, ErrAuthorizationCount return nil, ErrAuthorizationCount
} }
existingAuths, err := a.Get(userID)
if err != nil {
return nil, err
}
var ( var (
newAuths Authorizations newAuths Authorizations
authErrs utils.ErrorGroup authErrs utils.ErrorGroup
@ -235,22 +344,16 @@ func (a *AuthorizationDB) Create(userID string, count int) (Authorizations, erro
return nil, ErrAuthorizationDB.Wrap(err) return nil, ErrAuthorizationDB.Wrap(err)
} }
existingAuths = append(existingAuths, newAuths...) if err := a.add(userID, newAuths); err != nil {
authsBytes, err := existingAuths.Marshal() return nil, err
if err != nil {
return nil, ErrAuthorizationDB.Wrap(err)
}
if err := a.DB.Put(storage.Key(userID), authsBytes); err != nil {
return nil, ErrAuthorizationDB.Wrap(err)
} }
return newAuths, nil return newAuths, nil
} }
// Get retrieves authorizations by email. // Get retrieves authorizations by user ID.
func (a *AuthorizationDB) Get(email string) (Authorizations, error) { func (a *AuthorizationDB) Get(userID string) (Authorizations, error) {
authsBytes, err := a.DB.Get(storage.Key(email)) authsBytes, err := a.DB.Get(storage.Key(userID))
if err != nil && !storage.ErrKeyNotFound.Has(err) { if err != nil && !storage.ErrKeyNotFound.Has(err) {
return nil, ErrAuthorizationDB.Wrap(err) return nil, ErrAuthorizationDB.Wrap(err)
} }
@ -274,6 +377,84 @@ func (a *AuthorizationDB) UserIDs() ([]string, error) {
return keys.Strings(), nil return keys.Strings(), nil
} }
// Claim marks an authorization as claimed and records claim information
func (a *AuthorizationDB) Claim(opts *ClaimOpts) error {
now := time.Now().Unix()
if !(now-MaxClaimDelaySeconds < opts.Req.Timestamp) ||
!(opts.Req.Timestamp < now+MaxClaimDelaySeconds) {
return ErrAuthorization.New("claim timestamp is outside of max delay window: %d", opts.Req.Timestamp)
}
ident, err := identity.PeerIdentityFromPeer(opts.Peer)
if err != nil {
return err
}
peerDifficulty, err := ident.ID.Difficulty()
if err != nil {
return err
}
if peerDifficulty < opts.MinDifficulty {
return ErrAuthorization.New("difficulty must be greater than: %d", opts.MinDifficulty)
}
token, err := ParseToken(opts.Req.AuthToken)
if err != nil {
return err
}
auths, err := a.Get(token.UserID)
if err != nil {
return err
}
for i, auth := range auths {
if auth.Token.Equal(token) {
if auth.Claim != nil {
return ErrAuthorization.New("authorization has already been claimed: %s", auth.String())
}
auths[i] = &Authorization{
Token: auth.Token,
Claim: &Claim{
Timestamp: now,
Addr: opts.Peer.Addr.String(),
Identity: ident,
SignedChainBytes: opts.ChainBytes,
},
}
if err := a.put(token.UserID, auths); err != nil {
return err
}
break
}
}
return nil
}
func (a *AuthorizationDB) add(userID string, newAuths Authorizations) error {
auths, err := a.Get(userID)
if err != nil {
return err
}
auths = append(auths, newAuths...)
return a.put(userID, auths)
}
func (a *AuthorizationDB) put(userID string, auths Authorizations) error {
authsBytes, err := auths.Marshal()
if err != nil {
return ErrAuthorizationDB.Wrap(err)
}
if err := a.DB.Put(storage.Key(userID), authsBytes); err != nil {
return ErrAuthorizationDB.Wrap(err)
}
return nil
}
// Unmarshal deserializes a set of authorizations // Unmarshal deserializes a set of authorizations
func (a *Authorizations) Unmarshal(data []byte) error { func (a *Authorizations) Unmarshal(data []byte) error {
decoder := gob.NewDecoder(bytes.NewBuffer(data)) decoder := gob.NewDecoder(bytes.NewBuffer(data))
@ -309,9 +490,15 @@ func (a Authorizations) Group() (claimed, open Authorizations) {
} }
// String implements the stringer interface and prevents authorization data // String implements the stringer interface and prevents authorization data
// from completely leaking into logs. // from completely leaking into logs and errors.
func (a Authorization) String() string { func (a Authorization) String() string {
return fmt.Sprintf("%.5s..", a.Token.String()) fmtLen := strconv.Itoa(len(a.Token.UserID) + 7)
return fmt.Sprintf("%."+fmtLen+"s..", a.Token.String())
}
// Equal checks if two tokens have equal user IDs and data
func (t *Token) Equal(cmpToken *Token) bool {
return t.UserID == cmpToken.UserID && bytes.Equal(t.Data[:], cmpToken.Data[:])
} }
// String implements the stringer interface. Base68 w/ version and checksum bytes // String implements the stringer interface. Base68 w/ version and checksum bytes

View File

@ -5,22 +5,36 @@ package certificates
import ( import (
"bytes" "bytes"
"crypto/tls"
"crypto/x509"
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"net"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"time" "time"
"github.com/btcsuite/btcutil/base58" "github.com/btcsuite/btcutil/base58"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeebo/errs" "github.com/zeebo/errs"
"go.uber.org/zap"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/internal/testplanet"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/provider"
"storj.io/storj/pkg/transport"
"storj.io/storj/pkg/utils" "storj.io/storj/pkg/utils"
"storj.io/storj/storage" "storj.io/storj/storage"
) )
var ( var (
idents = testplanet.NewPregeneratedIdentities()
t1 = Token{ t1 = Token{
UserID: "user@example.com", UserID: "user@example.com",
Data: [tokenDataLength]byte{1, 2, 3}, Data: [tokenDataLength]byte{1, 2, 3},
@ -33,14 +47,12 @@ var (
func TestCertSignerConfig_NewAuthDB(t *testing.T) { func TestCertSignerConfig_NewAuthDB(t *testing.T) {
ctx := testcontext.New(t) ctx := testcontext.New(t)
defer ctx.Cleanup()
authDB, err := newTestAuthDB(ctx) authDB, err := newTestAuthDB(ctx)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer ctx.Check(authDB.Close)
_ = authDB.Close()
ctx.Cleanup()
}()
assert.NotNil(t, authDB) assert.NotNil(t, authDB)
assert.NotNil(t, authDB.DB) assert.NotNil(t, authDB.DB)
@ -48,14 +60,12 @@ func TestCertSignerConfig_NewAuthDB(t *testing.T) {
func TestAuthorizationDB_Create(t *testing.T) { func TestAuthorizationDB_Create(t *testing.T) {
ctx := testcontext.New(t) ctx := testcontext.New(t)
defer ctx.Cleanup()
authDB, err := newTestAuthDB(ctx) authDB, err := newTestAuthDB(ctx)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer ctx.Check(authDB.Close)
_ = authDB.Close()
ctx.Cleanup()
}()
cases := []struct { cases := []struct {
testID, testID,
@ -144,7 +154,8 @@ func TestAuthorizationDB_Get(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
_ = authDB.Close() err := authDB.Close()
assert.NoError(t, err)
ctx.Cleanup() ctx.Cleanup()
}() }()
@ -194,6 +205,225 @@ func TestAuthorizationDB_Get(t *testing.T) {
} }
} }
func TestAuthorizationDB_Claim_Valid(t *testing.T) {
ctx := testcontext.New(t)
userID := "user@example.com"
authDB, err := newTestAuthDB(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, authDB) {
t.Fatal(err)
}
defer func() {
err := authDB.Close()
assert.NoError(t, err)
ctx.Cleanup()
}()
auths, err := authDB.Create(userID, 1)
if !assert.NoError(t, err) || !assert.NotEmpty(t, auths) {
t.Fatal(err)
}
ident, err := testidentity.NewTestIdentity(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, ident) {
t.Fatal(err)
}
addr := &net.TCPAddr{
IP: net.ParseIP("1.2.3.4"),
Port: 5,
}
grpcPeer := &peer.Peer{
Addr: addr,
AuthInfo: credentials.TLSInfo{
State: tls.ConnectionState{
PeerCertificates: []*x509.Certificate{ident.Leaf, ident.CA},
},
},
}
now := time.Now().Unix()
req := &pb.SigningRequest{
AuthToken: auths[0].Token.String(),
Timestamp: now,
}
difficulty, err := ident.ID.Difficulty()
if !assert.NoError(t, err) {
t.Fatal(err)
}
err = authDB.Claim(&ClaimOpts{
Req: req,
Peer: grpcPeer,
ChainBytes: [][]byte{ident.CA.Raw},
MinDifficulty: difficulty,
})
if !assert.NoError(t, err) {
t.Fatal(err)
}
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) || !assert.NotEmpty(t, updatedAuths) {
t.Fatal(err)
}
assert.Equal(t, auths[0].Token, updatedAuths[0].Token)
if !assert.NotNil(t, updatedAuths[0].Claim) {
t.FailNow()
}
claim := updatedAuths[0].Claim
assert.Equal(t, grpcPeer.Addr.String(), claim.Addr)
assert.Equal(t, [][]byte{ident.CA.Raw}, claim.SignedChainBytes)
assert.Condition(t, func() bool {
return now-MaxClaimDelaySeconds < claim.Timestamp &&
claim.Timestamp < now+MaxClaimDelaySeconds
})
}
func TestAuthorizationDB_Claim_Invalid(t *testing.T) {
ctx := testcontext.New(t)
userID := "user@example.com"
claimedTime := int64(1000000)
claimedAddr := "6.7.8.9:0"
ident1, err := testidentity.NewTestIdentity(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, ident1) {
t.Fatal(err)
}
claimedIdent := &provider.PeerIdentity{
CA: ident1.CA,
Leaf: ident1.Leaf,
}
authDB, err := newTestAuthDB(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, authDB) {
t.Fatal(err)
}
defer func() {
err := authDB.Close()
assert.NoError(t, err)
ctx.Cleanup()
}()
auths, err := authDB.Create(userID, 2)
if !assert.NoError(t, err) || !assert.NotEmpty(t, auths) {
t.Fatal(err)
}
claimedIndex, unclaimedIndex := 0, 1
auths[claimedIndex].Claim = &Claim{
Timestamp: claimedTime,
Addr: claimedAddr,
Identity: claimedIdent,
SignedChainBytes: [][]byte{claimedIdent.CA.Raw},
}
err = authDB.put(userID, auths)
if !assert.NoError(t, err) {
t.Fatal(err)
}
ident2, err := testidentity.NewTestIdentity(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, ident2) {
t.Fatal(err)
}
addr := &net.TCPAddr{
IP: net.ParseIP("1.2.3.4"),
Port: 5,
}
grpcPeer := &peer.Peer{
Addr: addr,
AuthInfo: credentials.TLSInfo{
State: tls.ConnectionState{
PeerCertificates: []*x509.Certificate{ident2.Leaf, ident2.CA},
},
},
}
difficulty2, err := ident2.ID.Difficulty()
if !assert.NoError(t, err) {
t.Fatal(err)
}
t.Run("double claim", func(t *testing.T) {
err = authDB.Claim(&ClaimOpts{
Req: &pb.SigningRequest{
AuthToken: auths[claimedIndex].Token.String(),
Timestamp: time.Now().Unix(),
},
Peer: grpcPeer,
ChainBytes: [][]byte{ident2.CA.Raw},
MinDifficulty: difficulty2,
})
if assert.Error(t, err) {
assert.True(t, ErrAuthorization.Has(err))
// NB: token string shouldn't leak into error message
assert.NotContains(t, err.Error(), auths[claimedIndex].Token.String())
}
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) || !assert.NotEmpty(t, updatedAuths) {
t.Fatal(err)
}
assert.Equal(t, auths[claimedIndex].Token, updatedAuths[claimedIndex].Token)
claim := updatedAuths[claimedIndex].Claim
assert.Equal(t, claimedAddr, claim.Addr)
assert.Equal(t, [][]byte{ident1.CA.Raw}, claim.SignedChainBytes)
assert.Equal(t, claimedTime, claim.Timestamp)
})
t.Run("invalid timestamp", func(t *testing.T) {
err = authDB.Claim(&ClaimOpts{
Req: &pb.SigningRequest{
AuthToken: auths[unclaimedIndex].Token.String(),
// NB: 1 day ago
Timestamp: time.Now().Unix() - 86400,
},
Peer: grpcPeer,
ChainBytes: [][]byte{ident2.CA.Raw},
MinDifficulty: difficulty2,
})
if assert.Error(t, err) {
assert.True(t, ErrAuthorization.Has(err))
// NB: token string shouldn't leak into error message
assert.NotContains(t, err.Error(), auths[unclaimedIndex].Token.String())
}
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) || !assert.NotEmpty(t, updatedAuths) {
t.Fatal(err)
}
assert.Equal(t, auths[unclaimedIndex].Token, updatedAuths[unclaimedIndex].Token)
assert.Nil(t, updatedAuths[unclaimedIndex].Claim)
})
t.Run("invalid difficulty", func(t *testing.T) {
err = authDB.Claim(&ClaimOpts{
Req: &pb.SigningRequest{
AuthToken: auths[unclaimedIndex].Token.String(),
Timestamp: time.Now().Unix(),
},
Peer: grpcPeer,
ChainBytes: [][]byte{ident2.CA.Raw},
MinDifficulty: difficulty2 + 1,
})
if assert.Error(t, err) {
assert.True(t, ErrAuthorization.Has(err))
// NB: token string shouldn't leak into error message
assert.NotContains(t, err.Error(), auths[unclaimedIndex].Token.String())
}
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) || !assert.NotEmpty(t, updatedAuths) {
t.Fatal(err)
}
assert.Equal(t, auths[unclaimedIndex].Token, updatedAuths[unclaimedIndex].Token)
assert.Nil(t, updatedAuths[unclaimedIndex].Claim)
})
}
func TestNewAuthorization(t *testing.T) { func TestNewAuthorization(t *testing.T) {
userID := "user@example.com" userID := "user@example.com"
auth, err := NewAuthorization(userID) auth, err := NewAuthorization(userID)
@ -274,7 +504,8 @@ func TestAuthorizationDB_Emails(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
_ = authDB.Close() err = authDB.Close()
assert.NoError(t, err)
ctx.Cleanup() ctx.Cleanup()
}() }()
@ -368,6 +599,322 @@ func TestParseToken_Invalid(t *testing.T) {
} }
} }
func TestToken_Equal(t *testing.T) {
assert.True(t, t1.Equal(&t1))
assert.False(t, t1.Equal(&t2))
}
// TODO: test sad path
func TestCertificateSigner_Sign_E2E(t *testing.T) {
ctx := testcontext.New(t)
tmp := ctx.Dir()
defer ctx.Cleanup()
caCert := filepath.Join(tmp, "ca.cert")
caKey := filepath.Join(tmp, "ca.key")
userID := "user@example.com"
caSetupConfig := provider.CASetupConfig{
CertPath: caCert,
KeyPath: caKey,
}
caConfig := provider.FullCAConfig{
CertPath: caCert,
KeyPath: caKey,
}
config := CertSignerConfig{
AuthorizationDBURL: "bolt://" + filepath.Join(tmp, "authorizations.db"),
CA: caConfig,
}
signingCA, err := caSetupConfig.Create(ctx)
if !assert.NoError(t, err) {
t.Fatal(err)
}
authDB, err := config.NewAuthDB()
if !assert.NoError(t, err) || !assert.NotNil(t, authDB) {
t.Fatal(err)
}
auths, err := authDB.Create("user@example.com", 1)
if !assert.NoError(t, err) || !assert.NotEmpty(t, auths) {
t.Fatal(err)
}
err = authDB.Close()
if !assert.NoError(t, err) {
t.Fatal(err)
}
// TODO(bryanchriswhite): figure out why pregenerated
// identities change issuers when signed
//
// Issuer: {
// Names: null => [],
// Organization: null => [],
// RawIssue": "MAA=" => "MBAxDjAMBgNVBAoTBVN0b3Jq",
//------
//serverIdent, err := idents.NewIdentity()
//------
serverCA, err := testidentity.NewTestCA(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, serverCA) {
t.Fatal(err)
}
serverIdent, err := serverCA.NewIdentity()
//------
if !assert.NoError(t, err) || !assert.NotNil(t, serverIdent) {
t.Fatal(err)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
if !assert.NoError(t, err) || !assert.NotNil(t, listener) {
t.Fatal(err)
}
serverConfig := provider.ServerConfig{Address: listener.Addr().String()}
opts, err := provider.NewServerOptions(serverIdent, serverConfig)
if !assert.NoError(t, err) || !assert.NotNil(t, opts) {
t.Fatal(err)
}
service, err := provider.NewProvider(opts, listener, nil, config)
if !assert.NoError(t, err) || !assert.NotNil(t, service) {
t.Fatal(err)
}
ctx.Go(func() error {
err := service.Run(ctx)
assert.NoError(t, err)
return err
})
defer func() {
err := service.Close()
assert.NoError(t, err)
}()
// TODO(bryanchriswhite): figure out why pregenerated
// identities change issuers when signed
//
// Issuer: {
// Names: null => [],
// Organization: null => [],
// RawIssue": "MAA=" => "MBAxDjAMBgNVBAoTBVN0b3Jq",
//------
//clientIdent, err := idents.NewIdentity()
//------
clientCA, err := testidentity.NewTestCA(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, clientCA) {
t.Fatal(err)
}
clientIdent, err := clientCA.NewIdentity()
//------
if !assert.NoError(t, err) || !assert.NotNil(t, clientIdent) {
t.Fatal(err)
}
client, err := NewClient(ctx, clientIdent, listener.Addr().String())
if !assert.NoError(t, err) || !assert.NotNil(t, client) {
t.Fatal(err)
}
signedChainBytes, err := client.Sign(ctx, auths[0].Token.String())
if !assert.NoError(t, err) || !assert.NotEmpty(t, signedChainBytes) {
t.Fatal(err)
}
signedChain, err := identity.ParseCertChain(signedChainBytes)
if !assert.NoError(t, err) {
t.Fatal(err)
}
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)
assert.Equal(t, signingCA.Cert.Raw, signedChainBytes[1])
// TODO: test scenario with rest chain
//assert.Equal(t, signingCA.RestChainRaw(), signedChainBytes[1:])
err = signedChain[0].CheckSignatureFrom(signingCA.Cert)
assert.NoError(t, err)
err = service.Close()
assert.NoError(t, err)
// NB: re-open after closing for server
authDB, err = config.NewAuthDB()
if !assert.NoError(t, err) || !assert.NotNil(t, authDB) {
t.Fatal(err)
}
defer func() {
err := authDB.Close()
assert.NoError(t, err)
}()
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) ||
!assert.NotEmpty(t, updatedAuths) ||
!assert.NotNil(t, updatedAuths[0].Claim) {
t.Fatal(err)
}
now := time.Now().Unix()
claim := updatedAuths[0].Claim
assert.Equal(t,
strings.Split(listener.Addr().String(), ":")[0],
strings.Split(claim.Addr, ":")[0])
assert.Equal(t, signedChainBytes, claim.SignedChainBytes)
assert.Condition(t, func() bool {
return now-10 < claim.Timestamp &&
claim.Timestamp < now+10
})
}
func TestNewClient(t *testing.T) {
ctx := testcontext.New(t)
ident, err := idents.NewIdentity()
if !assert.NoError(t, err) || !assert.NotNil(t, ident) {
t.Fatal(err)
}
client, err := NewClient(ctx, ident, "")
assert.NoError(t, err)
assert.NotNil(t, client)
}
func TestNewClientFrom(t *testing.T) {
ctx := testcontext.New(t)
ident, err := idents.NewIdentity()
if !assert.NoError(t, err) || !assert.NotNil(t, ident) {
t.Fatal(err)
}
listener, err := net.Listen("tcp", "127.0.0.1:0")
if !assert.NoError(t, err) || !assert.NotNil(t, listener) {
t.Fatal(err)
}
tc := transport.NewClient(ident)
conn, err := tc.DialAddress(ctx, listener.Addr().String())
if !assert.NoError(t, err) || !assert.NotNil(t, conn) {
t.Fatal(err)
}
pbClient := pb.NewCertificatesClient(conn)
if !assert.NotNil(t, pbClient) {
t.FailNow()
}
client, err := NewClientFrom(pbClient)
assert.NoError(t, err)
assert.NotNil(t, client)
}
func TestCertificateSigner_Sign(t *testing.T) {
ctx := testcontext.New(t)
tmp := ctx.Dir()
defer ctx.Cleanup()
caCert := filepath.Join(tmp, "ca.cert")
caKey := filepath.Join(tmp, "ca.key")
userID := "user@example.com"
caSetupConfig := provider.CASetupConfig{
CertPath: caCert,
KeyPath: caKey,
}
config := CertSignerConfig{
AuthorizationDBURL: "bolt://" + filepath.Join(tmp, "authorizations.db"),
}
signingCA, err := caSetupConfig.Create(ctx)
if !assert.NoError(t, err) {
t.Fatal(err)
}
authDB, err := config.NewAuthDB()
if !assert.NoError(t, err) || !assert.NotNil(t, authDB) {
t.Fatal(err)
}
defer func() {
err := authDB.Close()
assert.NoError(t, err)
}()
auths, err := authDB.Create(userID, 1)
if !assert.NoError(t, err) || !assert.NotEmpty(t, auths) {
t.Fatal(err)
}
// TODO(bryanchriswhite): figure out why pregenerated
// identities change issuers when signed
//
// Issuer: {
// Names: null => [],
// Organization: null => [],
// RawIssue": "MAA=" => "MBAxDjAMBgNVBAoTBVN0b3Jq",
//------
//clientIdent, err := idents.NewIdentity()
//------
clientCA, err := testidentity.NewTestCA(ctx)
if !assert.NoError(t, err) || !assert.NotNil(t, clientCA) {
t.Fatal(err)
}
clientIdent, err := clientCA.NewIdentity()
//------
if !assert.NoError(t, err) || !assert.NotNil(t, clientIdent) {
t.Fatal(err)
}
expectedAddr := &net.TCPAddr{
IP: net.ParseIP("1.2.3.4"),
Port: 5,
}
grpcPeer := &peer.Peer{
Addr: expectedAddr,
AuthInfo: credentials.TLSInfo{
State: tls.ConnectionState{
PeerCertificates: []*x509.Certificate{clientIdent.Leaf, clientIdent.CA},
},
},
}
peerCtx := peer.NewContext(ctx, grpcPeer)
certSigner := &CertificateSigner{
Logger: zap.L(),
Signer: signingCA,
AuthDB: authDB,
}
req := pb.SigningRequest{
Timestamp: time.Now().Unix(),
AuthToken: auths[0].Token.String(),
}
res, err := certSigner.Sign(peerCtx, &req)
if !assert.NoError(t, err) || !assert.NotNil(t, res) || !assert.NotEmpty(t, res.Chain) {
t.Fatal(err)
}
signedChain, err := identity.ParseCertChain(res.Chain)
if !assert.NoError(t, err) {
t.Fatal(err)
}
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)
assert.Equal(t, signingCA.Cert.Raw, signedChain[1].Raw)
// TODO: test scenario with rest chain
//assert.Equal(t, signingCA.RestChainRaw(), res.Chain[1:])
err = signedChain[0].CheckSignatureFrom(signingCA.Cert)
assert.NoError(t, err)
updatedAuths, err := authDB.Get(userID)
if !assert.NoError(t, err) ||
!assert.NotEmpty(t, updatedAuths) ||
!assert.NotNil(t, updatedAuths[0].Claim) {
t.Fatal(err)
}
now := time.Now().Unix()
claim := updatedAuths[0].Claim
assert.Equal(t, expectedAddr.String(), claim.Addr)
assert.Equal(t, res.Chain, claim.SignedChainBytes)
assert.Condition(t, func() bool {
return now-MaxClaimDelaySeconds < claim.Timestamp &&
claim.Timestamp < now+MaxClaimDelaySeconds
})
}
func newTestAuthDB(ctx *testcontext.Context) (*AuthorizationDB, error) { func newTestAuthDB(ctx *testcontext.Context) (*AuthorizationDB, error) {
dbPath := "bolt://" + filepath.Join(ctx.Dir(), "authorizations.db") dbPath := "bolt://" + filepath.Join(ctx.Dir(), "authorizations.db")
config := CertSignerConfig{ config := CertSignerConfig{

View File

@ -9,6 +9,8 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"storj.io/storj/internal/testcontext"
) )
func TestNewCA(t *testing.T) { func TestNewCA(t *testing.T) {
@ -27,21 +29,19 @@ func TestNewCA(t *testing.T) {
} }
func TestFullCertificateAuthority_NewIdentity(t *testing.T) { func TestFullCertificateAuthority_NewIdentity(t *testing.T) {
ca, err := NewCA(context.Background(), NewCAOptions{ ctx := testcontext.New(t)
ca, err := NewCA(ctx, NewCAOptions{
Difficulty: 12, Difficulty: 12,
Concurrency: 4, Concurrency: 4,
}) })
if !assert.NoError(t, err) || !assert.NotNil(t, ca) {
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.NotEmpty(t, ca)
fi, err := ca.NewIdentity() fi, err := ca.NewIdentity()
if err != nil { if !assert.NoError(t, err) || !assert.NotNil(t, fi) {
t.Fatal(err) t.Fatal(err)
} }
assert.NotEmpty(t, fi)
assert.Equal(t, ca.Cert, fi.CA) assert.Equal(t, ca.Cert, fi.CA)
assert.Equal(t, ca.ID, fi.ID) assert.Equal(t, ca.ID, fi.ID)
@ -52,6 +52,36 @@ func TestFullCertificateAuthority_NewIdentity(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestFullCertificateAuthority_Sign(t *testing.T) {
ctx := testcontext.New(t)
caOpts := NewCAOptions{
Difficulty: 12,
Concurrency: 4,
}
ca, err := NewCA(ctx, caOpts)
if !assert.NoError(t, err) || !assert.NotNil(t, ca) {
t.Fatal(err)
}
toSign, err := NewCA(ctx, caOpts)
if !assert.NoError(t, err) || !assert.NotNil(t, toSign) {
t.Fatal(err)
}
signed, err := ca.Sign(toSign.Cert)
if !assert.NoError(t, err) || !assert.NotNil(t, signed) {
t.Fatal(err)
}
assert.Equal(t, toSign.Cert.RawTBSCertificate, signed.RawTBSCertificate)
assert.NotEqual(t, toSign.Cert.Signature, signed.Signature)
assert.NotEqual(t, toSign.Cert.Raw, signed.Raw)
err = signed.CheckSignatureFrom(ca.Cert)
assert.NoError(t, err)
}
func TestFullCAConfig_Save(t *testing.T) { func TestFullCAConfig_Save(t *testing.T) {
// TODO(bryanchriswhite): test with both // TODO(bryanchriswhite): test with both
// TODO(bryanchriswhite): test with only cert path // TODO(bryanchriswhite): test with only cert path

View File

@ -8,6 +8,7 @@ import (
"context" "context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"io/ioutil" "io/ioutil"
@ -75,6 +76,38 @@ type FullCAConfig struct {
KeyPath string `help:"path to the private key for this identity" default:"$CONFDIR/ca.key"` KeyPath string `help:"path to the private key for this identity" default:"$CONFDIR/ca.key"`
} }
// NewCA creates a new full identity with the given difficulty
func NewCA(ctx context.Context, opts NewCAOptions) (_ *FullCertificateAuthority, err error) {
defer mon.Task()(&ctx)(&err)
var (
highscore uint32
)
if opts.Concurrency < 1 {
opts.Concurrency = 1
}
ctx, cancel := context.WithCancel(ctx)
log.Printf("Generating a certificate matching a difficulty of %d\n", opts.Difficulty)
eC := make(chan error)
caC := make(chan FullCertificateAuthority, 1)
for i := 0; i < int(opts.Concurrency); i++ {
go newCAWorker(ctx, i, &highscore, opts.Difficulty, opts.ParentCert, opts.ParentKey, caC, eC)
}
select {
case ca := <-caC:
cancel()
return &ca, nil
case err := <-eC:
cancel()
return nil, err
case <-ctx.Done():
cancel()
return nil, ctx.Err()
}
}
// Status returns the status of the CA cert/key files for the config // Status returns the status of the CA cert/key files for the config
func (caS CASetupConfig) Status() TLSFilesStatus { func (caS CASetupConfig) Status() TLSFilesStatus {
return statTLSFiles(caS.CertPath, caS.KeyPath) return statTLSFiles(caS.CertPath, caS.KeyPath)
@ -175,39 +208,6 @@ func (pc PeerCAConfig) Load() (*PeerCertificateAuthority, error) {
}, nil }, nil
} }
// NewCA creates a new full identity with the given difficulty
func NewCA(ctx context.Context, opts NewCAOptions) (
rv *FullCertificateAuthority, err error) {
defer mon.Task()(&ctx)(&err)
var (
highscore uint32
)
if opts.Concurrency < 1 {
opts.Concurrency = 1
}
ctx, cancel := context.WithCancel(ctx)
log.Printf("Generating a certificate matching a difficulty of %d\n", opts.Difficulty)
eC := make(chan error)
caC := make(chan FullCertificateAuthority, 1)
for i := 0; i < int(opts.Concurrency); i++ {
go newCAWorker(ctx, i, &highscore, opts.Difficulty, opts.ParentCert, opts.ParentKey, caC, eC)
}
select {
case ca := <-caC:
cancel()
return &ca, nil
case err := <-eC:
cancel()
return nil, err
case <-ctx.Done():
cancel()
return nil, ctx.Err()
}
}
// Save saves a CA with the given configuration // Save saves a CA with the given configuration
func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error { func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
var ( var (
@ -246,7 +246,7 @@ func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
// NewIdentity generates a new `FullIdentity` based on the CA. The CA // NewIdentity generates a new `FullIdentity` based on the CA. The CA
// cert is included in the identity's cert chain and the identity's leaf cert // cert is included in the identity's cert chain and the identity's leaf cert
// is signed by the CA. // is signed by the CA.
func (ca FullCertificateAuthority) NewIdentity() (*FullIdentity, error) { func (ca *FullCertificateAuthority) NewIdentity() (*FullIdentity, error) {
leafTemplate, err := peertls.LeafTemplate() leafTemplate, err := peertls.LeafTemplate()
if err != nil { if err != nil {
return nil, err return nil, err
@ -278,4 +278,29 @@ func (ca FullCertificateAuthority) NewIdentity() (*FullIdentity, error) {
Key: leafKey, Key: leafKey,
ID: ca.ID, ID: ca.ID,
}, nil }, nil
}
// RestChainRaw returns the rest (excluding leaf and CA) of the certificate chain as a 2d byte slice
func (ca *FullCertificateAuthority) RestChainRaw() [][]byte {
var chain [][]byte
for _, cert := range ca.RestChain {
chain = append(chain, cert.Raw)
}
return chain
}
// Sign signs the passed certificate with ca certificate
func (ca *FullCertificateAuthority) Sign(cert *x509.Certificate) (*x509.Certificate, error) {
signedCertBytes, err := x509.CreateCertificate(rand.Reader, cert, ca.Cert, cert.PublicKey, ca.Key)
if err != nil {
return nil, errs.Wrap(err)
}
signedCert, err := x509.ParseCertificate(signedCertBytes)
if err != nil {
return nil, errs.Wrap(err)
}
return signedCert, nil
} }

View File

@ -131,7 +131,7 @@ func PeerIdentityFromPeer(peer *peer.Peer) (*PeerIdentity, error) {
if len(c) < 2 { if len(c) < 2 {
return nil, Error.New("invalid certificate chain") return nil, Error.New("invalid certificate chain")
} }
pi, err := PeerIdentityFromCerts(c[0], c[1], c[2:]) pi, err := PeerIdentityFromCerts(c[peertls.LeafIndex], c[peertls.CAIndex], c[2:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -244,7 +244,7 @@ func (ic Config) Save(fi *FullIdentity) error {
) )
} }
// RestChainRaw returns the rest (excluding leaf and CA) of the certficate chain as a 2d byte slice // RestChainRaw returns the rest (excluding leaf and CA) of the certificate chain as a 2d byte slice
func (fi *FullIdentity) RestChainRaw() [][]byte { func (fi *FullIdentity) RestChainRaw() [][]byte {
var chain [][]byte var chain [][]byte
for _, cert := range fi.RestChain { for _, cert := range fi.RestChain {

View File

@ -21,9 +21,10 @@ import (
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"google.golang.org/grpc" "google.golang.org/grpc"
testidentity "storj.io/storj/internal/identity"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/internal/teststorj" "storj.io/storj/internal/teststorj"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/node" "storj.io/storj/pkg/node"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"
"storj.io/storj/pkg/provider" "storj.io/storj/pkg/provider"
@ -41,14 +42,14 @@ func TestNewKademlia(t *testing.T) {
rootdir, cleanup := mktempdir(t, "kademlia") rootdir, cleanup := mktempdir(t, "kademlia")
defer cleanup() defer cleanup()
cases := []struct { cases := []struct {
id *provider.FullIdentity id *identity.FullIdentity
bn []pb.Node bn []pb.Node
addr string addr string
expectedErr error expectedErr error
}{ }{
{ {
id: func() *provider.FullIdentity { id: func() *identity.FullIdentity {
id, err := testidentity.NewTestIdentity() id, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
return id return id
}(), }(),
@ -57,7 +58,7 @@ func TestNewKademlia(t *testing.T) {
}, },
{ {
id: func() *provider.FullIdentity { id: func() *provider.FullIdentity {
id, err := testidentity.NewTestIdentity() id, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
return id return id
}(), }(),
@ -163,12 +164,13 @@ func TestBootstrap(t *testing.T) {
} }
func testNode(t *testing.T, bn []pb.Node) (*Kademlia, *grpc.Server, func()) { func testNode(t *testing.T, bn []pb.Node) (*Kademlia, *grpc.Server, func()) {
ctx := testcontext.New(t)
// new address // new address
lis, err := net.Listen("tcp", "127.0.0.1:0") lis, err := net.Listen("tcp", "127.0.0.1:0")
assert.NoError(t, err) assert.NoError(t, err)
// new config // new config
// new identity // new identity
fid, err := testidentity.NewTestIdentity() fid, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
// new kademlia // new kademlia
dir, cleanup := mktempdir(t, "kademlia") dir, cleanup := mktempdir(t, "kademlia")
@ -238,9 +240,9 @@ func TestGetNodes(t *testing.T) {
defer srv.Stop() defer srv.Stop()
// make new identity // make new identity
fid, err := testidentity.NewTestIdentity() fid, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
fid2, err := testidentity.NewTestIdentity() fid2, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
fid.ID = nodeIDA fid.ID = nodeIDA
fid2.ID = nodeIDB fid2.ID = nodeIDB

View File

@ -17,9 +17,9 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
testidentity "storj.io/storj/internal/identity"
"storj.io/storj/internal/s3client" "storj.io/storj/internal/s3client"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/internal/testplanet" "storj.io/storj/internal/testplanet"
"storj.io/storj/pkg/cfgstruct" "storj.io/storj/pkg/cfgstruct"
"storj.io/storj/pkg/miniogw" "storj.io/storj/pkg/miniogw"

View File

@ -9,8 +9,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
testidentity "storj.io/storj/internal/identity"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/internal/testplanet" "storj.io/storj/internal/testplanet"
"storj.io/storj/pkg/overlay" "storj.io/storj/pkg/overlay"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"

View File

@ -36,7 +36,7 @@ func (m *SigningRequest) Reset() { *m = SigningRequest{} }
func (m *SigningRequest) String() string { return proto.CompactTextString(m) } func (m *SigningRequest) String() string { return proto.CompactTextString(m) }
func (*SigningRequest) ProtoMessage() {} func (*SigningRequest) ProtoMessage() {}
func (*SigningRequest) Descriptor() ([]byte, []int) { func (*SigningRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_certificate_80970fe789528feb, []int{0} return fileDescriptor_certificate_c55bd879e75eb964, []int{0}
} }
func (m *SigningRequest) XXX_Unmarshal(b []byte) error { func (m *SigningRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SigningRequest.Unmarshal(m, b) return xxx_messageInfo_SigningRequest.Unmarshal(m, b)
@ -71,7 +71,7 @@ func (m *SigningRequest) GetTimestamp() int64 {
} }
type SigningResponse struct { type SigningResponse struct {
Cert []byte `protobuf:"bytes,1,opt,name=cert,proto3" json:"cert,omitempty"` Chain [][]byte `protobuf:"bytes,1,rep,name=chain" json:"chain,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -81,7 +81,7 @@ func (m *SigningResponse) Reset() { *m = SigningResponse{} }
func (m *SigningResponse) String() string { return proto.CompactTextString(m) } func (m *SigningResponse) String() string { return proto.CompactTextString(m) }
func (*SigningResponse) ProtoMessage() {} func (*SigningResponse) ProtoMessage() {}
func (*SigningResponse) Descriptor() ([]byte, []int) { func (*SigningResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_certificate_80970fe789528feb, []int{1} return fileDescriptor_certificate_c55bd879e75eb964, []int{1}
} }
func (m *SigningResponse) XXX_Unmarshal(b []byte) error { func (m *SigningResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SigningResponse.Unmarshal(m, b) return xxx_messageInfo_SigningResponse.Unmarshal(m, b)
@ -101,9 +101,9 @@ func (m *SigningResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_SigningResponse proto.InternalMessageInfo var xxx_messageInfo_SigningResponse proto.InternalMessageInfo
func (m *SigningResponse) GetCert() []byte { func (m *SigningResponse) GetChain() [][]byte {
if m != nil { if m != nil {
return m.Cert return m.Chain
} }
return nil return nil
} }
@ -185,20 +185,20 @@ var _Certificates_serviceDesc = grpc.ServiceDesc{
Metadata: "certificate.proto", Metadata: "certificate.proto",
} }
func init() { proto.RegisterFile("certificate.proto", fileDescriptor_certificate_80970fe789528feb) } func init() { proto.RegisterFile("certificate.proto", fileDescriptor_certificate_c55bd879e75eb964) }
var fileDescriptor_certificate_80970fe789528feb = []byte{ var fileDescriptor_certificate_c55bd879e75eb964 = []byte{
// 188 bytes of a gzipped FileDescriptorProto // 192 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x4e, 0x2d, 0x2a, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x4e, 0x2d, 0x2a,
0xc9, 0x4c, 0xcb, 0x4c, 0x4e, 0x2c, 0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0xc9, 0x4c, 0xcb, 0x4c, 0x4e, 0x2c, 0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9,
0xcb, 0x4f, 0x49, 0x95, 0xe2, 0x4a, 0xcf, 0x4f, 0xcf, 0x87, 0x88, 0x28, 0xf9, 0x72, 0xf1, 0x05, 0xcb, 0x4f, 0x49, 0x95, 0xe2, 0x4a, 0xcf, 0x4f, 0xcf, 0x87, 0x88, 0x28, 0xf9, 0x72, 0xf1, 0x05,
0x67, 0xa6, 0xe7, 0x65, 0xe6, 0xa5, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xc9, 0x72, 0x67, 0xa6, 0xe7, 0x65, 0xe6, 0xa5, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0xc9, 0x72,
0x71, 0x25, 0x96, 0x96, 0x64, 0xc4, 0x97, 0xe4, 0x67, 0xa7, 0xe6, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x71, 0x25, 0x96, 0x96, 0x64, 0xc4, 0x97, 0xe4, 0x67, 0xa7, 0xe6, 0x49, 0x30, 0x2a, 0x30, 0x6a,
0x70, 0x06, 0x71, 0x82, 0x44, 0x42, 0x40, 0x02, 0x42, 0x32, 0x5c, 0x9c, 0x25, 0x99, 0xb9, 0xa9, 0x70, 0x06, 0x71, 0x82, 0x44, 0x42, 0x40, 0x02, 0x42, 0x32, 0x5c, 0x9c, 0x25, 0x99, 0xb9, 0xa9,
0xc5, 0x25, 0x89, 0xb9, 0x05, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x08, 0x01, 0x25, 0x55, 0xc5, 0x25, 0x89, 0xb9, 0x05, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x08, 0x01, 0x25, 0x75,
0x2e, 0x7e, 0xb8, 0x71, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x42, 0x5c, 0x2c, 0x20, 0x87, 0x2e, 0x7e, 0xb8, 0x71, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x22, 0x5c, 0xac, 0xc9, 0x19,
0x80, 0x4d, 0xe2, 0x09, 0x02, 0xb3, 0x8d, 0x9c, 0xb9, 0x78, 0x9c, 0x11, 0x8e, 0x2b, 0x16, 0x32, 0x89, 0x99, 0x20, 0xa3, 0x98, 0x35, 0x78, 0x82, 0x20, 0x1c, 0x23, 0x67, 0x2e, 0x1e, 0x67, 0x84,
0xe6, 0x62, 0x01, 0x69, 0x13, 0x12, 0xd1, 0x03, 0x39, 0x50, 0x0f, 0xd5, 0x45, 0x52, 0xa2, 0x68, 0xf3, 0x8a, 0x85, 0x8c, 0xb9, 0x58, 0x40, 0x1a, 0x85, 0x44, 0xf4, 0x40, 0x4e, 0xd4, 0x43, 0x75,
0xa2, 0x10, 0x83, 0x9d, 0x58, 0xa2, 0x98, 0x0a, 0x92, 0x92, 0xd8, 0xc0, 0xfe, 0x30, 0x06, 0x04, 0x93, 0x94, 0x28, 0x9a, 0x28, 0xc4, 0x68, 0x27, 0x96, 0x28, 0xa6, 0x82, 0xa4, 0x24, 0x36, 0xb0,
0x00, 0x00, 0xff, 0xff, 0x4b, 0x1b, 0x87, 0x66, 0xee, 0x00, 0x00, 0x00, 0x4f, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3a, 0xd8, 0xc7, 0x87, 0xf0, 0x00, 0x00, 0x00,
} }

View File

@ -18,5 +18,5 @@ message SigningRequest {
} }
message SigningResponse { message SigningResponse {
bytes cert = 1; repeated bytes chain = 1;
} }

View File

@ -72,13 +72,15 @@ type TLSExtConfig struct {
} }
// ExtensionHandlers is a collection of `extensionHandler`s for convenience (see `VerifyFunc`) // ExtensionHandlers is a collection of `extensionHandler`s for convenience (see `VerifyFunc`)
type ExtensionHandlers []extensionHandler type ExtensionHandlers []ExtensionHandler
type extensionVerificationFunc func(pkix.Extension, [][]*x509.Certificate) error type extensionVerificationFunc func(pkix.Extension, [][]*x509.Certificate) error
type extensionHandler struct { // ExtensionHandler represents a verify function for handling an extension
id asn1.ObjectIdentifier // with the given ID
verify extensionVerificationFunc type ExtensionHandler struct {
ID asn1.ObjectIdentifier
Verify extensionVerificationFunc
} }
// ParseExtOptions holds options for calling `ParseExtensions` // ParseExtOptions holds options for calling `ParseExtensions`
@ -107,16 +109,16 @@ type RevocationDB struct {
// to be used in the context of peer certificate verification. // to be used in the context of peer certificate verification.
func ParseExtensions(c TLSExtConfig, opts ParseExtOptions) (handlers ExtensionHandlers) { func ParseExtensions(c TLSExtConfig, opts ParseExtOptions) (handlers ExtensionHandlers) {
if c.WhitelistSignedLeaf { if c.WhitelistSignedLeaf {
handlers = append(handlers, extensionHandler{ handlers = append(handlers, ExtensionHandler{
id: ExtensionIDs[SignedCertExtID], ID: ExtensionIDs[SignedCertExtID],
verify: verifyCAWhitelistSignedLeafFunc(opts.CAWhitelist), Verify: verifyCAWhitelistSignedLeafFunc(opts.CAWhitelist),
}) })
} }
if c.Revocation { if c.Revocation {
handlers = append(handlers, extensionHandler{ handlers = append(handlers, ExtensionHandler{
id: ExtensionIDs[RevocationExtID], ID: ExtensionIDs[RevocationExtID],
verify: func(certExt pkix.Extension, chains [][]*x509.Certificate) error { Verify: func(certExt pkix.Extension, chains [][]*x509.Certificate) error {
if err := opts.RevDB.Put(chains[0], certExt); err != nil { if err := opts.RevDB.Put(chains[0], certExt); err != nil {
return err return err
} }
@ -245,8 +247,8 @@ func (e ExtensionHandlers) VerifyFunc() PeerCertVerificationFunc {
} }
for _, handler := range e { for _, handler := range e {
if ext, ok := leafExts[handler.id.String()]; ok { if ext, ok := leafExts[handler.ID.String()]; ok {
err := handler.verify(ext, parsedChains) err := handler.Verify(ext, parsedChains)
if err != nil { if err != nil {
return ErrExtension.Wrap(err) return ErrExtension.Wrap(err)
} }

View File

@ -1,7 +1,7 @@
// Copyright (C) 2018 Storj Labs, Inc. // Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information. // See LICENSE for copying information.
package peertls package peertls_test
import ( import (
"bytes" "bytes"
@ -21,16 +21,19 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/zeebo/errs" "github.com/zeebo/errs"
"storj.io/storj/internal/testpeertls"
"storj.io/storj/pkg/peertls"
) )
func TestNewCert_CA(t *testing.T) { func TestNewCert_CA(t *testing.T) {
caKey, err := NewKey() caKey, err := peertls.NewKey()
assert.NoError(t, err) assert.NoError(t, err)
caTemplate, err := CATemplate() caTemplate, err := peertls.CATemplate()
assert.NoError(t, err) assert.NoError(t, err)
caCert, err := NewCert(caKey, nil, caTemplate, nil) caCert, err := peertls.NewCert(caKey, nil, caTemplate, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey)) assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey))
@ -42,22 +45,22 @@ func TestNewCert_CA(t *testing.T) {
} }
func TestNewCert_Leaf(t *testing.T) { func TestNewCert_Leaf(t *testing.T) {
caKey, err := NewKey() caKey, err := peertls.NewKey()
assert.NoError(t, err) assert.NoError(t, err)
caTemplate, err := CATemplate() caTemplate, err := peertls.CATemplate()
assert.NoError(t, err) assert.NoError(t, err)
caCert, err := NewCert(caKey, nil, caTemplate, nil) caCert, err := peertls.NewCert(caKey, nil, caTemplate, nil)
assert.NoError(t, err) assert.NoError(t, err)
leafKey, err := NewKey() leafKey, err := peertls.NewKey()
assert.NoError(t, err) assert.NoError(t, err)
leafTemplate, err := LeafTemplate() leafTemplate, err := peertls.LeafTemplate()
assert.NoError(t, err) assert.NoError(t, err)
leafCert, err := NewCert(leafKey, caKey, leafTemplate, caCert) leafCert, err := peertls.NewCert(leafKey, caKey, leafTemplate, caCert)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey)) assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey))
@ -71,7 +74,7 @@ func TestNewCert_Leaf(t *testing.T) {
} }
func TestVerifyPeerFunc(t *testing.T) { func TestVerifyPeerFunc(t *testing.T) {
_, chain, err := newCertChain(2) _, chain, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -97,88 +100,88 @@ func TestVerifyPeerFunc(t *testing.T) {
return nil return nil
} }
err = VerifyPeerFunc(testFunc)([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(testFunc)([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestVerifyPeerCertChains(t *testing.T) { func TestVerifyPeerCertChains(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
leafKey, leafCert, caCert := keys[1], chain[0], chain[1] leafKey, leafCert, caCert := keys[1], chain[0], chain[1]
err = VerifyPeerFunc(VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
wrongKey, err := NewKey() wrongKey, err := peertls.NewKey()
assert.NoError(t, err) assert.NoError(t, err)
leafCert, err = NewCert(leafKey, wrongKey, leafCert, caCert) leafCert, err = peertls.NewCert(leafKey, wrongKey, leafCert, caCert)
assert.NoError(t, err) assert.NoError(t, err)
err = VerifyPeerFunc(VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.True(t, ErrVerifyPeerCert.Has(err)) assert.True(t, peertls.ErrVerifyPeerCert.Has(err))
assert.True(t, ErrVerifyCertificateChain.Has(err)) assert.True(t, peertls.ErrVerifyCertificateChain.Has(err))
} }
func TestVerifyCAWhitelist(t *testing.T) { func TestVerifyCAWhitelist(t *testing.T) {
_, chain2, err := newCertChain(2) _, chain2, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
leafCert, caCert := chain2[0], chain2[1] leafCert, caCert := chain2[0], chain2[1]
t.Run("empty whitelist", func(t *testing.T) { t.Run("empty whitelist", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist(nil))([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist(nil))([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("whitelist contains ca", func(t *testing.T) { t.Run("whitelist contains ca", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{caCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{caCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
_, unrelatedChain, err := newCertChain(1) _, unrelatedChain, err := testpeertls.NewCertChain(1)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
unrelatedCert := unrelatedChain[0] unrelatedCert := unrelatedChain[0]
t.Run("no valid signed extension, non-empty whitelist", func(t *testing.T) { t.Run("no valid signed extension, non-empty whitelist", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{unrelatedCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{unrelatedCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.True(t, ErrVerifyCAWhitelist.Has(err)) assert.True(t, peertls.ErrVerifyCAWhitelist.Has(err))
}) })
t.Run("last cert in whitelist is signer", func(t *testing.T) { t.Run("last cert in whitelist is signer", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{unrelatedCert, caCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{unrelatedCert, caCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("first cert in whitelist is signer", func(t *testing.T) { t.Run("first cert in whitelist is signer", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{caCert, unrelatedCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{caCert, unrelatedCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
_, chain3, err := newCertChain(3) _, chain3, err := testpeertls.NewCertChain(3)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
leaf2Cert, ca2Cert, rootCert := chain3[0], chain3[1], chain3[2] leaf2Cert, ca2Cert, rootCert := chain3[0], chain3[1], chain3[2]
t.Run("length 3 chain - first cert in whitelist is signer", func(t *testing.T) { t.Run("length 3 chain - first cert in whitelist is signer", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{rootCert, unrelatedCert}))([][]byte{leaf2Cert.Raw, ca2Cert.Raw, unrelatedCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{rootCert, unrelatedCert}))([][]byte{leaf2Cert.Raw, ca2Cert.Raw, unrelatedCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("length 3 chain - last cert in whitelist is signer", func(t *testing.T) { t.Run("length 3 chain - last cert in whitelist is signer", func(t *testing.T) {
err = VerifyPeerFunc(VerifyCAWhitelist([]*x509.Certificate{unrelatedCert, rootCert}))([][]byte{leaf2Cert.Raw, ca2Cert.Raw, unrelatedCert.Raw}, nil) err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{unrelatedCert, rootCert}))([][]byte{leaf2Cert.Raw, ca2Cert.Raw, unrelatedCert.Raw}, nil)
assert.NoError(t, err) assert.NoError(t, err)
}) })
} }
func TestAddExtension(t *testing.T) { func TestAddExtension(t *testing.T) {
_, chain, err := newCertChain(1) _, chain, err := testpeertls.NewCertChain(1)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -197,30 +200,30 @@ func TestAddExtension(t *testing.T) {
Value: randBytes, Value: randBytes,
} }
err = AddExtension(chain[0], ext) err = peertls.AddExtension(chain[0], ext)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, chain[0].ExtraExtensions, 1) assert.Len(t, chain[0].ExtraExtensions, 1)
assert.Equal(t, ext, chain[0].ExtraExtensions[0]) assert.Equal(t, ext, chain[0].ExtraExtensions[0])
} }
func TestAddSignedCertExt(t *testing.T) { func TestAddSignedCertExt(t *testing.T) {
keys, chain, err := newCertChain(1) keys, chain, err := testpeertls.NewCertChain(1)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
err = AddSignedCertExt(keys[0], chain[0]) err = peertls.AddSignedCertExt(keys[0], chain[0])
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, chain[0].ExtraExtensions, 1) assert.Len(t, chain[0].ExtraExtensions, 1)
assert.Equal(t, ExtensionIDs[SignedCertExtID], chain[0].ExtraExtensions[0].Id) assert.Equal(t, peertls.ExtensionIDs[peertls.SignedCertExtID], chain[0].ExtraExtensions[0].Id)
ecKey, ok := keys[0].(*ecdsa.PrivateKey) ecKey, ok := keys[0].(*ecdsa.PrivateKey)
if !assert.True(t, ok) { if !assert.True(t, ok) {
t.FailNow() t.FailNow()
} }
err = VerifySignature( err = peertls.VerifySignature(
chain[0].ExtraExtensions[0].Value, chain[0].ExtraExtensions[0].Value,
chain[0].RawTBSCertificate, chain[0].RawTBSCertificate,
&ecKey.PublicKey, &ecKey.PublicKey,
@ -229,35 +232,35 @@ func TestAddSignedCertExt(t *testing.T) {
} }
func TestSignLeafExt(t *testing.T) { func TestSignLeafExt(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
caKey, leafCert := keys[0], chain[0] caKey, leafCert := keys[0], chain[0]
err = AddSignedCertExt(caKey, leafCert) err = peertls.AddSignedCertExt(caKey, leafCert)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(leafCert.ExtraExtensions)) assert.Equal(t, 1, len(leafCert.ExtraExtensions))
assert.True(t, ExtensionIDs[SignedCertExtID].Equal(leafCert.ExtraExtensions[0].Id)) assert.True(t, peertls.ExtensionIDs[peertls.SignedCertExtID].Equal(leafCert.ExtraExtensions[0].Id))
caECKey, ok := caKey.(*ecdsa.PrivateKey) caECKey, ok := caKey.(*ecdsa.PrivateKey)
if !assert.True(t, ok) { if !assert.True(t, ok) {
t.FailNow() t.FailNow()
} }
err = VerifySignature(leafCert.ExtraExtensions[0].Value, leafCert.RawTBSCertificate, &caECKey.PublicKey) err = peertls.VerifySignature(leafCert.ExtraExtensions[0].Value, leafCert.RawTBSCertificate, &caECKey.PublicKey)
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestRevocation_Sign(t *testing.T) { func TestRevocation_Sign(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
assert.NoError(t, err) assert.NoError(t, err)
leafCert, caKey := chain[0], keys[0] leafCert, caKey := chain[0], keys[0]
leafHash, err := SHA256Hash(leafCert.Raw) leafHash, err := peertls.SHA256Hash(leafCert.Raw)
assert.NoError(t, err) assert.NoError(t, err)
rev := Revocation{ rev := peertls.Revocation{
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
CertHash: make([]byte, len(leafHash)), CertHash: make([]byte, len(leafHash)),
} }
@ -268,14 +271,14 @@ func TestRevocation_Sign(t *testing.T) {
} }
func TestRevocation_Verify(t *testing.T) { func TestRevocation_Verify(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
assert.NoError(t, err) assert.NoError(t, err)
leafCert, caCert, caKey := chain[0], chain[1], keys[0] leafCert, caCert, caKey := chain[0], chain[1], keys[0]
leafHash, err := SHA256Hash(leafCert.Raw) leafHash, err := peertls.SHA256Hash(leafCert.Raw)
assert.NoError(t, err) assert.NoError(t, err)
rev := Revocation{ rev := peertls.Revocation{
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
CertHash: make([]byte, len(leafHash)), CertHash: make([]byte, len(leafHash)),
} }
@ -289,14 +292,14 @@ func TestRevocation_Verify(t *testing.T) {
} }
func TestRevocation_Marshal(t *testing.T) { func TestRevocation_Marshal(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
assert.NoError(t, err) assert.NoError(t, err)
leafCert, caKey := chain[0], keys[0] leafCert, caKey := chain[0], keys[0]
leafHash, err := SHA256Hash(leafCert.Raw) leafHash, err := peertls.SHA256Hash(leafCert.Raw)
assert.NoError(t, err) assert.NoError(t, err)
rev := Revocation{ rev := peertls.Revocation{
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
CertHash: make([]byte, len(leafHash)), CertHash: make([]byte, len(leafHash)),
} }
@ -309,7 +312,7 @@ func TestRevocation_Marshal(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, revBytes) assert.NotEmpty(t, revBytes)
decodedRev := new(Revocation) decodedRev := new(peertls.Revocation)
decoder := gob.NewDecoder(bytes.NewBuffer(revBytes)) decoder := gob.NewDecoder(bytes.NewBuffer(revBytes))
err = decoder.Decode(decodedRev) err = decoder.Decode(decodedRev)
assert.NoError(t, err) assert.NoError(t, err)
@ -317,14 +320,14 @@ func TestRevocation_Marshal(t *testing.T) {
} }
func TestRevocation_Unmarshal(t *testing.T) { func TestRevocation_Unmarshal(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
assert.NoError(t, err) assert.NoError(t, err)
leafCert, caKey := chain[0], keys[0] leafCert, caKey := chain[0], keys[0]
leafHash, err := SHA256Hash(leafCert.Raw) leafHash, err := peertls.SHA256Hash(leafCert.Raw)
assert.NoError(t, err) assert.NoError(t, err)
rev := Revocation{ rev := peertls.Revocation{
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
CertHash: make([]byte, len(leafHash)), CertHash: make([]byte, len(leafHash)),
} }
@ -338,7 +341,7 @@ func TestRevocation_Unmarshal(t *testing.T) {
err = encoder.Encode(rev) err = encoder.Encode(rev)
assert.NoError(t, err) assert.NoError(t, err)
unmarshaledRev := new(Revocation) unmarshaledRev := new(peertls.Revocation)
err = unmarshaledRev.Unmarshal(encodedRev.Bytes()) err = unmarshaledRev.Unmarshal(encodedRev.Bytes())
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, rev) assert.NotNil(t, rev)
@ -346,13 +349,13 @@ func TestRevocation_Unmarshal(t *testing.T) {
} }
func TestNewRevocationExt(t *testing.T) { func TestNewRevocationExt(t *testing.T) {
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
assert.NoError(t, err) assert.NoError(t, err)
ext, err := NewRevocationExt(keys[0], chain[0]) ext, err := peertls.NewRevocationExt(keys[0], chain[0])
assert.NoError(t, err) assert.NoError(t, err)
var rev Revocation var rev peertls.Revocation
err = rev.Unmarshal(ext.Value) err = rev.Unmarshal(ext.Value)
assert.NoError(t, err) assert.NoError(t, err)
@ -364,29 +367,29 @@ func TestRevocationDB_Get(t *testing.T) {
tmp, err := ioutil.TempDir("", "TestRevocationDB_Get") tmp, err := ioutil.TempDir("", "TestRevocationDB_Get")
defer func() { _ = os.RemoveAll(tmp) }() defer func() { _ = os.RemoveAll(tmp) }()
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
ext, err := NewRevocationExt(keys[0], chain[0]) ext, err := peertls.NewRevocationExt(keys[0], chain[0])
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
revDB, err := NewRevocationDBBolt(filepath.Join(tmp, "revocations.db")) revDB, err := peertls.NewRevocationDBBolt(filepath.Join(tmp, "revocations.db"))
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
var rev *Revocation var rev *peertls.Revocation
t.Run("missing key", func(t *testing.T) { t.Run("missing key", func(t *testing.T) {
rev, err = revDB.Get(chain) rev, err = revDB.Get(chain)
assert.NoError(t, err) assert.NoError(t, err)
assert.Nil(t, rev) assert.Nil(t, rev)
}) })
caHash, err := SHA256Hash(chain[1].Raw) caHash, err := peertls.SHA256Hash(chain[1].Raw)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -410,25 +413,25 @@ func TestRevocationDB_Put(t *testing.T) {
tmp, err := ioutil.TempDir("", "TestRevocationDB_Put") tmp, err := ioutil.TempDir("", "TestRevocationDB_Put")
defer func() { _ = os.RemoveAll(tmp) }() defer func() { _ = os.RemoveAll(tmp) }()
keys, chain, err := newCertChain(2) keys, chain, err := testpeertls.NewCertChain(2)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
olderExt, err := NewRevocationExt(keys[0], chain[0]) olderExt, err := peertls.NewRevocationExt(keys[0], chain[0])
assert.NoError(t, err) assert.NoError(t, err)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
ext, err := NewRevocationExt(keys[0], chain[0]) ext, err := peertls.NewRevocationExt(keys[0], chain[0])
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
newerExt, err := NewRevocationExt(keys[0], chain[0]) newerExt, err := peertls.NewRevocationExt(keys[0], chain[0])
assert.NoError(t, err) assert.NoError(t, err)
revDB, err := NewRevocationDBBolt(filepath.Join(tmp, "revocations.db")) revDB, err := peertls.NewRevocationDBBolt(filepath.Join(tmp, "revocations.db"))
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -448,8 +451,8 @@ func TestRevocationDB_Put(t *testing.T) {
{ {
"existing key - older timestamp", "existing key - older timestamp",
olderExt, olderExt,
&ErrExtension, &peertls.ErrExtension,
ErrRevocationTimestamp, peertls.ErrRevocationTimestamp,
}, },
{ {
"existing key - newer timestamp", "existing key - newer timestamp",
@ -480,7 +483,7 @@ func TestRevocationDB_Put(t *testing.T) {
t.FailNow() t.FailNow()
} }
func(t2 *testing.T, ext pkix.Extension) { func(t2 *testing.T, ext pkix.Extension) {
caHash, err := SHA256Hash(chain[1].Raw) caHash, err := peertls.SHA256Hash(chain[1].Raw)
if !assert.NoError(t2, err) { if !assert.NoError(t2, err) {
t2.FailNow() t2.FailNow()
} }
@ -490,7 +493,7 @@ func TestRevocationDB_Put(t *testing.T) {
t2.FailNow() t2.FailNow()
} }
rev := new(Revocation) rev := new(peertls.Revocation)
err = rev.Unmarshal(revBytes) err = rev.Unmarshal(revBytes)
assert.NoError(t2, err) assert.NoError(t2, err)
assert.True(t2, bytes.Equal(ext.Value, revBytes)) assert.True(t2, bytes.Equal(ext.Value, revBytes))
@ -515,7 +518,7 @@ func TestExtensionHandlers_VerifyFunc(t *testing.T) {
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
err = AddSignedCertExt(keys[0], chain[0]) err = peertls.AddSignedCertExt(keys[0], chain[0])
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -525,24 +528,24 @@ func TestExtensionHandlers_VerifyFunc(t *testing.T) {
return extMock.verify(ext, chain) return extMock.verify(ext, chain)
} }
handlers := ExtensionHandlers{ handlers := peertls.ExtensionHandlers{
{ {
id: ExtensionIDs[RevocationExtID], ID: peertls.ExtensionIDs[peertls.RevocationExtID],
verify: verify, Verify: verify,
}, },
{ {
id: ExtensionIDs[SignedCertExtID], ID: peertls.ExtensionIDs[peertls.SignedCertExtID],
verify: verify, Verify: verify,
}, },
} }
extMock.On("verify", chains[0][LeafIndex].ExtraExtensions[0], chains).Return(nil) extMock.On("verify", chains[0][peertls.LeafIndex].ExtraExtensions[0], chains).Return(nil)
extMock.On("verify", chains[0][LeafIndex].ExtraExtensions[1], chains).Return(nil) extMock.On("verify", chains[0][peertls.LeafIndex].ExtraExtensions[1], chains).Return(nil)
err = handlers.VerifyFunc()(nil, chains) err = handlers.VerifyFunc()(nil, chains)
assert.NoError(t, err) assert.NoError(t, err)
extMock.AssertCalled(t, "verify", chains[0][LeafIndex].ExtraExtensions[0], chains) extMock.AssertCalled(t, "verify", chains[0][peertls.LeafIndex].ExtraExtensions[0], chains)
extMock.AssertCalled(t, "verify", chains[0][LeafIndex].ExtraExtensions[1], chains) extMock.AssertCalled(t, "verify", chains[0][peertls.LeafIndex].ExtraExtensions[1], chains)
extMock.AssertExpectations(t) extMock.AssertExpectations(t)
// TODO: test error scenario(s) // TODO: test error scenario(s)
@ -552,13 +555,13 @@ func TestParseExtensions(t *testing.T) {
revokedLeafKeys, revokedLeafChain, err := newRevokedLeafChain() revokedLeafKeys, revokedLeafChain, err := newRevokedLeafChain()
assert.NoError(t, err) assert.NoError(t, err)
whitelistSignedKeys, whitelistSignedChain, err := newCertChain(3) whitelistSignedKeys, whitelistSignedChain, err := testpeertls.NewCertChain(3)
assert.NoError(t, err) assert.NoError(t, err)
err = AddSignedCertExt(whitelistSignedKeys[0], whitelistSignedChain[0]) err = peertls.AddSignedCertExt(whitelistSignedKeys[0], whitelistSignedChain[0])
assert.NoError(t, err) assert.NoError(t, err)
_, unrelatedChain, err := newCertChain(1) _, unrelatedChain, err := testpeertls.NewCertChain(1)
assert.NoError(t, err) assert.NoError(t, err)
tmp, err := ioutil.TempDir("", "TestParseExtensions") tmp, err := ioutil.TempDir("", "TestParseExtensions")
@ -567,12 +570,12 @@ func TestParseExtensions(t *testing.T) {
} }
defer func() { _ = os.RemoveAll(tmp) }() defer func() { _ = os.RemoveAll(tmp) }()
revDB, err := NewRevocationDBBolt(filepath.Join(tmp, "revocations.db")) revDB, err := peertls.NewRevocationDBBolt(filepath.Join(tmp, "revocations.db"))
assert.NoError(t, err) assert.NoError(t, err)
cases := []struct { cases := []struct {
testID string testID string
config TLSExtConfig config peertls.TLSExtConfig
extLen int extLen int
certChain []*x509.Certificate certChain []*x509.Certificate
whitelist []*x509.Certificate whitelist []*x509.Certificate
@ -581,7 +584,7 @@ func TestParseExtensions(t *testing.T) {
}{ }{
{ {
"leaf whitelist signature - success", "leaf whitelist signature - success",
TLSExtConfig{WhitelistSignedLeaf: true}, peertls.TLSExtConfig{WhitelistSignedLeaf: true},
1, 1,
whitelistSignedChain, whitelistSignedChain,
[]*x509.Certificate{whitelistSignedChain[2]}, []*x509.Certificate{whitelistSignedChain[2]},
@ -590,25 +593,25 @@ func TestParseExtensions(t *testing.T) {
}, },
{ {
"leaf whitelist signature - failure (empty whitelist)", "leaf whitelist signature - failure (empty whitelist)",
TLSExtConfig{WhitelistSignedLeaf: true}, peertls.TLSExtConfig{WhitelistSignedLeaf: true},
1, 1,
whitelistSignedChain, whitelistSignedChain,
nil, nil,
&ErrVerifyCAWhitelist, &peertls.ErrVerifyCAWhitelist,
nil, nil,
}, },
{ {
"leaf whitelist signature - failure", "leaf whitelist signature - failure",
TLSExtConfig{WhitelistSignedLeaf: true}, peertls.TLSExtConfig{WhitelistSignedLeaf: true},
1, 1,
whitelistSignedChain, whitelistSignedChain,
unrelatedChain, unrelatedChain,
&ErrVerifyCAWhitelist, &peertls.ErrVerifyCAWhitelist,
nil, nil,
}, },
{ {
"certificate revocation - single revocation ", "certificate revocation - single revocation ",
TLSExtConfig{Revocation: true}, peertls.TLSExtConfig{Revocation: true},
1, 1,
revokedLeafChain, revokedLeafChain,
nil, nil,
@ -617,10 +620,10 @@ func TestParseExtensions(t *testing.T) {
}, },
{ {
"certificate revocation - serial revocations", "certificate revocation - serial revocations",
TLSExtConfig{Revocation: true}, peertls.TLSExtConfig{Revocation: true},
1, 1,
func() []*x509.Certificate { func() []*x509.Certificate {
rev := new(Revocation) rev := new(peertls.Revocation)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
_, chain, err := revokeLeaf(revokedLeafKeys, revokedLeafChain) _, chain, err := revokeLeaf(revokedLeafKeys, revokedLeafChain)
assert.NoError(t, err) assert.NoError(t, err)
@ -636,13 +639,13 @@ func TestParseExtensions(t *testing.T) {
}, },
{ {
"certificate revocation - serial revocations error (older timestamp)", "certificate revocation - serial revocations error (older timestamp)",
TLSExtConfig{Revocation: true}, peertls.TLSExtConfig{Revocation: true},
1, 1,
func() []*x509.Certificate { func() []*x509.Certificate {
keys, chain, err := newRevokedLeafChain() keys, chain, err := newRevokedLeafChain()
assert.NoError(t, err) assert.NoError(t, err)
rev := new(Revocation) rev := new(peertls.Revocation)
err = rev.Unmarshal(chain[0].ExtraExtensions[0].Value) err = rev.Unmarshal(chain[0].ExtraExtensions[0].Value)
assert.NoError(t, err) assert.NoError(t, err)
@ -654,25 +657,25 @@ func TestParseExtensions(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
err = revDB.Put(chain, pkix.Extension{ err = revDB.Put(chain, pkix.Extension{
Id: ExtensionIDs[RevocationExtID], Id: peertls.ExtensionIDs[peertls.RevocationExtID],
Value: revBytes, Value: revBytes,
}) })
assert.NoError(t, err) assert.NoError(t, err)
return chain return chain
}(), }(),
nil, nil,
&ErrExtension, &peertls.ErrExtension,
ErrRevocationTimestamp, peertls.ErrRevocationTimestamp,
}, },
{ {
"certificate revocation and leaf whitelist signature", "certificate revocation and leaf whitelist signature",
TLSExtConfig{Revocation: true, WhitelistSignedLeaf: true}, peertls.TLSExtConfig{Revocation: true, WhitelistSignedLeaf: true},
2, 2,
func() []*x509.Certificate { func() []*x509.Certificate {
_, chain, err := newRevokedLeafChain() _, chain, err := newRevokedLeafChain()
assert.NoError(t, err) assert.NoError(t, err)
err = AddSignedCertExt(whitelistSignedKeys[0], chain[0]) err = peertls.AddSignedCertExt(whitelistSignedKeys[0], chain[0])
assert.NoError(t, err) assert.NoError(t, err)
return chain return chain
@ -685,12 +688,12 @@ func TestParseExtensions(t *testing.T) {
for _, c := range cases { for _, c := range cases {
t.Run(c.testID, func(t *testing.T) { t.Run(c.testID, func(t *testing.T) {
opts := ParseExtOptions{ opts := peertls.ParseExtOptions{
CAWhitelist: c.whitelist, CAWhitelist: c.whitelist,
RevDB: revDB, RevDB: revDB,
} }
handlers := ParseExtensions(c.config, opts) handlers := peertls.ParseExtensions(c.config, opts)
assert.Equal(t, c.extLen, len(handlers)) assert.Equal(t, c.extLen, len(handlers))
err := handlers.VerifyFunc()(nil, [][]*x509.Certificate{c.certChain}) err := handlers.VerifyFunc()(nil, [][]*x509.Certificate{c.certChain})
if c.errClass != nil { if c.errClass != nil {
@ -706,57 +709,23 @@ func TestParseExtensions(t *testing.T) {
} }
} }
// NB: keys are in the reverse order compared to certs (i.e. first key belongs to last cert)!
func newCertChain(length int) (keys []crypto.PrivateKey, certs []*x509.Certificate, _ error) {
for i := 0; i < length; i++ {
key, err := NewKey()
if err != nil {
return nil, nil, err
}
keys = append(keys, key)
var template *x509.Certificate
if i == length-1 {
template, err = CATemplate()
} else {
template, err = LeafTemplate()
}
if err != nil {
return nil, nil, err
}
var cert *x509.Certificate
if i == 0 {
cert, err = NewCert(key, nil, template, nil)
} else {
cert, err = NewCert(key, keys[i-1], template, certs[i-1:][0])
}
if err != nil {
return nil, nil, err
}
certs = append([]*x509.Certificate{cert}, certs...)
}
return keys, certs, nil
}
func revokeLeaf(keys []crypto.PrivateKey, chain []*x509.Certificate) ([]crypto.PrivateKey, []*x509.Certificate, error) { func revokeLeaf(keys []crypto.PrivateKey, chain []*x509.Certificate) ([]crypto.PrivateKey, []*x509.Certificate, error) {
revokingKey, err := NewKey() revokingKey, err := peertls.NewKey()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
revokingTemplate, err := LeafTemplate() revokingTemplate, err := peertls.LeafTemplate()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
revokingCert, err := NewCert(revokingKey, keys[0], revokingTemplate, chain[1]) revokingCert, err := peertls.NewCert(revokingKey, keys[0], revokingTemplate, chain[1])
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
err = AddRevocationExt(keys[0], chain[0], revokingCert) err = peertls.AddRevocationExt(keys[0], chain[0], revokingCert)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -765,7 +734,7 @@ func revokeLeaf(keys []crypto.PrivateKey, chain []*x509.Certificate) ([]crypto.P
} }
func newRevokedLeafChain() ([]crypto.PrivateKey, []*x509.Certificate, error) { func newRevokedLeafChain() ([]crypto.PrivateKey, []*x509.Certificate, error) {
keys2, certs2, err := newCertChain(2) keys2, certs2, err := testpeertls.NewCertChain(2)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -9,7 +9,8 @@ import (
"github.com/mr-tron/base58/base58" "github.com/mr-tron/base58/base58"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"storj.io/storj/internal/identity" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
) )
func TestNewPieceID(t *testing.T) { func TestNewPieceID(t *testing.T) {
@ -26,8 +27,9 @@ func TestNewPieceID(t *testing.T) {
} }
func TestDerivePieceID(t *testing.T) { func TestDerivePieceID(t *testing.T) {
ctx := testcontext.New(t)
pid := NewPieceID() pid := NewPieceID()
fid, err := testidentity.NewTestIdentity() fid, err := testidentity.NewTestIdentity(ctx)
assert.NoError(t, err) assert.NoError(t, err)
did, err := pid.Derive(fid.ID.Bytes()) did, err := pid.Derive(fid.ID.Bytes())
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -28,9 +28,9 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"storj.io/storj/internal/identity" "storj.io/storj/internal/testidentity"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"
"storj.io/storj/pkg/piecestore" pstore "storj.io/storj/pkg/piecestore"
"storj.io/storj/pkg/piecestore/psserver/psdb" "storj.io/storj/pkg/piecestore/psserver/psdb"
"storj.io/storj/pkg/storj" "storj.io/storj/pkg/storj"
) )

View File

@ -21,7 +21,7 @@ import (
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"storj.io/storj/internal/identity" "storj.io/storj/internal/testidentity"
"storj.io/storj/pkg/auth" "storj.io/storj/pkg/auth"
"storj.io/storj/pkg/pb" "storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storage/meta" "storj.io/storj/pkg/storage/meta"