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:
parent
b712fbcbb0
commit
249244536a
2
Makefile
2
Makefile
@ -57,7 +57,7 @@ goimports-fix: ## Applies goimports to every go file (excluding vendored files)
|
||||
|
||||
.PHONY: goimports-st
|
||||
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
|
||||
proto: ## Rebuild protobuf files
|
||||
|
3
go.mod
3
go.mod
@ -42,7 +42,7 @@ require (
|
||||
github.com/go-redis/redis v6.14.1+incompatible
|
||||
github.com/gogo/protobuf v1.1.2-0.20181116123445-07eab6a8298c
|
||||
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/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
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-streaming-server v0.11.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/pierrec/lz4 v2.0.5+incompatible // indirect
|
||||
github.com/pkg/profile v1.2.1 // indirect
|
||||
|
10
go.sum
10
go.sum
@ -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/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||
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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
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/go.mod h1:2871C3urfEJnq/bpTYjFdMOdgxVd8otLLEL6vMNy/Iw=
|
||||
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/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-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/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
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/nuid v1.0.0 h1:44QGdhbiANq8ZCbUkdn6W5bqtg+mHuDE4wOUuxxndFs=
|
||||
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/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
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/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/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/go.mod h1:Yj8dHrUQwls1bF3dr/vcSIu+qf4mI7idnTcHfoACc6I=
|
||||
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=
|
||||
|
96
internal/debugging/certificates.go
Normal file
96
internal/debugging/certificates.go
Normal 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)
|
||||
}
|
@ -11,8 +11,8 @@ import (
|
||||
|
||||
// NewTestIdentity is a helper function to generate new node identities with
|
||||
// correct difficulty and concurrency
|
||||
func NewTestIdentity() (*provider.FullIdentity, error) {
|
||||
ca, err := provider.NewCA(context.Background(), provider.NewCAOptions{
|
||||
func NewTestIdentity(ctx context.Context) (*provider.FullIdentity, error) {
|
||||
ca, err := provider.NewCA(ctx, provider.NewCAOptions{
|
||||
Difficulty: 12,
|
||||
Concurrency: 4,
|
||||
})
|
@ -4,102 +4,43 @@
|
||||
package testpeertls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"storj.io/storj/pkg/peertls"
|
||||
)
|
||||
|
||||
// DebugCert is a subset of the most relevant fields from an x509.Certificate for debugging
|
||||
type DebugCert struct {
|
||||
Raw []byte
|
||||
RawTBSCertificate []byte
|
||||
Signature []byte
|
||||
PublicKeyX *big.Int
|
||||
PublicKeyY *big.Int
|
||||
Extensions []pkix.Extension
|
||||
}
|
||||
|
||||
// NewCertDebug converts an *x509.Certificate into a DebugCert
|
||||
func NewCertDebug(cert x509.Certificate) DebugCert {
|
||||
pubKey := cert.PublicKey.(*ecdsa.PublicKey)
|
||||
c := DebugCert{
|
||||
Raw: make([]byte, len(cert.Raw)),
|
||||
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,
|
||||
// NewCertChain creates a valid peertls certificate chain (and respective keys) of the desired length.
|
||||
// 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 := peertls.NewKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case *ecdsa.PrivateKey:
|
||||
data = struct {
|
||||
X *big.Int
|
||||
Y *big.Int
|
||||
D *big.Int
|
||||
}{
|
||||
d.X, d.Y, d.D,
|
||||
keys = append(keys, key)
|
||||
|
||||
var template *x509.Certificate
|
||||
if i == length-1 {
|
||||
template, err = peertls.CATemplate()
|
||||
} else {
|
||||
template, err = peertls.LeafTemplate()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
jsonBytes, err = json.MarshalIndent(data, "", "\t\t")
|
||||
var cert *x509.Certificate
|
||||
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
|
||||
}
|
||||
|
||||
if label != "" {
|
||||
fmt.Println(label + ": ---================================================================---")
|
||||
certs = append([]*x509.Certificate{cert}, certs...)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %s", err.Error())
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonBytes))
|
||||
fmt.Println("")
|
||||
return keys, certs, nil
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/pkg/bwagreement"
|
||||
"storj.io/storj/pkg/bwagreement/test"
|
||||
"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)
|
||||
|
||||
//get a private key
|
||||
fiC, err := testidentity.NewTestIdentity()
|
||||
fiC, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
k, ok := fiC.Key.(*ecdsa.PrivateKey)
|
||||
assert.True(t, ok)
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/internal/teststorj"
|
||||
"storj.io/storj/pkg/auth"
|
||||
"storj.io/storj/pkg/overlay"
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/gtank/cryptopasta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
)
|
||||
|
||||
func TestGenerateSignature(t *testing.T) {
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/pkg/bwagreement"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"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) {
|
||||
fiS, err := testidentity.NewTestIdentity()
|
||||
fiS, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
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)
|
||||
assert.True(t, ok)
|
||||
|
||||
fiU, err := testidentity.NewTestIdentity()
|
||||
fiU, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
uplinkPrivKey, ok = fiU.Key.(*ecdsa.PrivateKey)
|
||||
|
@ -6,21 +6,27 @@ package certificates
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"github.com/zeebo/errs"
|
||||
"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/peertls"
|
||||
"storj.io/storj/pkg/provider"
|
||||
"storj.io/storj/pkg/transport"
|
||||
"storj.io/storj/pkg/utils"
|
||||
"storj.io/storj/storage"
|
||||
"storj.io/storj/storage/boltdb"
|
||||
@ -30,6 +36,9 @@ import (
|
||||
const (
|
||||
// AuthorizationsBucket is the bucket used with a bolt-backed authorizations DB.
|
||||
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
|
||||
tokenDelimiter = ":"
|
||||
tokenVersion = 0
|
||||
@ -47,15 +56,26 @@ var (
|
||||
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 {
|
||||
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"`
|
||||
MinDifficulty uint `help:"minimum difficulty of the requester's identity required to claim an authorization"`
|
||||
CA provider.FullCAConfig
|
||||
}
|
||||
|
||||
// CertificateSigner implements pb.CertificatesServer
|
||||
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
|
||||
@ -82,22 +102,52 @@ type Token struct {
|
||||
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
|
||||
// token was claimed.
|
||||
type Claim struct {
|
||||
IP string
|
||||
Timestamp int64
|
||||
Identity *provider.PeerIdentity
|
||||
SignedCert *x509.Certificate
|
||||
Addr string
|
||||
Timestamp int64
|
||||
Identity *provider.PeerIdentity
|
||||
SignedChainBytes [][]byte
|
||||
}
|
||||
|
||||
// NewServer creates a new certificate signing grpc server
|
||||
func NewServer(log *zap.Logger) pb.CertificatesServer {
|
||||
srv := CertificateSigner{
|
||||
Log: log,
|
||||
// Client implements pb.CertificateClient
|
||||
type Client struct {
|
||||
client pb.CertificatesClient
|
||||
}
|
||||
|
||||
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 &srv
|
||||
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
|
||||
@ -141,6 +191,20 @@ func ParseToken(tokenString string) (*Token, error) {
|
||||
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
|
||||
func (c CertSignerConfig) NewAuthDB() (*AuthorizationDB, error) {
|
||||
// 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) {
|
||||
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)
|
||||
|
||||
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.
|
||||
func (c CertificateSigner) Sign(ctx context.Context, req *pb.SigningRequest) (*pb.SigningResponse, error) {
|
||||
// lookup authtoken
|
||||
// sign cert
|
||||
// send response
|
||||
return &pb.SigningResponse{}, nil
|
||||
grpcPeer, ok := peer.FromContext(ctx)
|
||||
if !ok {
|
||||
// TODO: better error
|
||||
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.
|
||||
@ -214,11 +328,6 @@ func (a *AuthorizationDB) Create(userID string, count int) (Authorizations, erro
|
||||
return nil, ErrAuthorizationCount
|
||||
}
|
||||
|
||||
existingAuths, err := a.Get(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
newAuths Authorizations
|
||||
authErrs utils.ErrorGroup
|
||||
@ -235,22 +344,16 @@ func (a *AuthorizationDB) Create(userID string, count int) (Authorizations, erro
|
||||
return nil, ErrAuthorizationDB.Wrap(err)
|
||||
}
|
||||
|
||||
existingAuths = append(existingAuths, newAuths...)
|
||||
authsBytes, err := existingAuths.Marshal()
|
||||
if err != nil {
|
||||
return nil, ErrAuthorizationDB.Wrap(err)
|
||||
}
|
||||
|
||||
if err := a.DB.Put(storage.Key(userID), authsBytes); err != nil {
|
||||
return nil, ErrAuthorizationDB.Wrap(err)
|
||||
if err := a.add(userID, newAuths); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newAuths, nil
|
||||
}
|
||||
|
||||
// Get retrieves authorizations by email.
|
||||
func (a *AuthorizationDB) Get(email string) (Authorizations, error) {
|
||||
authsBytes, err := a.DB.Get(storage.Key(email))
|
||||
// Get retrieves authorizations by user ID.
|
||||
func (a *AuthorizationDB) Get(userID string) (Authorizations, error) {
|
||||
authsBytes, err := a.DB.Get(storage.Key(userID))
|
||||
if err != nil && !storage.ErrKeyNotFound.Has(err) {
|
||||
return nil, ErrAuthorizationDB.Wrap(err)
|
||||
}
|
||||
@ -274,6 +377,84 @@ func (a *AuthorizationDB) UserIDs() ([]string, error) {
|
||||
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
|
||||
func (a *Authorizations) Unmarshal(data []byte) error {
|
||||
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
|
||||
// from completely leaking into logs.
|
||||
// from completely leaking into logs and errors.
|
||||
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
|
||||
|
@ -5,23 +5,37 @@ package certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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/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/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
t1 = Token{
|
||||
idents = testplanet.NewPregeneratedIdentities()
|
||||
t1 = Token{
|
||||
UserID: "user@example.com",
|
||||
Data: [tokenDataLength]byte{1, 2, 3},
|
||||
}
|
||||
@ -33,14 +47,12 @@ var (
|
||||
|
||||
func TestCertSignerConfig_NewAuthDB(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
authDB, err := newTestAuthDB(ctx)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = authDB.Close()
|
||||
ctx.Cleanup()
|
||||
}()
|
||||
defer ctx.Check(authDB.Close)
|
||||
|
||||
assert.NotNil(t, authDB)
|
||||
assert.NotNil(t, authDB.DB)
|
||||
@ -48,14 +60,12 @@ func TestCertSignerConfig_NewAuthDB(t *testing.T) {
|
||||
|
||||
func TestAuthorizationDB_Create(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
authDB, err := newTestAuthDB(ctx)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = authDB.Close()
|
||||
ctx.Cleanup()
|
||||
}()
|
||||
defer ctx.Check(authDB.Close)
|
||||
|
||||
cases := []struct {
|
||||
testID,
|
||||
@ -144,7 +154,8 @@ func TestAuthorizationDB_Get(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = authDB.Close()
|
||||
err := authDB.Close()
|
||||
assert.NoError(t, err)
|
||||
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) {
|
||||
userID := "user@example.com"
|
||||
auth, err := NewAuthorization(userID)
|
||||
@ -274,7 +504,8 @@ func TestAuthorizationDB_Emails(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = authDB.Close()
|
||||
err = authDB.Close()
|
||||
assert.NoError(t, err)
|
||||
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) {
|
||||
dbPath := "bolt://" + filepath.Join(ctx.Dir(), "authorizations.db")
|
||||
config := CertSignerConfig{
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"storj.io/storj/internal/testcontext"
|
||||
)
|
||||
|
||||
func TestNewCA(t *testing.T) {
|
||||
@ -27,21 +29,19 @@ func TestNewCA(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,
|
||||
Concurrency: 4,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if !assert.NoError(t, err) || !assert.NotNil(t, ca) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.NotEmpty(t, ca)
|
||||
|
||||
fi, err := ca.NewIdentity()
|
||||
if err != nil {
|
||||
if !assert.NoError(t, err) || !assert.NotNil(t, fi) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.NotEmpty(t, fi)
|
||||
|
||||
assert.Equal(t, ca.Cert, fi.CA)
|
||||
assert.Equal(t, ca.ID, fi.ID)
|
||||
@ -52,6 +52,36 @@ func TestFullCertificateAuthority_NewIdentity(t *testing.T) {
|
||||
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) {
|
||||
// TODO(bryanchriswhite): test with both
|
||||
// TODO(bryanchriswhite): test with only cert path
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
@ -75,6 +76,38 @@ type FullCAConfig struct {
|
||||
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
|
||||
func (caS CASetupConfig) Status() TLSFilesStatus {
|
||||
return statTLSFiles(caS.CertPath, caS.KeyPath)
|
||||
@ -175,39 +208,6 @@ func (pc PeerCAConfig) Load() (*PeerCertificateAuthority, error) {
|
||||
}, 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
|
||||
func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
|
||||
var (
|
||||
@ -246,7 +246,7 @@ func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
|
||||
// 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
|
||||
// is signed by the CA.
|
||||
func (ca FullCertificateAuthority) NewIdentity() (*FullIdentity, error) {
|
||||
func (ca *FullCertificateAuthority) NewIdentity() (*FullIdentity, error) {
|
||||
leafTemplate, err := peertls.LeafTemplate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -278,4 +278,29 @@ func (ca FullCertificateAuthority) NewIdentity() (*FullIdentity, error) {
|
||||
Key: leafKey,
|
||||
ID: ca.ID,
|
||||
}, 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
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func PeerIdentityFromPeer(peer *peer.Peer) (*PeerIdentity, error) {
|
||||
if len(c) < 2 {
|
||||
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 {
|
||||
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 {
|
||||
var chain [][]byte
|
||||
for _, cert := range fi.RestChain {
|
||||
|
@ -21,9 +21,10 @@ import (
|
||||
"go.uber.org/zap/zaptest"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/internal/teststorj"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/node"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/provider"
|
||||
@ -41,14 +42,14 @@ func TestNewKademlia(t *testing.T) {
|
||||
rootdir, cleanup := mktempdir(t, "kademlia")
|
||||
defer cleanup()
|
||||
cases := []struct {
|
||||
id *provider.FullIdentity
|
||||
id *identity.FullIdentity
|
||||
bn []pb.Node
|
||||
addr string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
id: func() *provider.FullIdentity {
|
||||
id, err := testidentity.NewTestIdentity()
|
||||
id: func() *identity.FullIdentity {
|
||||
id, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
return id
|
||||
}(),
|
||||
@ -57,7 +58,7 @@ func TestNewKademlia(t *testing.T) {
|
||||
},
|
||||
{
|
||||
id: func() *provider.FullIdentity {
|
||||
id, err := testidentity.NewTestIdentity()
|
||||
id, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
return id
|
||||
}(),
|
||||
@ -163,12 +164,13 @@ func TestBootstrap(t *testing.T) {
|
||||
}
|
||||
|
||||
func testNode(t *testing.T, bn []pb.Node) (*Kademlia, *grpc.Server, func()) {
|
||||
ctx := testcontext.New(t)
|
||||
// new address
|
||||
lis, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
// new config
|
||||
// new identity
|
||||
fid, err := testidentity.NewTestIdentity()
|
||||
fid, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
// new kademlia
|
||||
dir, cleanup := mktempdir(t, "kademlia")
|
||||
@ -238,9 +240,9 @@ func TestGetNodes(t *testing.T) {
|
||||
defer srv.Stop()
|
||||
|
||||
// make new identity
|
||||
fid, err := testidentity.NewTestIdentity()
|
||||
fid, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
fid2, err := testidentity.NewTestIdentity()
|
||||
fid2, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
fid.ID = nodeIDA
|
||||
fid2.ID = nodeIDB
|
||||
|
@ -17,9 +17,9 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/s3client"
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/internal/testplanet"
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/miniogw"
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
testidentity "storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/internal/testplanet"
|
||||
"storj.io/storj/pkg/overlay"
|
||||
"storj.io/storj/pkg/pb"
|
||||
|
@ -36,7 +36,7 @@ func (m *SigningRequest) Reset() { *m = SigningRequest{} }
|
||||
func (m *SigningRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SigningRequest) ProtoMessage() {}
|
||||
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 {
|
||||
return xxx_messageInfo_SigningRequest.Unmarshal(m, b)
|
||||
@ -71,7 +71,7 @@ func (m *SigningRequest) GetTimestamp() int64 {
|
||||
}
|
||||
|
||||
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_unrecognized []byte `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 (*SigningResponse) ProtoMessage() {}
|
||||
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 {
|
||||
return xxx_messageInfo_SigningResponse.Unmarshal(m, b)
|
||||
@ -101,9 +101,9 @@ func (m *SigningResponse) XXX_DiscardUnknown() {
|
||||
|
||||
var xxx_messageInfo_SigningResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *SigningResponse) GetCert() []byte {
|
||||
func (m *SigningResponse) GetChain() [][]byte {
|
||||
if m != nil {
|
||||
return m.Cert
|
||||
return m.Chain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -185,20 +185,20 @@ var _Certificates_serviceDesc = grpc.ServiceDesc{
|
||||
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{
|
||||
// 188 bytes of a gzipped FileDescriptorProto
|
||||
var fileDescriptor_certificate_c55bd879e75eb964 = []byte{
|
||||
// 192 bytes of a gzipped FileDescriptorProto
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
0xc5, 0x25, 0x89, 0xb9, 0x05, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x08, 0x01, 0x25, 0x55,
|
||||
0x2e, 0x7e, 0xb8, 0x71, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x42, 0x5c, 0x2c, 0x20, 0x87,
|
||||
0x80, 0x4d, 0xe2, 0x09, 0x02, 0xb3, 0x8d, 0x9c, 0xb9, 0x78, 0x9c, 0x11, 0x8e, 0x2b, 0x16, 0x32,
|
||||
0xe6, 0x62, 0x01, 0x69, 0x13, 0x12, 0xd1, 0x03, 0x39, 0x50, 0x0f, 0xd5, 0x45, 0x52, 0xa2, 0x68,
|
||||
0xa2, 0x10, 0x83, 0x9d, 0x58, 0xa2, 0x98, 0x0a, 0x92, 0x92, 0xd8, 0xc0, 0xfe, 0x30, 0x06, 0x04,
|
||||
0x00, 0x00, 0xff, 0xff, 0x4b, 0x1b, 0x87, 0x66, 0xee, 0x00, 0x00, 0x00,
|
||||
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, 0x22, 0x5c, 0xac, 0xc9, 0x19,
|
||||
0x89, 0x99, 0x20, 0xa3, 0x98, 0x35, 0x78, 0x82, 0x20, 0x1c, 0x23, 0x67, 0x2e, 0x1e, 0x67, 0x84,
|
||||
0xf3, 0x8a, 0x85, 0x8c, 0xb9, 0x58, 0x40, 0x1a, 0x85, 0x44, 0xf4, 0x40, 0x4e, 0xd4, 0x43, 0x75,
|
||||
0x93, 0x94, 0x28, 0x9a, 0x28, 0xc4, 0x68, 0x27, 0x96, 0x28, 0xa6, 0x82, 0xa4, 0x24, 0x36, 0xb0,
|
||||
0x4f, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3a, 0xd8, 0xc7, 0x87, 0xf0, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
@ -18,5 +18,5 @@ message SigningRequest {
|
||||
}
|
||||
|
||||
message SigningResponse {
|
||||
bytes cert = 1;
|
||||
repeated bytes chain = 1;
|
||||
}
|
@ -72,13 +72,15 @@ type TLSExtConfig struct {
|
||||
}
|
||||
|
||||
// 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 extensionHandler struct {
|
||||
id asn1.ObjectIdentifier
|
||||
verify extensionVerificationFunc
|
||||
// ExtensionHandler represents a verify function for handling an extension
|
||||
// with the given ID
|
||||
type ExtensionHandler struct {
|
||||
ID asn1.ObjectIdentifier
|
||||
Verify extensionVerificationFunc
|
||||
}
|
||||
|
||||
// ParseExtOptions holds options for calling `ParseExtensions`
|
||||
@ -107,16 +109,16 @@ type RevocationDB struct {
|
||||
// to be used in the context of peer certificate verification.
|
||||
func ParseExtensions(c TLSExtConfig, opts ParseExtOptions) (handlers ExtensionHandlers) {
|
||||
if c.WhitelistSignedLeaf {
|
||||
handlers = append(handlers, extensionHandler{
|
||||
id: ExtensionIDs[SignedCertExtID],
|
||||
verify: verifyCAWhitelistSignedLeafFunc(opts.CAWhitelist),
|
||||
handlers = append(handlers, ExtensionHandler{
|
||||
ID: ExtensionIDs[SignedCertExtID],
|
||||
Verify: verifyCAWhitelistSignedLeafFunc(opts.CAWhitelist),
|
||||
})
|
||||
}
|
||||
|
||||
if c.Revocation {
|
||||
handlers = append(handlers, extensionHandler{
|
||||
id: ExtensionIDs[RevocationExtID],
|
||||
verify: func(certExt pkix.Extension, chains [][]*x509.Certificate) error {
|
||||
handlers = append(handlers, ExtensionHandler{
|
||||
ID: ExtensionIDs[RevocationExtID],
|
||||
Verify: func(certExt pkix.Extension, chains [][]*x509.Certificate) error {
|
||||
if err := opts.RevDB.Put(chains[0], certExt); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -245,8 +247,8 @@ func (e ExtensionHandlers) VerifyFunc() PeerCertVerificationFunc {
|
||||
}
|
||||
|
||||
for _, handler := range e {
|
||||
if ext, ok := leafExts[handler.id.String()]; ok {
|
||||
err := handler.verify(ext, parsedChains)
|
||||
if ext, ok := leafExts[handler.ID.String()]; ok {
|
||||
err := handler.Verify(ext, parsedChains)
|
||||
if err != nil {
|
||||
return ErrExtension.Wrap(err)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package peertls
|
||||
package peertls_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -21,16 +21,19 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/internal/testpeertls"
|
||||
"storj.io/storj/pkg/peertls"
|
||||
)
|
||||
|
||||
func TestNewCert_CA(t *testing.T) {
|
||||
caKey, err := NewKey()
|
||||
caKey, err := peertls.NewKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
caTemplate, err := CATemplate()
|
||||
caTemplate, err := peertls.CATemplate()
|
||||
assert.NoError(t, err)
|
||||
|
||||
caCert, err := NewCert(caKey, nil, caTemplate, nil)
|
||||
caCert, err := peertls.NewCert(caKey, nil, caTemplate, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey))
|
||||
@ -42,22 +45,22 @@ func TestNewCert_CA(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewCert_Leaf(t *testing.T) {
|
||||
caKey, err := NewKey()
|
||||
caKey, err := peertls.NewKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
caTemplate, err := CATemplate()
|
||||
caTemplate, err := peertls.CATemplate()
|
||||
assert.NoError(t, err)
|
||||
|
||||
caCert, err := NewCert(caKey, nil, caTemplate, nil)
|
||||
caCert, err := peertls.NewCert(caKey, nil, caTemplate, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
leafKey, err := NewKey()
|
||||
leafKey, err := peertls.NewKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
leafTemplate, err := LeafTemplate()
|
||||
leafTemplate, err := peertls.LeafTemplate()
|
||||
assert.NoError(t, err)
|
||||
|
||||
leafCert, err := NewCert(leafKey, caKey, leafTemplate, caCert)
|
||||
leafCert, err := peertls.NewCert(leafKey, caKey, leafTemplate, caCert)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, caKey.(*ecdsa.PrivateKey))
|
||||
@ -71,7 +74,7 @@ func TestNewCert_Leaf(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVerifyPeerFunc(t *testing.T) {
|
||||
_, chain, err := newCertChain(2)
|
||||
_, chain, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@ -97,88 +100,88 @@ func TestVerifyPeerFunc(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
func TestVerifyPeerCertChains(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
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)
|
||||
|
||||
wrongKey, err := NewKey()
|
||||
wrongKey, err := peertls.NewKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
leafCert, err = NewCert(leafKey, wrongKey, leafCert, caCert)
|
||||
leafCert, err = peertls.NewCert(leafKey, wrongKey, leafCert, caCert)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = VerifyPeerFunc(VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil)
|
||||
assert.True(t, ErrVerifyPeerCert.Has(err))
|
||||
assert.True(t, ErrVerifyCertificateChain.Has(err))
|
||||
err = peertls.VerifyPeerFunc(peertls.VerifyPeerCertChains)([][]byte{leafCert.Raw, caCert.Raw}, nil)
|
||||
assert.True(t, peertls.ErrVerifyPeerCert.Has(err))
|
||||
assert.True(t, peertls.ErrVerifyCertificateChain.Has(err))
|
||||
}
|
||||
|
||||
func TestVerifyCAWhitelist(t *testing.T) {
|
||||
_, chain2, err := newCertChain(2)
|
||||
_, chain2, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
leafCert, caCert := chain2[0], chain2[1]
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
_, unrelatedChain, err := newCertChain(1)
|
||||
_, unrelatedChain, err := testpeertls.NewCertChain(1)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
unrelatedCert := unrelatedChain[0]
|
||||
|
||||
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)
|
||||
assert.True(t, ErrVerifyCAWhitelist.Has(err))
|
||||
err = peertls.VerifyPeerFunc(peertls.VerifyCAWhitelist([]*x509.Certificate{unrelatedCert}))([][]byte{leafCert.Raw, caCert.Raw}, nil)
|
||||
assert.True(t, peertls.ErrVerifyCAWhitelist.Has(err))
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
_, chain3, err := newCertChain(3)
|
||||
_, chain3, err := testpeertls.NewCertChain(3)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
leaf2Cert, ca2Cert, rootCert := chain3[0], chain3[1], chain3[2]
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddExtension(t *testing.T) {
|
||||
_, chain, err := newCertChain(1)
|
||||
_, chain, err := testpeertls.NewCertChain(1)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@ -197,30 +200,30 @@ func TestAddExtension(t *testing.T) {
|
||||
Value: randBytes,
|
||||
}
|
||||
|
||||
err = AddExtension(chain[0], ext)
|
||||
err = peertls.AddExtension(chain[0], ext)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, chain[0].ExtraExtensions, 1)
|
||||
assert.Equal(t, ext, chain[0].ExtraExtensions[0])
|
||||
}
|
||||
|
||||
func TestAddSignedCertExt(t *testing.T) {
|
||||
keys, chain, err := newCertChain(1)
|
||||
keys, chain, err := testpeertls.NewCertChain(1)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = AddSignedCertExt(keys[0], chain[0])
|
||||
err = peertls.AddSignedCertExt(keys[0], chain[0])
|
||||
assert.NoError(t, err)
|
||||
|
||||
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)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = VerifySignature(
|
||||
err = peertls.VerifySignature(
|
||||
chain[0].ExtraExtensions[0].Value,
|
||||
chain[0].RawTBSCertificate,
|
||||
&ecKey.PublicKey,
|
||||
@ -229,35 +232,35 @@ func TestAddSignedCertExt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignLeafExt(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
caKey, leafCert := keys[0], chain[0]
|
||||
|
||||
err = AddSignedCertExt(caKey, leafCert)
|
||||
err = peertls.AddSignedCertExt(caKey, leafCert)
|
||||
assert.NoError(t, err)
|
||||
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)
|
||||
if !assert.True(t, ok) {
|
||||
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)
|
||||
}
|
||||
|
||||
func TestRevocation_Sign(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
assert.NoError(t, err)
|
||||
leafCert, caKey := chain[0], keys[0]
|
||||
|
||||
leafHash, err := SHA256Hash(leafCert.Raw)
|
||||
leafHash, err := peertls.SHA256Hash(leafCert.Raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rev := Revocation{
|
||||
rev := peertls.Revocation{
|
||||
Timestamp: time.Now().Unix(),
|
||||
CertHash: make([]byte, len(leafHash)),
|
||||
}
|
||||
@ -268,14 +271,14 @@ func TestRevocation_Sign(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevocation_Verify(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
assert.NoError(t, err)
|
||||
leafCert, caCert, caKey := chain[0], chain[1], keys[0]
|
||||
|
||||
leafHash, err := SHA256Hash(leafCert.Raw)
|
||||
leafHash, err := peertls.SHA256Hash(leafCert.Raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rev := Revocation{
|
||||
rev := peertls.Revocation{
|
||||
Timestamp: time.Now().Unix(),
|
||||
CertHash: make([]byte, len(leafHash)),
|
||||
}
|
||||
@ -289,14 +292,14 @@ func TestRevocation_Verify(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevocation_Marshal(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
assert.NoError(t, err)
|
||||
leafCert, caKey := chain[0], keys[0]
|
||||
|
||||
leafHash, err := SHA256Hash(leafCert.Raw)
|
||||
leafHash, err := peertls.SHA256Hash(leafCert.Raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rev := Revocation{
|
||||
rev := peertls.Revocation{
|
||||
Timestamp: time.Now().Unix(),
|
||||
CertHash: make([]byte, len(leafHash)),
|
||||
}
|
||||
@ -309,7 +312,7 @@ func TestRevocation_Marshal(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, revBytes)
|
||||
|
||||
decodedRev := new(Revocation)
|
||||
decodedRev := new(peertls.Revocation)
|
||||
decoder := gob.NewDecoder(bytes.NewBuffer(revBytes))
|
||||
err = decoder.Decode(decodedRev)
|
||||
assert.NoError(t, err)
|
||||
@ -317,14 +320,14 @@ func TestRevocation_Marshal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevocation_Unmarshal(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
assert.NoError(t, err)
|
||||
leafCert, caKey := chain[0], keys[0]
|
||||
|
||||
leafHash, err := SHA256Hash(leafCert.Raw)
|
||||
leafHash, err := peertls.SHA256Hash(leafCert.Raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rev := Revocation{
|
||||
rev := peertls.Revocation{
|
||||
Timestamp: time.Now().Unix(),
|
||||
CertHash: make([]byte, len(leafHash)),
|
||||
}
|
||||
@ -338,7 +341,7 @@ func TestRevocation_Unmarshal(t *testing.T) {
|
||||
err = encoder.Encode(rev)
|
||||
assert.NoError(t, err)
|
||||
|
||||
unmarshaledRev := new(Revocation)
|
||||
unmarshaledRev := new(peertls.Revocation)
|
||||
err = unmarshaledRev.Unmarshal(encodedRev.Bytes())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, rev)
|
||||
@ -346,13 +349,13 @@ func TestRevocation_Unmarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewRevocationExt(t *testing.T) {
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ext, err := NewRevocationExt(keys[0], chain[0])
|
||||
ext, err := peertls.NewRevocationExt(keys[0], chain[0])
|
||||
assert.NoError(t, err)
|
||||
|
||||
var rev Revocation
|
||||
var rev peertls.Revocation
|
||||
err = rev.Unmarshal(ext.Value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@ -364,29 +367,29 @@ func TestRevocationDB_Get(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "TestRevocationDB_Get")
|
||||
defer func() { _ = os.RemoveAll(tmp) }()
|
||||
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
ext, err := NewRevocationExt(keys[0], chain[0])
|
||||
ext, err := peertls.NewRevocationExt(keys[0], chain[0])
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
revDB, err := NewRevocationDBBolt(filepath.Join(tmp, "revocations.db"))
|
||||
revDB, err := peertls.NewRevocationDBBolt(filepath.Join(tmp, "revocations.db"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var rev *Revocation
|
||||
var rev *peertls.Revocation
|
||||
t.Run("missing key", func(t *testing.T) {
|
||||
rev, err = revDB.Get(chain)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, rev)
|
||||
})
|
||||
|
||||
caHash, err := SHA256Hash(chain[1].Raw)
|
||||
caHash, err := peertls.SHA256Hash(chain[1].Raw)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@ -410,25 +413,25 @@ func TestRevocationDB_Put(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "TestRevocationDB_Put")
|
||||
defer func() { _ = os.RemoveAll(tmp) }()
|
||||
|
||||
keys, chain, err := newCertChain(2)
|
||||
keys, chain, err := testpeertls.NewCertChain(2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
olderExt, err := NewRevocationExt(keys[0], chain[0])
|
||||
olderExt, err := peertls.NewRevocationExt(keys[0], chain[0])
|
||||
assert.NoError(t, err)
|
||||
|
||||
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) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
newerExt, err := NewRevocationExt(keys[0], chain[0])
|
||||
newerExt, err := peertls.NewRevocationExt(keys[0], chain[0])
|
||||
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) {
|
||||
t.FailNow()
|
||||
}
|
||||
@ -448,8 +451,8 @@ func TestRevocationDB_Put(t *testing.T) {
|
||||
{
|
||||
"existing key - older timestamp",
|
||||
olderExt,
|
||||
&ErrExtension,
|
||||
ErrRevocationTimestamp,
|
||||
&peertls.ErrExtension,
|
||||
peertls.ErrRevocationTimestamp,
|
||||
},
|
||||
{
|
||||
"existing key - newer timestamp",
|
||||
@ -480,7 +483,7 @@ func TestRevocationDB_Put(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
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) {
|
||||
t2.FailNow()
|
||||
}
|
||||
@ -490,7 +493,7 @@ func TestRevocationDB_Put(t *testing.T) {
|
||||
t2.FailNow()
|
||||
}
|
||||
|
||||
rev := new(Revocation)
|
||||
rev := new(peertls.Revocation)
|
||||
err = rev.Unmarshal(revBytes)
|
||||
assert.NoError(t2, err)
|
||||
assert.True(t2, bytes.Equal(ext.Value, revBytes))
|
||||
@ -515,7 +518,7 @@ func TestExtensionHandlers_VerifyFunc(t *testing.T) {
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
err = AddSignedCertExt(keys[0], chain[0])
|
||||
err = peertls.AddSignedCertExt(keys[0], chain[0])
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@ -525,24 +528,24 @@ func TestExtensionHandlers_VerifyFunc(t *testing.T) {
|
||||
return extMock.verify(ext, chain)
|
||||
}
|
||||
|
||||
handlers := ExtensionHandlers{
|
||||
handlers := peertls.ExtensionHandlers{
|
||||
{
|
||||
id: ExtensionIDs[RevocationExtID],
|
||||
verify: verify,
|
||||
ID: peertls.ExtensionIDs[peertls.RevocationExtID],
|
||||
Verify: verify,
|
||||
},
|
||||
{
|
||||
id: ExtensionIDs[SignedCertExtID],
|
||||
verify: verify,
|
||||
ID: peertls.ExtensionIDs[peertls.SignedCertExtID],
|
||||
Verify: verify,
|
||||
},
|
||||
}
|
||||
|
||||
extMock.On("verify", chains[0][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[0], chains).Return(nil)
|
||||
extMock.On("verify", chains[0][peertls.LeafIndex].ExtraExtensions[1], chains).Return(nil)
|
||||
|
||||
err = handlers.VerifyFunc()(nil, chains)
|
||||
assert.NoError(t, err)
|
||||
extMock.AssertCalled(t, "verify", chains[0][LeafIndex].ExtraExtensions[0], chains)
|
||||
extMock.AssertCalled(t, "verify", chains[0][LeafIndex].ExtraExtensions[1], chains)
|
||||
extMock.AssertCalled(t, "verify", chains[0][peertls.LeafIndex].ExtraExtensions[0], chains)
|
||||
extMock.AssertCalled(t, "verify", chains[0][peertls.LeafIndex].ExtraExtensions[1], chains)
|
||||
extMock.AssertExpectations(t)
|
||||
|
||||
// TODO: test error scenario(s)
|
||||
@ -552,13 +555,13 @@ func TestParseExtensions(t *testing.T) {
|
||||
revokedLeafKeys, revokedLeafChain, err := newRevokedLeafChain()
|
||||
assert.NoError(t, err)
|
||||
|
||||
whitelistSignedKeys, whitelistSignedChain, err := newCertChain(3)
|
||||
whitelistSignedKeys, whitelistSignedChain, err := testpeertls.NewCertChain(3)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = AddSignedCertExt(whitelistSignedKeys[0], whitelistSignedChain[0])
|
||||
err = peertls.AddSignedCertExt(whitelistSignedKeys[0], whitelistSignedChain[0])
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, unrelatedChain, err := newCertChain(1)
|
||||
_, unrelatedChain, err := testpeertls.NewCertChain(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmp, err := ioutil.TempDir("", "TestParseExtensions")
|
||||
@ -567,12 +570,12 @@ func TestParseExtensions(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
cases := []struct {
|
||||
testID string
|
||||
config TLSExtConfig
|
||||
config peertls.TLSExtConfig
|
||||
extLen int
|
||||
certChain []*x509.Certificate
|
||||
whitelist []*x509.Certificate
|
||||
@ -581,7 +584,7 @@ func TestParseExtensions(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
"leaf whitelist signature - success",
|
||||
TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
peertls.TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
1,
|
||||
whitelistSignedChain,
|
||||
[]*x509.Certificate{whitelistSignedChain[2]},
|
||||
@ -590,25 +593,25 @@ func TestParseExtensions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"leaf whitelist signature - failure (empty whitelist)",
|
||||
TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
peertls.TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
1,
|
||||
whitelistSignedChain,
|
||||
nil,
|
||||
&ErrVerifyCAWhitelist,
|
||||
&peertls.ErrVerifyCAWhitelist,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"leaf whitelist signature - failure",
|
||||
TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
peertls.TLSExtConfig{WhitelistSignedLeaf: true},
|
||||
1,
|
||||
whitelistSignedChain,
|
||||
unrelatedChain,
|
||||
&ErrVerifyCAWhitelist,
|
||||
&peertls.ErrVerifyCAWhitelist,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"certificate revocation - single revocation ",
|
||||
TLSExtConfig{Revocation: true},
|
||||
peertls.TLSExtConfig{Revocation: true},
|
||||
1,
|
||||
revokedLeafChain,
|
||||
nil,
|
||||
@ -617,10 +620,10 @@ func TestParseExtensions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"certificate revocation - serial revocations",
|
||||
TLSExtConfig{Revocation: true},
|
||||
peertls.TLSExtConfig{Revocation: true},
|
||||
1,
|
||||
func() []*x509.Certificate {
|
||||
rev := new(Revocation)
|
||||
rev := new(peertls.Revocation)
|
||||
time.Sleep(1 * time.Second)
|
||||
_, chain, err := revokeLeaf(revokedLeafKeys, revokedLeafChain)
|
||||
assert.NoError(t, err)
|
||||
@ -636,13 +639,13 @@ func TestParseExtensions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"certificate revocation - serial revocations error (older timestamp)",
|
||||
TLSExtConfig{Revocation: true},
|
||||
peertls.TLSExtConfig{Revocation: true},
|
||||
1,
|
||||
func() []*x509.Certificate {
|
||||
keys, chain, err := newRevokedLeafChain()
|
||||
assert.NoError(t, err)
|
||||
|
||||
rev := new(Revocation)
|
||||
rev := new(peertls.Revocation)
|
||||
err = rev.Unmarshal(chain[0].ExtraExtensions[0].Value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@ -654,25 +657,25 @@ func TestParseExtensions(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = revDB.Put(chain, pkix.Extension{
|
||||
Id: ExtensionIDs[RevocationExtID],
|
||||
Id: peertls.ExtensionIDs[peertls.RevocationExtID],
|
||||
Value: revBytes,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
return chain
|
||||
}(),
|
||||
nil,
|
||||
&ErrExtension,
|
||||
ErrRevocationTimestamp,
|
||||
&peertls.ErrExtension,
|
||||
peertls.ErrRevocationTimestamp,
|
||||
},
|
||||
{
|
||||
"certificate revocation and leaf whitelist signature",
|
||||
TLSExtConfig{Revocation: true, WhitelistSignedLeaf: true},
|
||||
peertls.TLSExtConfig{Revocation: true, WhitelistSignedLeaf: true},
|
||||
2,
|
||||
func() []*x509.Certificate {
|
||||
_, chain, err := newRevokedLeafChain()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = AddSignedCertExt(whitelistSignedKeys[0], chain[0])
|
||||
err = peertls.AddSignedCertExt(whitelistSignedKeys[0], chain[0])
|
||||
assert.NoError(t, err)
|
||||
|
||||
return chain
|
||||
@ -685,12 +688,12 @@ func TestParseExtensions(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.testID, func(t *testing.T) {
|
||||
opts := ParseExtOptions{
|
||||
opts := peertls.ParseExtOptions{
|
||||
CAWhitelist: c.whitelist,
|
||||
RevDB: revDB,
|
||||
}
|
||||
|
||||
handlers := ParseExtensions(c.config, opts)
|
||||
handlers := peertls.ParseExtensions(c.config, opts)
|
||||
assert.Equal(t, c.extLen, len(handlers))
|
||||
err := handlers.VerifyFunc()(nil, [][]*x509.Certificate{c.certChain})
|
||||
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) {
|
||||
revokingKey, err := NewKey()
|
||||
revokingKey, err := peertls.NewKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
revokingTemplate, err := LeafTemplate()
|
||||
revokingTemplate, err := peertls.LeafTemplate()
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = AddRevocationExt(keys[0], chain[0], revokingCert)
|
||||
err = peertls.AddRevocationExt(keys[0], chain[0], revokingCert)
|
||||
if err != nil {
|
||||
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) {
|
||||
keys2, certs2, err := newCertChain(2)
|
||||
keys2, certs2, err := testpeertls.NewCertChain(2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ import (
|
||||
"github.com/mr-tron/base58/base58"
|
||||
"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) {
|
||||
@ -26,8 +27,9 @@ func TestNewPieceID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDerivePieceID(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
pid := NewPieceID()
|
||||
fid, err := testidentity.NewTestIdentity()
|
||||
fid, err := testidentity.NewTestIdentity(ctx)
|
||||
assert.NoError(t, err)
|
||||
did, err := pid.Derive(fid.ID.Bytes())
|
||||
assert.NoError(t, err)
|
||||
|
@ -28,9 +28,9 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"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/storj"
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"storj.io/storj/internal/identity"
|
||||
"storj.io/storj/internal/testidentity"
|
||||
"storj.io/storj/pkg/auth"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/storage/meta"
|
||||
|
Loading…
Reference in New Issue
Block a user