git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/session/container_test.go (about)

     1  package session_test
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"math"
     8  	mrand "math/rand"
     9  	"testing"
    10  
    11  	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
    12  	v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
    13  	cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
    14  	frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
    15  	frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
    16  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
    17  	sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session/test"
    18  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
    19  	usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
    20  	"github.com/google/uuid"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestContainerProtocolV2(t *testing.T) {
    25  	var validV2 v2session.Token
    26  
    27  	var body v2session.TokenBody
    28  	validV2.SetBody(&body)
    29  
    30  	// ID
    31  	id := uuid.New()
    32  	binID, err := id.MarshalBinary()
    33  	require.NoError(t, err)
    34  	restoreID := func() {
    35  		body.SetID(binID)
    36  	}
    37  	restoreID()
    38  
    39  	// Owner
    40  	usr := usertest.ID()
    41  	var usrV2 refs.OwnerID
    42  	usr.WriteToV2(&usrV2)
    43  	restoreUser := func() {
    44  		body.SetOwnerID(&usrV2)
    45  	}
    46  	restoreUser()
    47  
    48  	// Lifetime
    49  	var lifetime v2session.TokenLifetime
    50  	lifetime.SetIat(1)
    51  	lifetime.SetNbf(2)
    52  	lifetime.SetExp(3)
    53  	restoreLifetime := func() {
    54  		body.SetLifetime(&lifetime)
    55  	}
    56  	restoreLifetime()
    57  
    58  	// Session key
    59  	signer := randSigner()
    60  	authKey := frostfsecdsa.PublicKey(signer.PublicKey)
    61  	binAuthKey := make([]byte, authKey.MaxEncodedSize())
    62  	binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)]
    63  	restoreAuthKey := func() {
    64  		body.SetSessionKey(binAuthKey)
    65  	}
    66  	restoreAuthKey()
    67  
    68  	// Context
    69  	cnr := cidtest.ID()
    70  	var cnrV2 refs.ContainerID
    71  	cnr.WriteToV2(&cnrV2)
    72  	var cCnr v2session.ContainerSessionContext
    73  	restoreCtx := func() {
    74  		cCnr.SetContainerID(&cnrV2)
    75  		cCnr.SetWildcard(false)
    76  		body.SetContext(&cCnr)
    77  	}
    78  	restoreCtx()
    79  
    80  	// Signature
    81  	var sig refs.Signature
    82  	restoreSig := func() {
    83  		validV2.SetSignature(&sig)
    84  	}
    85  	restoreSig()
    86  
    87  	// TODO(@cthulhu-rider): #260 use functionality for message corruption
    88  
    89  	for _, testcase := range []struct {
    90  		name      string
    91  		corrupt   []func()
    92  		restore   func()
    93  		assert    func(session.Container)
    94  		breakSign func(*v2session.Token)
    95  	}{
    96  		{
    97  			name: "Signature",
    98  			corrupt: []func(){
    99  				func() {
   100  					validV2.SetSignature(nil)
   101  				},
   102  			},
   103  			restore: restoreSig,
   104  		},
   105  		{
   106  			name: "ID",
   107  			corrupt: []func(){
   108  				func() {
   109  					body.SetID([]byte{1, 2, 3})
   110  				},
   111  				func() {
   112  					id, err := uuid.NewDCEPerson()
   113  					require.NoError(t, err)
   114  					bindID, err := id.MarshalBinary()
   115  					require.NoError(t, err)
   116  					body.SetID(bindID)
   117  				},
   118  			},
   119  			restore: restoreID,
   120  			assert: func(val session.Container) {
   121  				require.Equal(t, id, val.ID())
   122  			},
   123  			breakSign: func(m *v2session.Token) {
   124  				id := m.GetBody().GetID()
   125  				id[len(id)-1]++
   126  			},
   127  		},
   128  		{
   129  			name: "User",
   130  			corrupt: []func(){
   131  				func() {
   132  					var brokenUsrV2 refs.OwnerID
   133  					brokenUsrV2.SetValue(append(usrV2.GetValue(), 1))
   134  					body.SetOwnerID(&brokenUsrV2)
   135  				},
   136  			},
   137  			restore: restoreUser,
   138  			assert: func(val session.Container) {
   139  				require.Equal(t, usr, val.Issuer())
   140  			},
   141  			breakSign: func(m *v2session.Token) {
   142  				id := m.GetBody().GetOwnerID().GetValue()
   143  				copy(id, usertest.ID().WalletBytes())
   144  			},
   145  		},
   146  		{
   147  			name: "Lifetime",
   148  			corrupt: []func(){
   149  				func() {
   150  					body.SetLifetime(nil)
   151  				},
   152  			},
   153  			restore: restoreLifetime,
   154  			assert: func(val session.Container) {
   155  				require.True(t, val.InvalidAt(1))
   156  				require.False(t, val.InvalidAt(2))
   157  				require.False(t, val.InvalidAt(3))
   158  				require.True(t, val.InvalidAt(4))
   159  			},
   160  			breakSign: func(m *v2session.Token) {
   161  				lt := m.GetBody().GetLifetime()
   162  				lt.SetIat(lt.GetIat() + 1)
   163  			},
   164  		},
   165  		{
   166  			name: "Auth key",
   167  			corrupt: []func(){
   168  				func() {
   169  					body.SetSessionKey(nil)
   170  				},
   171  				func() {
   172  					body.SetSessionKey([]byte{})
   173  				},
   174  			},
   175  			restore: restoreAuthKey,
   176  			assert: func(val session.Container) {
   177  				require.True(t, val.AssertAuthKey(&authKey))
   178  			},
   179  			breakSign: func(m *v2session.Token) {
   180  				body := m.GetBody()
   181  				key := body.GetSessionKey()
   182  				cp := bytes.Clone(key)
   183  				cp[len(cp)-1]++
   184  				body.SetSessionKey(cp)
   185  			},
   186  		},
   187  		{
   188  			name: "Context",
   189  			corrupt: []func(){
   190  				func() {
   191  					body.SetContext(nil)
   192  				},
   193  				func() {
   194  					cCnr.SetWildcard(true)
   195  				},
   196  				func() {
   197  					cCnr.SetContainerID(nil)
   198  				},
   199  				func() {
   200  					var brokenCnr refs.ContainerID
   201  					brokenCnr.SetValue(append(cnrV2.GetValue(), 1))
   202  					cCnr.SetContainerID(&brokenCnr)
   203  				},
   204  			},
   205  			restore: restoreCtx,
   206  			assert: func(val session.Container) {
   207  				require.True(t, val.AppliedTo(cnr))
   208  				require.False(t, val.AppliedTo(cidtest.ID()))
   209  			},
   210  			breakSign: func(m *v2session.Token) {
   211  				cnr := m.GetBody().GetContext().(*v2session.ContainerSessionContext).ContainerID().GetValue()
   212  				cnr[len(cnr)-1]++
   213  			},
   214  		},
   215  	} {
   216  		var val session.Container
   217  
   218  		for i, corrupt := range testcase.corrupt {
   219  			corrupt()
   220  			require.Error(t, val.ReadFromV2(validV2), testcase.name, fmt.Sprintf("corrupt #%d", i))
   221  
   222  			testcase.restore()
   223  			require.NoError(t, val.ReadFromV2(validV2), testcase.name)
   224  
   225  			if testcase.assert != nil {
   226  				testcase.assert(val)
   227  			}
   228  
   229  			if testcase.breakSign != nil {
   230  				require.NoError(t, val.Sign(signer), testcase.name)
   231  				require.True(t, val.VerifySignature(), testcase.name)
   232  
   233  				var signedV2 v2session.Token
   234  				val.WriteToV2(&signedV2)
   235  
   236  				var restored session.Container
   237  				require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
   238  				require.True(t, restored.VerifySignature(), testcase.name)
   239  
   240  				testcase.breakSign(&signedV2)
   241  
   242  				require.NoError(t, restored.ReadFromV2(signedV2), testcase.name)
   243  				require.False(t, restored.VerifySignature(), testcase.name)
   244  			}
   245  		}
   246  	}
   247  }
   248  
   249  func TestContainer_WriteToV2(t *testing.T) {
   250  	var val session.Container
   251  
   252  	assert := func(baseAssert func(v2session.Token)) {
   253  		var m v2session.Token
   254  		val.WriteToV2(&m)
   255  		baseAssert(m)
   256  	}
   257  
   258  	// ID
   259  	id := uuid.New()
   260  
   261  	binID, err := id.MarshalBinary()
   262  	require.NoError(t, err)
   263  
   264  	val.SetID(id)
   265  	assert(func(m v2session.Token) {
   266  		require.Equal(t, binID, m.GetBody().GetID())
   267  	})
   268  
   269  	// Owner/Signature
   270  	signer := randSigner()
   271  
   272  	require.NoError(t, val.Sign(signer))
   273  
   274  	var usr user.ID
   275  	user.IDFromKey(&usr, signer.PublicKey)
   276  
   277  	var usrV2 refs.OwnerID
   278  	usr.WriteToV2(&usrV2)
   279  
   280  	assert(func(m v2session.Token) {
   281  		require.Equal(t, &usrV2, m.GetBody().GetOwnerID())
   282  
   283  		sig := m.GetSignature()
   284  		require.NotZero(t, sig.GetKey())
   285  		require.NotZero(t, sig.GetSign())
   286  	})
   287  
   288  	// Lifetime
   289  	const iat, nbf, exp = 1, 2, 3
   290  	val.SetIat(iat)
   291  	val.SetNbf(nbf)
   292  	val.SetExp(exp)
   293  
   294  	assert(func(m v2session.Token) {
   295  		lt := m.GetBody().GetLifetime()
   296  		require.EqualValues(t, iat, lt.GetIat())
   297  		require.EqualValues(t, nbf, lt.GetNbf())
   298  		require.EqualValues(t, exp, lt.GetExp())
   299  	})
   300  
   301  	// Context
   302  	assert(func(m v2session.Token) {
   303  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   304  		require.True(t, ok)
   305  		require.True(t, cCnr.Wildcard())
   306  		require.Zero(t, cCnr.ContainerID())
   307  	})
   308  
   309  	cnr := cidtest.ID()
   310  
   311  	var cnrV2 refs.ContainerID
   312  	cnr.WriteToV2(&cnrV2)
   313  
   314  	val.ApplyOnlyTo(cnr)
   315  
   316  	assert(func(m v2session.Token) {
   317  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   318  		require.True(t, ok)
   319  		require.False(t, cCnr.Wildcard())
   320  		require.Equal(t, &cnrV2, cCnr.ContainerID())
   321  	})
   322  }
   323  
   324  func TestContainer_ApplyOnlyTo(t *testing.T) {
   325  	var val session.Container
   326  	var m v2session.Token
   327  	filled := sessiontest.Container()
   328  
   329  	assertDefaults := func() {
   330  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   331  		require.True(t, ok)
   332  		require.True(t, cCnr.Wildcard())
   333  		require.Zero(t, cCnr.ContainerID())
   334  	}
   335  
   336  	assertBinary := func(baseAssert func()) {
   337  		val2 := filled
   338  
   339  		require.NoError(t, val2.Unmarshal(val.Marshal()))
   340  		baseAssert()
   341  	}
   342  
   343  	assertJSON := func(baseAssert func()) {
   344  		val2 := filled
   345  
   346  		jd, err := val.MarshalJSON()
   347  		require.NoError(t, err)
   348  
   349  		require.NoError(t, val2.UnmarshalJSON(jd))
   350  		baseAssert()
   351  	}
   352  
   353  	val.WriteToV2(&m)
   354  
   355  	assertDefaults()
   356  	assertBinary(assertDefaults)
   357  	assertJSON(assertDefaults)
   358  
   359  	// set value
   360  
   361  	cnr := cidtest.ID()
   362  
   363  	var cnrV2 refs.ContainerID
   364  	cnr.WriteToV2(&cnrV2)
   365  
   366  	val.ApplyOnlyTo(cnr)
   367  
   368  	val.WriteToV2(&m)
   369  
   370  	assertCnr := func() {
   371  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   372  		require.True(t, ok)
   373  		require.False(t, cCnr.Wildcard())
   374  		require.Equal(t, &cnrV2, cCnr.ContainerID())
   375  	}
   376  
   377  	assertCnr()
   378  	assertBinary(assertCnr)
   379  	assertJSON(assertCnr)
   380  }
   381  
   382  func TestContainer_AppliedTo(t *testing.T) {
   383  	var x session.Container
   384  
   385  	cnr1 := cidtest.ID()
   386  	cnr2 := cidtest.ID()
   387  
   388  	require.True(t, x.AppliedTo(cnr1))
   389  	require.True(t, x.AppliedTo(cnr2))
   390  
   391  	x.ApplyOnlyTo(cnr1)
   392  
   393  	require.True(t, x.AppliedTo(cnr1))
   394  	require.False(t, x.AppliedTo(cnr2))
   395  }
   396  
   397  func TestContainer_InvalidAt(t *testing.T) {
   398  	var x session.Container
   399  
   400  	nbf := mrand.Uint64()
   401  	if nbf == math.MaxUint64 {
   402  		nbf--
   403  	}
   404  
   405  	iat := nbf
   406  	exp := iat + 1
   407  
   408  	x.SetNbf(nbf)
   409  	x.SetIat(iat)
   410  	x.SetExp(exp)
   411  
   412  	require.True(t, x.InvalidAt(nbf-1))
   413  	require.True(t, x.InvalidAt(iat-1))
   414  	require.False(t, x.InvalidAt(iat))
   415  	require.False(t, x.InvalidAt(exp))
   416  	require.True(t, x.InvalidAt(exp+1))
   417  }
   418  
   419  func TestContainer_ID(t *testing.T) {
   420  	var x session.Container
   421  
   422  	require.Zero(t, x.ID())
   423  
   424  	id := uuid.New()
   425  
   426  	x.SetID(id)
   427  
   428  	require.Equal(t, id, x.ID())
   429  }
   430  
   431  func TestContainer_AssertAuthKey(t *testing.T) {
   432  	var x session.Container
   433  
   434  	key := randPublicKey()
   435  
   436  	require.False(t, x.AssertAuthKey(key))
   437  
   438  	x.SetAuthKey(key)
   439  
   440  	require.True(t, x.AssertAuthKey(key))
   441  }
   442  
   443  func TestContainer_ForVerb(t *testing.T) {
   444  	var val session.Container
   445  	var m v2session.Token
   446  	filled := sessiontest.Container()
   447  
   448  	assertDefaults := func() {
   449  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   450  		require.True(t, ok)
   451  		require.Zero(t, cCnr.Verb())
   452  	}
   453  
   454  	assertBinary := func(baseAssert func()) {
   455  		val2 := filled
   456  
   457  		require.NoError(t, val2.Unmarshal(val.Marshal()))
   458  		baseAssert()
   459  	}
   460  
   461  	assertJSON := func(baseAssert func()) {
   462  		val2 := filled
   463  
   464  		jd, err := val.MarshalJSON()
   465  		require.NoError(t, err)
   466  
   467  		require.NoError(t, val2.UnmarshalJSON(jd))
   468  		baseAssert()
   469  	}
   470  
   471  	val.WriteToV2(&m)
   472  
   473  	assertDefaults()
   474  	assertBinary(assertDefaults)
   475  	assertJSON(assertDefaults)
   476  
   477  	// set value
   478  
   479  	assertVerb := func(verb v2session.ContainerSessionVerb) {
   480  		cCnr, ok := m.GetBody().GetContext().(*v2session.ContainerSessionContext)
   481  		require.True(t, ok)
   482  		require.Equal(t, verb, cCnr.Verb())
   483  	}
   484  
   485  	for from, to := range map[session.ContainerVerb]v2session.ContainerSessionVerb{
   486  		session.VerbContainerPut:     v2session.ContainerVerbPut,
   487  		session.VerbContainerDelete:  v2session.ContainerVerbDelete,
   488  		session.VerbContainerSetEACL: v2session.ContainerVerbSetEACL,
   489  	} {
   490  		val.ForVerb(from)
   491  
   492  		val.WriteToV2(&m)
   493  
   494  		assertVerb(to)
   495  		assertBinary(func() { assertVerb(to) })
   496  		assertJSON(func() { assertVerb(to) })
   497  	}
   498  }
   499  
   500  func TestContainer_AssertVerb(t *testing.T) {
   501  	var x session.Container
   502  
   503  	const v1, v2 = session.VerbContainerPut, session.VerbContainerDelete
   504  
   505  	require.False(t, x.AssertVerb(v1))
   506  	require.False(t, x.AssertVerb(v2))
   507  
   508  	x.ForVerb(v1)
   509  	require.True(t, x.AssertVerb(v1))
   510  	require.False(t, x.AssertVerb(v2))
   511  }
   512  
   513  func TestIssuedBy(t *testing.T) {
   514  	var (
   515  		token  session.Container
   516  		issuer user.ID
   517  		signer = randSigner()
   518  	)
   519  
   520  	user.IDFromKey(&issuer, signer.PublicKey)
   521  
   522  	require.False(t, session.IssuedBy(token, issuer))
   523  
   524  	require.NoError(t, token.Sign(signer))
   525  	require.True(t, session.IssuedBy(token, issuer))
   526  }
   527  
   528  func TestContainer_Issuer(t *testing.T) {
   529  	var token session.Container
   530  	signer := randSigner()
   531  
   532  	require.Zero(t, token.Issuer())
   533  
   534  	require.NoError(t, token.Sign(signer))
   535  
   536  	var issuer user.ID
   537  
   538  	user.IDFromKey(&issuer, signer.PublicKey)
   539  
   540  	require.True(t, token.Issuer().Equals(issuer))
   541  }
   542  
   543  func TestContainer_Sign(t *testing.T) {
   544  	val := sessiontest.Container()
   545  
   546  	require.NoError(t, val.Sign(randSigner()))
   547  
   548  	require.True(t, val.VerifySignature())
   549  }
   550  
   551  func TestContainer_VerifyDataSignature(t *testing.T) {
   552  	signer := randSigner()
   553  
   554  	var tok session.Container
   555  
   556  	data := make([]byte, 100)
   557  	rand.Read(data)
   558  
   559  	var sig frostfscrypto.Signature
   560  	require.NoError(t, sig.Calculate(frostfsecdsa.SignerRFC6979(signer), data))
   561  
   562  	var sigV2 refs.Signature
   563  	sig.WriteToV2(&sigV2)
   564  
   565  	require.False(t, tok.VerifySessionDataSignature(data, sigV2.GetSign()))
   566  
   567  	tok.SetAuthKey((*frostfsecdsa.PublicKeyRFC6979)(&signer.PublicKey))
   568  	require.True(t, tok.VerifySessionDataSignature(data, sigV2.GetSign()))
   569  	require.False(t, tok.VerifySessionDataSignature(append(data, 1), sigV2.GetSign()))
   570  	require.False(t, tok.VerifySessionDataSignature(data, append(sigV2.GetSign(), 1)))
   571  }