github.com/zntrio/harp/v2@v2.0.9/pkg/container/seal/v1/seal_test.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //	http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  package v1
    18  
    19  import (
    20  	"crypto/rand"
    21  	"testing"
    22  
    23  	"github.com/awnumar/memguard"
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/google/go-cmp/cmp/cmpopts"
    26  	fuzz "github.com/google/gofuzz"
    27  	"github.com/stretchr/testify/assert"
    28  
    29  	containerv1 "github.com/zntrio/harp/v2/api/gen/go/harp/container/v1"
    30  )
    31  
    32  var (
    33  	opt = cmp.FilterPath(
    34  		func(p cmp.Path) bool {
    35  			// Remove ignoring of the fields below once go-cmp is able to ignore generated fields.
    36  			// See https://github.com/google/go-cmp/issues/153
    37  			ignoreXXXCache :=
    38  				p.String() == "XXX_sizecache" ||
    39  					p.String() == "Headers.XXX_sizecache"
    40  			return ignoreXXXCache
    41  		}, cmp.Ignore())
    42  
    43  	ignoreOpts = []cmp.Option{
    44  		cmpopts.IgnoreUnexported(containerv1.Container{}),
    45  		cmpopts.IgnoreUnexported(containerv1.Header{}),
    46  		opt,
    47  	}
    48  )
    49  
    50  // -----------------------------------------------------------------------------
    51  
    52  func TestSeal(t *testing.T) {
    53  	type args struct {
    54  		container      *containerv1.Container
    55  		peersPublicKey []string
    56  	}
    57  	tests := []struct {
    58  		name    string
    59  		args    args
    60  		want    *containerv1.Container
    61  		wantErr bool
    62  	}{
    63  		{
    64  			name:    "nil",
    65  			wantErr: true,
    66  		},
    67  		{
    68  			name: "empty container",
    69  			args: args{
    70  				container: &containerv1.Container{},
    71  			},
    72  			wantErr: true,
    73  		},
    74  		{
    75  			name: "empty container headers",
    76  			args: args{
    77  				container: &containerv1.Container{
    78  					Headers: &containerv1.Header{},
    79  				},
    80  			},
    81  			wantErr: true,
    82  		},
    83  		{
    84  			name: "empty container with public keys",
    85  			args: args{
    86  				container: &containerv1.Container{
    87  					Headers: &containerv1.Header{},
    88  				},
    89  				peersPublicKey: []string{
    90  					"v1.sk.qKXPnUP6-2Bb_4nYnmxOXyCdN4IV3AR5HooB33N3g2E",
    91  					"v1.sk.sYp90gC29yKfUUtr50pMR4Faf7c3d4-YX4xZsbwAs10",
    92  				},
    93  			},
    94  			wantErr: false,
    95  		},
    96  		{
    97  			name: "valid container with public keys",
    98  			args: args{
    99  				container: &containerv1.Container{
   100  					Headers: &containerv1.Header{},
   101  					Raw:     memguard.NewBufferRandom(1024).Bytes(),
   102  				},
   103  				peersPublicKey: []string{
   104  					"v1.sk.qKXPnUP6-2Bb_4nYnmxOXyCdN4IV3AR5HooB33N3g2E",
   105  					"v1.sk.sYp90gC29yKfUUtr50pMR4Faf7c3d4-YX4xZsbwAs10",
   106  				},
   107  			},
   108  			wantErr: false,
   109  		},
   110  	}
   111  	for _, tt := range tests {
   112  		t.Run(tt.name, func(t *testing.T) {
   113  			adapter := New()
   114  			_, err := adapter.Seal(rand.Reader, tt.args.container, tt.args.peersPublicKey...)
   115  			if (err != nil) != tt.wantErr {
   116  				t.Errorf("Seal() error = %v, wantErr %v", err, tt.wantErr)
   117  				return
   118  			}
   119  		})
   120  	}
   121  }
   122  
   123  func TestSealWithPSK(t *testing.T) {
   124  	psk := memguard.NewBufferRandom(64)
   125  
   126  	type args struct {
   127  		container      *containerv1.Container
   128  		peersPublicKey []string
   129  	}
   130  	tests := []struct {
   131  		name    string
   132  		args    args
   133  		want    *containerv1.Container
   134  		wantErr bool
   135  	}{
   136  		{
   137  			name:    "nil",
   138  			wantErr: true,
   139  		},
   140  		{
   141  			name: "empty container",
   142  			args: args{
   143  				container: &containerv1.Container{},
   144  			},
   145  			wantErr: true,
   146  		},
   147  		{
   148  			name: "empty container headers",
   149  			args: args{
   150  				container: &containerv1.Container{
   151  					Headers: &containerv1.Header{},
   152  				},
   153  			},
   154  			wantErr: true,
   155  		},
   156  		{
   157  			name: "empty container with public keys",
   158  			args: args{
   159  				container: &containerv1.Container{
   160  					Headers: &containerv1.Header{},
   161  				},
   162  				peersPublicKey: []string{
   163  					"v1.sk.qKXPnUP6-2Bb_4nYnmxOXyCdN4IV3AR5HooB33N3g2E",
   164  					"v1.sk.sYp90gC29yKfUUtr50pMR4Faf7c3d4-YX4xZsbwAs10",
   165  				},
   166  			},
   167  			wantErr: false,
   168  		},
   169  		{
   170  			name: "valid container with public keys",
   171  			args: args{
   172  				container: &containerv1.Container{
   173  					Headers: &containerv1.Header{},
   174  					Raw:     memguard.NewBufferRandom(1024).Bytes(),
   175  				},
   176  				peersPublicKey: []string{
   177  					"v1.sk.qKXPnUP6-2Bb_4nYnmxOXyCdN4IV3AR5HooB33N3g2E",
   178  					"v1.sk.sYp90gC29yKfUUtr50pMR4Faf7c3d4-YX4xZsbwAs10",
   179  				},
   180  			},
   181  			wantErr: false,
   182  		},
   183  	}
   184  	for _, tt := range tests {
   185  		t.Run(tt.name, func(t *testing.T) {
   186  			adapter := New()
   187  			_, err := adapter.SealWithPSK(rand.Reader, tt.args.container, psk, tt.args.peersPublicKey...)
   188  			if (err != nil) != tt.wantErr {
   189  				t.Errorf("SealWithPSK() error = %v, wantErr %v", err, tt.wantErr)
   190  				return
   191  			}
   192  		})
   193  	}
   194  }
   195  
   196  // -----------------------------------------------------------------------------
   197  
   198  func Test_Seal_Unseal(t *testing.T) {
   199  	adapter := New()
   200  
   201  	publicKey1, privateKey1, err := adapter.GenerateKey()
   202  	assert.NoError(t, err)
   203  
   204  	input := &containerv1.Container{
   205  		Headers: &containerv1.Header{
   206  			ContentEncoding: "gzip",
   207  			ContentType:     "application/vnd.harp.v1.Bundle",
   208  		},
   209  		Raw: memguard.NewBufferRandom(1024).Bytes(),
   210  	}
   211  
   212  	sealed, err := adapter.Seal(rand.Reader, input, publicKey1)
   213  	if err != nil {
   214  		t.Fatalf("unable to seal container: %v", err)
   215  	}
   216  
   217  	unsealed, err := adapter.Unseal(sealed, memguard.NewBufferFromBytes([]byte(privateKey1)))
   218  	if err != nil {
   219  		t.Fatalf("unable to unseal container: %v", err)
   220  	}
   221  
   222  	if diff := cmp.Diff(unsealed, input, ignoreOpts...); diff != "" {
   223  		t.Errorf("Seal/Unseal()\n-got/+want\ndiff %s", diff)
   224  	}
   225  }
   226  
   227  func Test_Seal_Unseal_WithPSK(t *testing.T) {
   228  	psk := memguard.NewBufferRandom(64)
   229  	adapter := New()
   230  
   231  	publicKey1, privateKey1, err := adapter.GenerateKey()
   232  	assert.NoError(t, err)
   233  
   234  	input := &containerv1.Container{
   235  		Headers: &containerv1.Header{
   236  			ContentEncoding: "gzip",
   237  			ContentType:     "application/vnd.harp.v1.Bundle",
   238  		},
   239  		Raw: memguard.NewBufferRandom(1024).Bytes(),
   240  	}
   241  
   242  	sealed, err := adapter.SealWithPSK(rand.Reader, input, psk, publicKey1)
   243  	if err != nil {
   244  		t.Fatalf("unable to seal container: %v", err)
   245  	}
   246  
   247  	unsealed, err := adapter.UnsealWithPSK(sealed, memguard.NewBufferFromBytes([]byte(privateKey1)), psk)
   248  	if err != nil {
   249  		t.Fatalf("unable to unseal container: %v", err)
   250  	}
   251  
   252  	if diff := cmp.Diff(unsealed, input, ignoreOpts...); diff != "" {
   253  		t.Errorf("Seal/Unseal()\n-got/+want\ndiff %s", diff)
   254  	}
   255  }
   256  
   257  func Test_Seal_Fuzz(t *testing.T) {
   258  	adapter := New()
   259  
   260  	// Making sure the function never panics
   261  	for i := 0; i < 500; i++ {
   262  		f := fuzz.New()
   263  
   264  		// Prepare arguments
   265  		var publicKey string
   266  		input := containerv1.Container{
   267  			Headers: &containerv1.Header{},
   268  			Raw:     []byte{0x00, 0x00},
   269  		}
   270  
   271  		f.Fuzz(&input.Headers)
   272  		f.Fuzz(&input.Raw)
   273  		f.Fuzz(&publicKey)
   274  
   275  		// Execute
   276  		adapter.Seal(rand.Reader, &input, publicKey)
   277  	}
   278  }
   279  
   280  func Test_UnSeal_Fuzz(t *testing.T) {
   281  	// Memguard buffer is excluded from fuzz for random race condition error
   282  	// investigation will be done in a separated thread.
   283  	identity := memguard.NewBufferRandom(32)
   284  
   285  	adapter := New()
   286  
   287  	// Making sure the function never panics
   288  	for i := 0; i < 500; i++ {
   289  		f := fuzz.New()
   290  
   291  		// Prepare arguments
   292  		input := containerv1.Container{
   293  			Headers: &containerv1.Header{},
   294  			Raw:     []byte{0x00, 0x00},
   295  		}
   296  
   297  		f.Fuzz(&input.Headers)
   298  		f.Fuzz(&input.Raw)
   299  
   300  		// Execute
   301  		adapter.Unseal(&input, identity)
   302  	}
   303  }
   304  
   305  // -----------------------------------------------------------------------------.
   306  func benchmarkSeal(container *containerv1.Container, peersPublicKeys []string, b *testing.B) {
   307  	adapter := New()
   308  	for n := 0; n < b.N; n++ {
   309  		_, err := adapter.Seal(rand.Reader, container, peersPublicKeys...)
   310  		if err != nil {
   311  			b.Fatal(err)
   312  		}
   313  	}
   314  }
   315  
   316  func Benchmark_Seal(b *testing.B) {
   317  	publicKey, _, err := New().GenerateKey()
   318  	assert.NoError(b, err)
   319  
   320  	input := &containerv1.Container{
   321  		Headers: &containerv1.Header{
   322  			ContentEncoding: "gzip",
   323  			ContentType:     "application/vnd.harp.v1.Bundle",
   324  		},
   325  		Raw: make([]byte, 1024),
   326  	}
   327  
   328  	benchmarkSeal(input, []string{publicKey}, b)
   329  }