forked from TrueCloudLab/frostfs-api-go
Merge pull request #89 from nspcc-dev/feature/bearer-token-definition
service: define BearerToken interface
This commit is contained in:
commit
73e90be443
3 changed files with 302 additions and 0 deletions
96
service/bearer.go
Normal file
96
service/bearer.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"io"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||
)
|
||||
|
||||
type signedBearerToken struct {
|
||||
BearerToken
|
||||
}
|
||||
|
||||
const fixedBearerTokenDataSize = 0 +
|
||||
refs.OwnerIDSize +
|
||||
8
|
||||
|
||||
// NewSignedBearerToken wraps passed BearerToken in a component suitable for signing.
|
||||
//
|
||||
// Result can be used in AddSignatureWithKey function.
|
||||
func NewSignedBearerToken(token BearerToken) DataWithSignKeyAccumulator {
|
||||
return &signedBearerToken{
|
||||
BearerToken: token,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVerifiedBearerToken wraps passed SessionToken in a component suitable for signature verification.
|
||||
//
|
||||
// Result can be used in VerifySignatureWithKey function.
|
||||
func NewVerifiedBearerToken(token BearerToken) DataWithSignature {
|
||||
return &signedBearerToken{
|
||||
BearerToken: token,
|
||||
}
|
||||
}
|
||||
|
||||
// AddSignKey calls a Signature field setter and an OwnerKey field setter with corresponding arguments.
|
||||
func (s signedBearerToken) AddSignKey(sig []byte, key *ecdsa.PublicKey) {
|
||||
if s.BearerToken != nil {
|
||||
s.SetSignature(sig)
|
||||
|
||||
s.SetOwnerKey(
|
||||
crypto.MarshalPublicKey(key),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// SignedData returns token information in a binary representation.
|
||||
func (s signedBearerToken) SignedData() ([]byte, error) {
|
||||
return SignedDataFromReader(s)
|
||||
}
|
||||
|
||||
// SignedDataSize returns the length of signed token information slice.
|
||||
func (s signedBearerToken) SignedDataSize() int {
|
||||
return bearerTokenInfoSize(s.BearerToken)
|
||||
}
|
||||
|
||||
// ReadSignedData copies a binary representation of the token information to passed buffer.
|
||||
//
|
||||
// If buffer length is less than required, io.ErrUnexpectedEOF returns.
|
||||
func (s signedBearerToken) ReadSignedData(p []byte) (int, error) {
|
||||
sz := s.SignedDataSize()
|
||||
if len(p) < sz {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
copyBearerTokenSignedData(p, s.BearerToken)
|
||||
|
||||
return sz, nil
|
||||
}
|
||||
|
||||
func bearerTokenInfoSize(v ACLRulesSource) int {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return fixedBearerTokenDataSize + len(v.GetACLRules())
|
||||
}
|
||||
|
||||
// Fills passed buffer with signing token information bytes.
|
||||
// Does not check buffer length, it is understood that enough space is allocated in it.
|
||||
//
|
||||
// If passed BearerTokenInfo, buffer remains unchanged.
|
||||
func copyBearerTokenSignedData(buf []byte, token BearerTokenInfo) {
|
||||
if token == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var off int
|
||||
|
||||
off += copy(buf[off:], token.GetACLRules())
|
||||
|
||||
off += copy(buf[off:], token.GetOwnerID().Bytes())
|
||||
|
||||
tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch())
|
||||
off += 8
|
||||
}
|
172
service/bearer_test.go
Normal file
172
service/bearer_test.go
Normal file
|
@ -0,0 +1,172 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-crypto/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testBearerToken struct {
|
||||
aclRules []byte
|
||||
expEpoch uint64
|
||||
owner OwnerID
|
||||
key []byte
|
||||
sig []byte
|
||||
}
|
||||
|
||||
func (s testBearerToken) GetACLRules() []byte {
|
||||
return s.aclRules
|
||||
}
|
||||
|
||||
func (s *testBearerToken) SetACLRules(v []byte) {
|
||||
s.aclRules = v
|
||||
}
|
||||
|
||||
func (s testBearerToken) ExpirationEpoch() uint64 {
|
||||
return s.expEpoch
|
||||
}
|
||||
|
||||
func (s *testBearerToken) SetExpirationEpoch(v uint64) {
|
||||
s.expEpoch = v
|
||||
}
|
||||
|
||||
func (s testBearerToken) GetOwnerID() OwnerID {
|
||||
return s.owner
|
||||
}
|
||||
|
||||
func (s *testBearerToken) SetOwnerID(v OwnerID) {
|
||||
s.owner = v
|
||||
}
|
||||
|
||||
func (s testBearerToken) GetOwnerKey() []byte {
|
||||
return s.key
|
||||
}
|
||||
|
||||
func (s *testBearerToken) SetOwnerKey(v []byte) {
|
||||
s.key = v
|
||||
}
|
||||
|
||||
func (s testBearerToken) GetSignature() []byte {
|
||||
return s.sig
|
||||
}
|
||||
|
||||
func (s *testBearerToken) SetSignature(v []byte) {
|
||||
s.sig = v
|
||||
}
|
||||
|
||||
func TestBearerTokenMsgGettersSetters(t *testing.T) {
|
||||
var tok BearerToken = new(testBearerToken)
|
||||
|
||||
{ // ACLRules
|
||||
rules := []byte{1, 2, 3}
|
||||
|
||||
tok.SetACLRules(rules)
|
||||
|
||||
require.Equal(t, rules, tok.GetACLRules())
|
||||
}
|
||||
|
||||
{ // OwnerID
|
||||
ownerID := OwnerID{}
|
||||
_, err := rand.Read(ownerID[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
tok.SetOwnerID(ownerID)
|
||||
|
||||
require.Equal(t, ownerID, tok.GetOwnerID())
|
||||
}
|
||||
|
||||
{ // ValidUntil
|
||||
e := uint64(5)
|
||||
|
||||
tok.SetExpirationEpoch(e)
|
||||
|
||||
require.Equal(t, e, tok.ExpirationEpoch())
|
||||
}
|
||||
|
||||
{ // OwnerKey
|
||||
key := make([]byte, 10)
|
||||
_, err := rand.Read(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
tok.SetOwnerKey(key)
|
||||
|
||||
require.Equal(t, key, tok.GetOwnerKey())
|
||||
}
|
||||
|
||||
{ // Signature
|
||||
sig := make([]byte, 10)
|
||||
_, err := rand.Read(sig)
|
||||
require.NoError(t, err)
|
||||
|
||||
tok.SetSignature(sig)
|
||||
|
||||
require.Equal(t, sig, tok.GetSignature())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerifyBearerToken(t *testing.T) {
|
||||
var token BearerToken = new(testBearerToken)
|
||||
|
||||
// create private key for signing
|
||||
sk := test.DecodeKey(0)
|
||||
pk := &sk.PublicKey
|
||||
|
||||
rules := []byte{1, 2, 3}
|
||||
token.SetACLRules(rules)
|
||||
|
||||
ownerID := OwnerID{}
|
||||
_, err := rand.Read(ownerID[:])
|
||||
require.NoError(t, err)
|
||||
token.SetOwnerID(ownerID)
|
||||
|
||||
fEpoch := uint64(2)
|
||||
token.SetExpirationEpoch(fEpoch)
|
||||
|
||||
signedToken := NewSignedBearerToken(token)
|
||||
verifiedToken := NewVerifiedBearerToken(token)
|
||||
|
||||
// sign and verify token
|
||||
require.NoError(t, AddSignatureWithKey(sk, signedToken))
|
||||
require.NoError(t, VerifySignatureWithKey(pk, verifiedToken))
|
||||
|
||||
items := []struct {
|
||||
corrupt func()
|
||||
restore func()
|
||||
}{
|
||||
{ // ACLRules
|
||||
corrupt: func() {
|
||||
token.SetACLRules(append(rules, 1))
|
||||
},
|
||||
restore: func() {
|
||||
token.SetACLRules(rules)
|
||||
},
|
||||
},
|
||||
{ // Owner ID
|
||||
corrupt: func() {
|
||||
ownerID[0]++
|
||||
token.SetOwnerID(ownerID)
|
||||
},
|
||||
restore: func() {
|
||||
ownerID[0]--
|
||||
token.SetOwnerID(ownerID)
|
||||
},
|
||||
},
|
||||
{ // Expiration epoch
|
||||
corrupt: func() {
|
||||
token.SetExpirationEpoch(fEpoch + 1)
|
||||
},
|
||||
restore: func() {
|
||||
token.SetExpirationEpoch(fEpoch)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range items {
|
||||
v.corrupt()
|
||||
require.Error(t, VerifySignatureWithKey(pk, verifiedToken))
|
||||
v.restore()
|
||||
require.NoError(t, VerifySignatureWithKey(pk, verifiedToken))
|
||||
}
|
||||
}
|
|
@ -267,3 +267,37 @@ type RequestVerifyData interface {
|
|||
RequestData
|
||||
SignKeyPairSource
|
||||
}
|
||||
|
||||
// ACLRulesSource is an interface of the container of binary extended ACL rules with read access.
|
||||
type ACLRulesSource interface {
|
||||
GetACLRules() []byte
|
||||
}
|
||||
|
||||
// ACLRulesContainer is an interface of the container of binary extended ACL rules.
|
||||
type ACLRulesContainer interface {
|
||||
ACLRulesSource
|
||||
SetACLRules([]byte)
|
||||
}
|
||||
|
||||
// BearerTokenInfo is an interface of a fixed set of Bearer token information value containers.
|
||||
// Contains:
|
||||
// - binary extended ACL rules;
|
||||
// - expiration epoch number;
|
||||
// - ID of the token's owner.
|
||||
type BearerTokenInfo interface {
|
||||
ACLRulesContainer
|
||||
ExpirationEpochContainer
|
||||
OwnerIDContainer
|
||||
}
|
||||
|
||||
// BearerToken is an interface of Bearer token information and key-signature pair.
|
||||
type BearerToken interface {
|
||||
BearerTokenInfo
|
||||
OwnerKeyContainer
|
||||
SignatureContainer
|
||||
}
|
||||
|
||||
// BearerTokenSource is an interface of the container of a BearerToken with read access.
|
||||
type BearerTokenSource interface {
|
||||
GetBearerToken() BearerToken
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue