...

Source file src/edge-infra.dev/pkg/edge/iam/crypto/encryption_key_rotation_test.go

Documentation: edge-infra.dev/pkg/edge/iam/crypto

     1  package crypto_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/go-kivik/kivik/v4"
    11  	"github.com/go-kivik/kivik/v4/driver"
    12  	kivikmock "github.com/go-kivik/kivik/v4/mockdb"
    13  
    14  	"edge-infra.dev/pkg/edge/iam/crypto"
    15  	"edge-infra.dev/pkg/edge/iam/storage/database"
    16  )
    17  
    18  // this function tests RotateCouchEncryptionKey(), which given two keys (old & new), grabs whats in
    19  // the couchdb database, decrypts it with the old key, then re-encrypts it with the new key
    20  func TestCouchDBSwapFunction(t *testing.T) {
    21  	oldKey := []byte("my-32bit-super-extra-secret-key!")
    22  	newKey := []byte("my-32bit-super-extra-secret-key2")
    23  
    24  	client, mock, err := kivikmock.New()
    25  	if err != nil {
    26  		panic(err)
    27  	}
    28  
    29  	// creating store with mock couchdb
    30  	s, err := NewCouchStoreWithMock(client)
    31  	if err != nil {
    32  		t.Errorf("Unable create mock store with couchdb %s\n", err)
    33  		return
    34  	}
    35  
    36  	// new mock db
    37  	db := mock.NewDB()
    38  
    39  	// returns a *driver.Document
    40  	document, err := createCouchData(oldKey)
    41  	if err != nil {
    42  		t.Errorf("Unable to create couch document %s\n", err)
    43  		return
    44  	}
    45  
    46  	// marshal to put into a Row
    47  	docAsBytes, err := json.Marshal(document)
    48  	if err != nil {
    49  		t.Errorf("Unable to marshal document %s\n", err)
    50  		return
    51  	}
    52  
    53  	docReader := bytes.NewReader(docAsBytes)
    54  	// construct the Row w/ doc
    55  	row := &driver.Row{
    56  		ID:  "test-doc",
    57  		Doc: docReader,
    58  	}
    59  
    60  	// adding the Row to the list of Rows
    61  	rows := kivikmock.NewRows().AddRow(row)
    62  
    63  	// what the mock couchdb expects to happen
    64  	mock.ExpectDB().WillReturn(db)
    65  	db.ExpectAllDocs().WillReturn(rows)
    66  	mock.ExpectDB().WillReturn(db)
    67  	db.ExpectGet().WillReturn(document)
    68  	mock.ExpectDB().WillReturn(db)
    69  	db.ExpectPut()
    70  
    71  	// actually test the swapping of encryption keys against the document
    72  	err = s.RotateCouchEncryptionKey(context.Background(), oldKey, newKey)
    73  	if err != nil {
    74  		t.Errorf("Unable to update couchdb data with new key %s\n", err)
    75  		return
    76  	}
    77  }
    78  
    79  // this tests the function GetDocWithKey, which retrieves a document with a given key
    80  // this is different than getDoc as it assumes 1. we're encrypted and 2. you have to provide a key
    81  func TestInnerUpdateFunction(t *testing.T) {
    82  	value := []byte(`{"number": 1,"title": "test","url": "https://google.com"}`)
    83  	key := []byte("my-32bit-super-extra-secret-key!")
    84  	client, mock, err := kivikmock.New()
    85  	if err != nil {
    86  		panic(err)
    87  	}
    88  
    89  	s, err := NewCouchStoreWithMock(client)
    90  	if err != nil {
    91  		t.Errorf("Unable create mock store with couchdb %s\n", err)
    92  		return
    93  	}
    94  
    95  	db := mock.NewDB()
    96  
    97  	// returns a *driver.Document
    98  	document, err := createCouchData(key)
    99  	if err != nil {
   100  		t.Errorf("Unable to create couch document %s\n", err)
   101  		return
   102  	}
   103  
   104  	// what mock db expects will happen
   105  	mock.ExpectDB().WillReturn(db)
   106  	db.ExpectGet().WillReturn(document)
   107  
   108  	// return the item from the db with a given key, unencrypted
   109  	unencryptedDoc, err := s.GetDocWithKey(context.Background(), "test-doc", key)
   110  	if err != nil {
   111  		t.Errorf("Unable to get test data with new key %s\n", err)
   112  		return
   113  	}
   114  
   115  	// confirming we did retrieve a doc
   116  	if unencryptedDoc == nil {
   117  		t.Errorf("Was not able to retrieve and decrypt doc %s\n", err)
   118  		return
   119  	}
   120  
   121  	// confirming it is unencrypted
   122  	if !bytes.Equal(unencryptedDoc.Value, value) {
   123  		t.Errorf("Unable to get test data with new key %s\n", err)
   124  		return
   125  	}
   126  }
   127  
   128  // creates a sample couchdb Document
   129  func createCouchData(key []byte) (*driver.Document, error) {
   130  	value := []byte(`{"number": 1,"title": "test","url": "https://google.com"}`)
   131  
   132  	encrypted, err := crypto.EncryptJSON(value, key)
   133  	if err != nil {
   134  		return &driver.Document{}, err
   135  	}
   136  
   137  	doc := database.Doc{
   138  		ID:         "test-doc",
   139  		Value:      encrypted,
   140  		Expiration: time.Now().Add(time.Hour).Unix(),
   141  	}
   142  
   143  	// Convert the Docs struct into a Kivik driver.Document
   144  	kivikDoc, err := kivikmock.Document(&doc)
   145  	if err != nil {
   146  		return &driver.Document{}, err
   147  	}
   148  
   149  	return kivikDoc, err
   150  }
   151  
   152  // new store with a mock couchDB client
   153  func NewCouchStoreWithMock(client *kivik.Client) (*database.Store, error) {
   154  	store := &database.Store{
   155  		CouchDB: client,
   156  	}
   157  	return store, nil
   158  }
   159  

View as plain text