Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

session: Improvements and test coverage #612

Merged
merged 8 commits into from
Sep 24, 2024
57 changes: 30 additions & 27 deletions bearer/bearer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Token struct {
iat, nbf, exp uint64

sigSet bool
sig refs.Signature
sig neofscrypto.Signature
}

// reads Token from the acl.BearerToken message. If checkFieldPresence is set,
Expand Down Expand Up @@ -84,7 +84,9 @@ func (b *Token) readFromV2(m acl.BearerToken, checkFieldPresence bool) error {

sig := m.GetSignature()
if b.sigSet = sig != nil; sig != nil {
b.sig = *sig
if err = b.sig.ReadFromV2(*sig); err != nil {
return fmt.Errorf("invalid body signature: %w", err)
}
} else if checkFieldPresence {
return errors.New("missing body signature")
}
Expand Down Expand Up @@ -150,7 +152,8 @@ func (b Token) WriteToV2(m *acl.BearerToken) {
var sig *refs.Signature

if b.sigSet {
sig = &b.sig
sig = new(refs.Signature)
b.sig.WriteToV2(sig)
}

m.SetSignature(sig)
Expand Down Expand Up @@ -278,17 +281,11 @@ func (b Token) AssertUser(id user.ID) bool {
func (b *Token) Sign(signer user.Signer) error {
b.SetIssuer(signer.UserID())

var sig neofscrypto.Signature

err := sig.Calculate(signer, b.signedData())
if err != nil {
return err
err := b.sig.Calculate(signer, b.signedData())
if err == nil {
b.sigSet = true
}

sig.WriteToV2(&b.sig)
b.sigSet = true

return nil
return err
}

// SignedData returns actual payload to sign.
Expand All @@ -311,20 +308,28 @@ func (b *Token) UnmarshalSignedData(data []byte) error {
return b.readFromV2(tok, false)
}

// AttachSignature attaches given signature to the Token. Use [Token.SignedData]
// for calculation. If signature instance itself is not needed, use
// [Token.Sign].
func (b *Token) AttachSignature(sig neofscrypto.Signature) {
b.sig, b.sigSet = sig, true
}

// Signature returns Token signature. If the signature is missing, false is
// returned. Use [Token.SignedData] for verification. If signature instance
// itself is not needed, use [Token.VerifySignature].
func (b Token) Signature() (neofscrypto.Signature, bool) {
return b.sig, b.sigSet
}

// VerifySignature checks if Token signature is presented and valid.
//
// Zero Token fails the check.
//
// See also Sign.
func (b Token) VerifySignature() bool {
if !b.sigSet {
return false
}

var sig neofscrypto.Signature

// TODO: (#233) check owner<->key relation
return sig.ReadFromV2(b.sig) == nil && sig.Verify(b.signedData())
return b.sigSet && b.sig.Verify(b.signedData())
}

// Marshal encodes Token into a binary format of the NeoFS API protocol
Expand Down Expand Up @@ -392,11 +397,11 @@ func (b *Token) UnmarshalJSON(data []byte) error {
// Make a copy if you need to change it.
//
// See also [Token.ResolveIssuer].
// Deprecated: use [Token.Signature] instead.
func (b Token) SigningKeyBytes() []byte {
if b.sigSet {
return b.sig.GetKey()
if sig, ok := b.Signature(); ok {
return sig.PublicKeyBytes()
}

return nil
}

Expand Down Expand Up @@ -427,10 +432,8 @@ func (b Token) ResolveIssuer() user.ID {
}

var usr user.ID
binKey := b.SigningKeyBytes()

if len(binKey) != 0 {
if err := idFromKey(&usr, binKey); err != nil {
if b.sigSet {
if err := idFromKey(&usr, b.sig.PublicKeyBytes()); err != nil {
usr = user.ID{}
}
}
Expand Down
5 changes: 4 additions & 1 deletion bearer/bearer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,10 @@ func TestToken_ReadFromV2(t *testing.T) {

require.NoError(t, val.ReadFromV2(m))
require.True(t, val.VerifySignature())
require.Equal(t, sig.GetKey(), val.SigningKeyBytes())
s, ok := val.Signature()
require.True(t, ok)
require.True(t, s.Verify(body.StableMarshal(nil)))
require.Equal(t, sig.GetKey(), s.PublicKeyBytes())
}

func TestResolveIssuer(t *testing.T) {
Expand Down
73 changes: 54 additions & 19 deletions client/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type PrmContainerPut struct {

sessionSet bool
session session.Container

sigSet bool
sig neofscrypto.Signature
}

// WithinSession specifies session within which container should be saved.
Expand All @@ -51,6 +54,13 @@ func (x *PrmContainerPut) WithinSession(s session.Container) {
x.sessionSet = true
}

// AttachSignature allows to attach pre-calculated container signature and free
// [Client.ContainerPut] from the calculation. The sig must have
// [neofscrypto.ECDSA_DETERMINISTIC_SHA256] scheme.
func (x *PrmContainerPut) AttachSignature(sig neofscrypto.Signature) {
x.sig, x.sigSet = sig, true
}

// ContainerPut sends request to save container in NeoFS.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
Expand All @@ -66,6 +76,8 @@ func (x *PrmContainerPut) WithinSession(s session.Container) {
//
// Signer is required and must not be nil. The account corresponding to the specified Signer will be charged for the operation.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// If signature already exists, use [PrmContainerPut.AttachSignature]:
// then signer will not be used.
//
// Return errors:
// - [ErrMissingSigner]
Expand All @@ -82,16 +94,16 @@ func (c *Client) ContainerPut(ctx context.Context, cont container.Container, sig
var cnr v2container.Container
cont.WriteToV2(&cnr)

var sig neofscrypto.Signature
err = cont.CalculateSignature(&sig, signer)
if err != nil {
err = fmt.Errorf("calculate container signature: %w", err)
return cid.ID{}, err
if !prm.sigSet {
if err = cont.CalculateSignature(&prm.sig, signer); err != nil {
err = fmt.Errorf("calculate container signature: %w", err)
return cid.ID{}, err
}
}

var sigv2 refs.Signature

sig.WriteToV2(&sigv2)
prm.sig.WriteToV2(&sigv2)

// form request body
reqBody := new(v2container.PutRequestBody)
Expand Down Expand Up @@ -290,6 +302,9 @@ type PrmContainerDelete struct {

tokSet bool
tok session.Container

sigSet bool
sig neofscrypto.Signature
}

// WithinSession specifies session within which container should be removed.
Expand All @@ -303,6 +318,13 @@ func (x *PrmContainerDelete) WithinSession(tok session.Container) {
x.tokSet = true
}

// AttachSignature allows to attach pre-calculated container ID signature and
// free [Client.ContainerDelete] from the calculation. The sig must have
// [neofscrypto.ECDSA_DETERMINISTIC_SHA256] scheme.
func (x *PrmContainerDelete) AttachSignature(sig neofscrypto.Signature) {
x.sig, x.sigSet = sig, true
}

// ContainerDelete sends request to remove the NeoFS container.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
Expand All @@ -317,6 +339,8 @@ func (x *PrmContainerDelete) WithinSession(tok session.Container) {
//
// Signer is required and must not be nil. The account corresponding to the specified Signer will be charged for the operation.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// If signature already exists, use [PrmContainerDelete.AttachSignature]:
// then signer will not be used.
//
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
// Return errors:
Expand All @@ -339,16 +363,16 @@ func (c *Client) ContainerDelete(ctx context.Context, id cid.ID, signer neofscry
// don't get confused with stable marshaled protobuf container.ID structure
data := cidV2.GetValue()

var sig neofscrypto.Signature
err = sig.Calculate(signer, data)
if err != nil {
err = fmt.Errorf("calculate signature: %w", err)
return err
if !prm.sigSet {
if err = prm.sig.Calculate(signer, data); err != nil {
err = fmt.Errorf("calculate signature: %w", err)
return err
}
}

var sigv2 refs.Signature

sig.WriteToV2(&sigv2)
prm.sig.WriteToV2(&sigv2)

// form request body
reqBody := new(v2container.DeleteRequestBody)
Expand Down Expand Up @@ -463,6 +487,9 @@ type PrmContainerSetEACL struct {

sessionSet bool
session session.Container

sigSet bool
sig neofscrypto.Signature
}

// WithinSession specifies session within which extended ACL of the container
Expand All @@ -481,6 +508,13 @@ func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
x.sessionSet = true
}

// AttachSignature allows to attach pre-calculated eACL signature and free
// [Client.ContainerSetEACL] from the calculation. The sig must have
// [neofscrypto.ECDSA_DETERMINISTIC_SHA256] scheme.
func (x *PrmContainerSetEACL) AttachSignature(sig neofscrypto.Signature) {
x.sig, x.sigSet = sig, true
}

// ContainerSetEACL sends request to update eACL table of the NeoFS container.
//
// Any errors (local or remote, including returned status codes) are returned as Go errors,
Expand All @@ -493,6 +527,8 @@ func (x *PrmContainerSetEACL) WithinSession(s session.Container) {
//
// Signer is required and must not be nil. The account corresponding to the specified Signer will be charged for the operation.
// Signer's scheme MUST be neofscrypto.ECDSA_DETERMINISTIC_SHA256. For example, you can use neofsecdsa.SignerRFC6979.
// If signature already exists, use [PrmContainerSetEACL.AttachSignature]:
// then signer will not be used.
//
// Return errors:
// - [ErrMissingEACLContainer]
Expand All @@ -516,17 +552,16 @@ func (c *Client) ContainerSetEACL(ctx context.Context, table eacl.Table, signer

// sign the eACL table
eaclV2 := table.ToV2()

var sig neofscrypto.Signature
err = sig.CalculateMarshalled(signer, eaclV2, nil)
if err != nil {
err = fmt.Errorf("calculate signature: %w", err)
return err
if !prm.sigSet {
if err = prm.sig.CalculateMarshalled(signer, eaclV2, nil); err != nil {
err = fmt.Errorf("calculate signature: %w", err)
return err
}
}

var sigv2 refs.Signature

sig.WriteToV2(&sigv2)
prm.sig.WriteToV2(&sigv2)

// form request body
reqBody := new(v2container.SetExtendedACLRequestBody)
Expand Down
83 changes: 54 additions & 29 deletions crypto/example_test.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,68 @@
package neofscrypto_test

import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"fmt"

neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
)

func ExampleSignature_Calculate() {
var signer neofscrypto.Signer
var data []byte

// instantiate Signer
// select data to be signed

// instantiate signer
k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
// get data to be signed
data := []byte("Hello, world!")
var sig neofscrypto.Signature
_ = sig.Calculate(signer, data)

// attach signature to the request
for _, signer := range []neofscrypto.Signer{
neofsecdsa.Signer(*k),
neofsecdsa.SignerRFC6979(*k),
neofsecdsa.SignerWalletConnect(*k),
} {
// calculate signature
_ = sig.Calculate(signer, data)
fmt.Printf("scheme: %d; public key: %x; signature: %x\n",
sig.Scheme(), sig.PublicKeyBytes(), sig.Value())
// attach the signature to the request or data structure
}
}

// PublicKey allows to verify signatures.
func ExampleSignature_Verify() {
var sig neofscrypto.Signature

var data []byte
sig.Verify(data)
}

// Instances can be also used to process NeoFS API V2 protocol messages with [https:/nspcc-dev/neofs-api] package.
func ExampleSignature_marshalling() {
// import "github.com/nspcc-dev/neofs-api-go/v2/refs"

// On the client side.

var sig neofscrypto.Signature
var msg refs.Signature
sig.WriteToV2(&msg)
// *send message*

// On the server side.
// get data to be verified
data := []byte("Hello, world!")

_ = sig.ReadFromV2(msg)
for _, tc := range []struct {
scheme neofscrypto.Scheme
pub string
val string
}{
{
scheme: neofscrypto.ECDSA_SHA512,
pub: "034f10e101921787b4bd69098350cdfea4c5493f6b1d0f8829276b2a460988bd6f",
val: "0401cf064093860d4996a9e22df3638c6bcebbd326e82f01cf9c37942a652a79a4fea18ec3777fda6c999162a9414180ce8e11aad97ede7a5da930faebf195aaa8",
},
{
scheme: neofscrypto.ECDSA_DETERMINISTIC_SHA256,
pub: "034f10e101921787b4bd69098350cdfea4c5493f6b1d0f8829276b2a460988bd6f",
val: "274e6a5ebae221f2c57ea9b9429c6410f31c17580a1d579088ee1839539a044f9cb89886552ed8b21b3c618ee223a061e9957518c327c81397fed074bd31d796",
},
{
scheme: neofscrypto.ECDSA_WALLETCONNECT,
pub: "034f10e101921787b4bd69098350cdfea4c5493f6b1d0f8829276b2a460988bd6f",
val: "b41afc64b7a77b15ce269f9a52e39131dbe06e827cd469010ee575b7d8e4df6a627c7c79673a292f32066640b9c9b487a5aee74c45dcef8e7a0da4a2a20e9bae018647a3a43e253c26749e964593e542",
},
} {
// get signature
pub, _ := hex.DecodeString(tc.pub)
val, _ := hex.DecodeString(tc.val)
sig := neofscrypto.NewSignatureFromRawKey(tc.scheme, pub, val)
// verify the signature
valid := sig.Verify(data)
fmt.Printf("valid: %t, scheme: %d; public key: %s; signature: %s\n",
valid, tc.scheme, tc.pub, tc.val)
}
}
Loading
Loading