frostfs-sdk-go/session/session.go
Leonard Lyubich ea043f4ca3 [#190] Refactor cryptographic functionality
Remove `signature` and `util/signature` packages. Re-implement their
functionality in new `crypto` package. Generalize the approach of
digital signature computation and verification by adding `Signer` and
`PublicKey` primitives similar to standard `crypto` package. Support
already exising in protocol signature schemes.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
2022-04-19 12:55:11 +03:00

288 lines
6.1 KiB
Go

package session
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/session"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/owner"
)
// Token represents NeoFS API v2-compatible
// session token.
type Token session.Token
// NewTokenFromV2 wraps session.Token message structure
// into Token.
//
// Nil session.Token converts to nil.
func NewTokenFromV2(tV2 *session.Token) *Token {
return (*Token)(tV2)
}
// NewToken creates and returns blank Token.
//
// Defaults:
// - body: nil;
// - id: nil;
// - ownerId: nil;
// - sessionKey: nil;
// - exp: 0;
// - iat: 0;
// - nbf: 0;
func NewToken() *Token {
return NewTokenFromV2(new(session.Token))
}
// ToV2 converts Token to session.Token message structure.
//
// Nil Token converts to nil.
func (t *Token) ToV2() *session.Token {
return (*session.Token)(t)
}
func (t *Token) setBodyField(setter func(*session.TokenBody)) {
token := (*session.Token)(t)
body := token.GetBody()
if body == nil {
body = new(session.TokenBody)
token.SetBody(body)
}
setter(body)
}
// ID returns Token identifier.
func (t *Token) ID() []byte {
return (*session.Token)(t).
GetBody().
GetID()
}
// SetID sets Token identifier.
func (t *Token) SetID(v []byte) {
t.setBodyField(func(body *session.TokenBody) {
body.SetID(v)
})
}
// OwnerID returns Token's owner identifier.
func (t *Token) OwnerID() *owner.ID {
return owner.NewIDFromV2(
(*session.Token)(t).
GetBody().
GetOwnerID(),
)
}
// SetOwnerID sets Token's owner identifier.
func (t *Token) SetOwnerID(v *owner.ID) {
t.setBodyField(func(body *session.TokenBody) {
body.SetOwnerID(v.ToV2())
})
}
// SessionKey returns public key of the session
// in a binary format.
func (t *Token) SessionKey() []byte {
return (*session.Token)(t).
GetBody().
GetSessionKey()
}
// SetSessionKey sets public key of the session
// in a binary format.
func (t *Token) SetSessionKey(v []byte) {
t.setBodyField(func(body *session.TokenBody) {
body.SetSessionKey(v)
})
}
func (t *Token) setLifetimeField(f func(*session.TokenLifetime)) {
t.setBodyField(func(body *session.TokenBody) {
lt := body.GetLifetime()
if lt == nil {
lt = new(session.TokenLifetime)
body.SetLifetime(lt)
}
f(lt)
})
}
// Exp returns epoch number of the token expiration.
func (t *Token) Exp() uint64 {
return (*session.Token)(t).
GetBody().
GetLifetime().
GetExp()
}
// SetExp sets epoch number of the token expiration.
func (t *Token) SetExp(exp uint64) {
t.setLifetimeField(func(lt *session.TokenLifetime) {
lt.SetExp(exp)
})
}
// Nbf returns starting epoch number of the token.
func (t *Token) Nbf() uint64 {
return (*session.Token)(t).
GetBody().
GetLifetime().
GetNbf()
}
// SetNbf sets starting epoch number of the token.
func (t *Token) SetNbf(nbf uint64) {
t.setLifetimeField(func(lt *session.TokenLifetime) {
lt.SetNbf(nbf)
})
}
// Iat returns starting epoch number of the token.
func (t *Token) Iat() uint64 {
return (*session.Token)(t).
GetBody().
GetLifetime().
GetIat()
}
// SetIat sets the number of the epoch in which the token was issued.
func (t *Token) SetIat(iat uint64) {
t.setLifetimeField(func(lt *session.TokenLifetime) {
lt.SetIat(iat)
})
}
// Sign calculates and writes signature of the Token data.
//
// Returns signature calculation errors.
func (t *Token) Sign(key *ecdsa.PrivateKey) error {
if key == nil {
return errors.New("nil private key")
}
tV2 := (*session.Token)(t)
digest, err := tV2.GetBody().StableMarshal(nil)
if err != nil {
panic(fmt.Sprintf("unexpected error from Token.StableMarshal: %v", err))
}
var sig neofscrypto.Signature
var signer neofsecdsa.Signer
signer.SetKey(*key)
err = sig.Calculate(signer, digest)
if err != nil {
return err
}
var sigV2 refs.Signature
sig.WriteToV2(&sigV2)
tV2.SetSignature(&sigV2)
return nil
}
// VerifySignature checks if token signature is
// presented and valid.
func (t *Token) VerifySignature() bool {
tV2 := (*session.Token)(t)
digest, err := tV2.GetBody().StableMarshal(nil)
if err != nil {
panic(fmt.Sprintf("unexpected error from Token.StableMarshal: %v", err))
}
sigV2 := tV2.GetSignature()
if sigV2 == nil {
return false
}
var sig neofscrypto.Signature
sig.ReadFromV2(*sigV2)
return sig.Verify(digest)
}
// SetContext sets context of the Token.
//
// Supported contexts:
// - *ContainerContext,
// - *ObjectContext.
//
// Resets context if it is not supported.
func (t *Token) SetContext(v interface{}) {
var cV2 session.TokenContext
switch c := v.(type) {
case *ContainerContext:
cV2 = c.ToV2()
case *ObjectContext:
cV2 = c.ToV2()
}
t.setBodyField(func(body *session.TokenBody) {
body.SetContext(cV2)
})
}
// Context returns context of the Token.
//
// Supports same contexts as SetContext.
//
// Returns nil if context is not supported.
func (t *Token) Context() interface{} {
switch v := (*session.Token)(t).
GetBody().
GetContext(); c := v.(type) {
default:
return nil
case *session.ContainerSessionContext:
return NewContainerContextFromV2(c)
case *session.ObjectSessionContext:
return NewObjectContextFromV2(c)
}
}
// GetContainerContext is a helper function that casts
// Token context to ContainerContext.
//
// Returns nil if context is not a ContainerContext.
func GetContainerContext(t *Token) *ContainerContext {
c, _ := t.Context().(*ContainerContext)
return c
}
// Marshal marshals Token into a protobuf binary form.
func (t *Token) Marshal() ([]byte, error) {
return (*session.Token)(t).
StableMarshal(nil)
}
// Unmarshal unmarshals protobuf binary representation of Token.
func (t *Token) Unmarshal(data []byte) error {
return (*session.Token)(t).
Unmarshal(data)
}
// MarshalJSON encodes Token to protobuf JSON format.
func (t *Token) MarshalJSON() ([]byte, error) {
return (*session.Token)(t).
MarshalJSON()
}
// UnmarshalJSON decodes Token from protobuf JSON format.
func (t *Token) UnmarshalJSON(data []byte) error {
return (*session.Token)(t).
UnmarshalJSON(data)
}