github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/account/credentials_test.go (about)

     1  package account
     2  
     3  import (
     4  	"bytes"
     5  	cryptorand "crypto/rand"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"io"
     9  	"math/rand"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/cozy/cozy-stack/pkg/config/config"
    14  	"github.com/cozy/cozy-stack/pkg/crypto"
    15  	"github.com/cozy/cozy-stack/pkg/utils"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestEncryptDecrytCredentials(t *testing.T) {
    21  	config.UseTestFile(t)
    22  
    23  	encryptedCreds1, err := EncryptCredentials("me@mycozy.cloud", "fzEE6HFWsSp8jP")
    24  	require.NoError(t, err)
    25  
    26  	encryptedCreds2, err := EncryptCredentials("me@mycozy.cloud", "fzEE6HFWsSp8jP")
    27  	require.NoError(t, err)
    28  
    29  	encryptedCreds3, err := EncryptCredentials("", "fzEE6HFWsSp8jP")
    30  	require.NoError(t, err)
    31  
    32  	assert.NotEqual(t, encryptedCreds1, encryptedCreds2)
    33  
    34  	{
    35  		login, password, err := DecryptCredentials(encryptedCreds1)
    36  		require.NoError(t, err)
    37  
    38  		assert.Equal(t, "me@mycozy.cloud", login)
    39  		assert.Equal(t, "fzEE6HFWsSp8jP", password)
    40  	}
    41  	{
    42  		login, password, err := DecryptCredentials(encryptedCreds2)
    43  		require.NoError(t, err)
    44  
    45  		assert.Equal(t, "me@mycozy.cloud", login)
    46  		assert.Equal(t, "fzEE6HFWsSp8jP", password)
    47  	}
    48  	{
    49  		login, password, err := DecryptCredentials(encryptedCreds3)
    50  		require.NoError(t, err)
    51  
    52  		assert.Equal(t, "", login)
    53  		assert.Equal(t, "fzEE6HFWsSp8jP", password)
    54  	}
    55  }
    56  
    57  func TestEncryptDecrytUTF8Credentials(t *testing.T) {
    58  	config.UseTestFile(t)
    59  
    60  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
    61  	for i := 0; i < 1024; i++ {
    62  		login := string(crypto.GenerateRandomBytes(rng.Intn(256)))
    63  		password := string(crypto.GenerateRandomBytes(rng.Intn(256)))
    64  
    65  		encryptedCreds, err := EncryptCredentials(login, password)
    66  		require.NoError(t, err)
    67  
    68  		loginDec, passwordDec, err := DecryptCredentials(encryptedCreds)
    69  		require.NoError(t, err)
    70  
    71  		assert.Equal(t, loginDec, login)
    72  		assert.Equal(t, passwordDec, password)
    73  	}
    74  
    75  	for i := 0; i < 1024; i++ {
    76  		login := utils.RandomString(rng.Intn(256))
    77  		password := utils.RandomString(rng.Intn(256))
    78  
    79  		encryptedCreds, err := EncryptCredentials(login, password)
    80  		require.NoError(t, err)
    81  
    82  		loginDec, passwordDec, err := DecryptCredentials(encryptedCreds)
    83  		require.NoError(t, err)
    84  
    85  		assert.Equal(t, loginDec, login)
    86  		assert.Equal(t, passwordDec, password)
    87  	}
    88  }
    89  
    90  func TestDecryptCredentialsRandom(t *testing.T) {
    91  	config.UseTestFile(t)
    92  
    93  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
    94  	for i := 0; i < 1024; i++ {
    95  		encrypted := base64.StdEncoding.EncodeToString(crypto.GenerateRandomBytes(rng.Intn(256)))
    96  		_, _, err := DecryptCredentials(encrypted)
    97  		assert.Error(t, err)
    98  	}
    99  	for i := 0; i < 1024; i++ {
   100  		encrypted := crypto.GenerateRandomBytes(rng.Intn(256))
   101  		encryptedWithHeader := make([]byte, len(cipherHeader)+len(encrypted))
   102  		copy(encryptedWithHeader[0:], cipherHeader)
   103  		copy(encryptedWithHeader[len(cipherHeader):], encrypted)
   104  		_, _, err := DecryptCredentials(base64.StdEncoding.EncodeToString(encryptedWithHeader))
   105  		assert.Error(t, err)
   106  	}
   107  }
   108  
   109  func TestRandomBitFlipsCredentials(t *testing.T) {
   110  	config.UseTestFile(t)
   111  
   112  	original, err := EncryptCredentials("toto@titi.com", "X3hVYLJLRiUyCs")
   113  	require.NoError(t, err)
   114  
   115  	originalBuffer, err := base64.StdEncoding.DecodeString(original)
   116  	require.NoError(t, err)
   117  
   118  	flipped := make([]byte, len(originalBuffer))
   119  	copy(flipped, originalBuffer)
   120  	login, passwd, err := DecryptCredentials(base64.StdEncoding.EncodeToString(flipped))
   121  	require.NoError(t, err)
   122  
   123  	assert.Equal(t, "toto@titi.com", login)
   124  	assert.Equal(t, "X3hVYLJLRiUyCs", passwd)
   125  
   126  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   127  	for i := 0; i < 1000; i++ {
   128  		copy(flipped, originalBuffer)
   129  		flipsLen := rng.Intn(30) + 1
   130  		flipsSet := make([]int, 0, flipsLen)
   131  		for len(flipsSet) < flipsLen {
   132  			flipValue := rng.Intn(len(originalBuffer) * 8)
   133  			flipFound := false
   134  			for j := 0; j < len(flipsSet); j++ {
   135  				if flipsSet[j] == flipValue {
   136  					flipFound = true
   137  					break
   138  				}
   139  			}
   140  			if !flipFound {
   141  				flipsSet = append(flipsSet, flipValue)
   142  			}
   143  		}
   144  		for _, flipValue := range flipsSet {
   145  			mask := byte(0x1 << uint(flipValue%8))
   146  			flipped[flipValue/8] ^= mask
   147  		}
   148  		_, _, err := DecryptCredentials(base64.StdEncoding.EncodeToString(flipped))
   149  		if !assert.Error(t, err) {
   150  			t.Fatalf("Failed with flips %v", flipsSet)
   151  			return
   152  		}
   153  	}
   154  }
   155  
   156  func TestEncryptDecryptData(t *testing.T) {
   157  	config.UseTestFile(t)
   158  
   159  	var data interface{}
   160  	err := json.Unmarshal([]byte(`{"foo":"bar","baz":{"quz": "quuz"}}`), &data)
   161  	require.NoError(t, err)
   162  
   163  	encBuffer, err := EncryptCredentialsData(data)
   164  	require.NoError(t, err)
   165  
   166  	decData, err := DecryptCredentialsData(encBuffer)
   167  	require.NoError(t, err)
   168  
   169  	assert.EqualValues(t, data, decData)
   170  }
   171  
   172  func TestRandomBitFlipsBuffer(t *testing.T) {
   173  	config.UseTestFile(t)
   174  
   175  	plainBuffer := make([]byte, 256)
   176  	_, err := io.ReadFull(cryptorand.Reader, plainBuffer)
   177  	require.NoError(t, err)
   178  
   179  	original, err := EncryptBufferWithKey(config.GetKeyring().CredentialsEncryptorKey(), plainBuffer)
   180  	require.NoError(t, err)
   181  
   182  	flipped := make([]byte, len(original))
   183  	copy(flipped, original)
   184  	testBuffer, err := DecryptBufferWithKey(config.GetKeyring().CredentialsDecryptorKey(), flipped)
   185  	require.NoError(t, err)
   186  
   187  	assert.True(t, bytes.Equal(plainBuffer, testBuffer))
   188  
   189  	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
   190  	for i := 0; i < 1000; i++ {
   191  		copy(flipped, original)
   192  		flipsLen := rng.Intn(30) + 1
   193  		flipsSet := make([]int, 0, flipsLen)
   194  		for len(flipsSet) < flipsLen {
   195  			flipValue := rng.Intn(len(original) * 8)
   196  			flipFound := false
   197  			for j := 0; j < len(flipsSet); j++ {
   198  				if flipsSet[j] == flipValue {
   199  					flipFound = true
   200  					break
   201  				}
   202  			}
   203  			if !flipFound {
   204  				flipsSet = append(flipsSet, flipValue)
   205  			}
   206  		}
   207  		for _, flipValue := range flipsSet {
   208  			mask := byte(0x1 << uint(flipValue%8))
   209  			flipped[flipValue/8] ^= mask
   210  		}
   211  		_, err := DecryptBufferWithKey(config.GetKeyring().CredentialsDecryptorKey(), flipped)
   212  		if !assert.Error(t, err) {
   213  			t.Fatalf("Failed with flips %v", flipsSet)
   214  			return
   215  		}
   216  	}
   217  }
   218  
   219  func TestAccountsEncryptDecrypt(t *testing.T) {
   220  	config.UseTestFile(t)
   221  
   222  	v := []byte(`
   223  {
   224      "_id": "d01aa821781612dce542a13d6989e6d0",
   225      "_rev": "5-c8fc2169ff3226165688865e7cb609ef",
   226      "_type": "io.cozy.accounts",
   227      "account_type": "labanquepostale44",
   228      "auth": {
   229          "accountName": "Perso",
   230          "identifier": "WHATYOUWANT",
   231          "secret": "YOUWANTTOREADMYSECRET"
   232      },
   233      "data": {
   234          "account_type": "linxo",
   235          "auth": {
   236              "login": "linxo.SOMEID@cozy.rocks",
   237              "password": "SOMEPASSWORD"
   238          },
   239          "status": "connected",
   240          "token": "4D757B74AD",
   241          "uuid": "f6bb19cf-1c03-4d80-92e9-af66c18c4aa4"
   242      },
   243      "type": "io.cozy.accounts"
   244  }
   245  `)
   246  
   247  	var encrypted, decrypted bool
   248  	var m1 map[string]interface{} // original
   249  	var m2 map[string]interface{} // encrypted
   250  	var m3 map[string]interface{} // decrypted
   251  	assert.NoError(t, json.Unmarshal(v, &m1))
   252  	assert.NoError(t, json.Unmarshal(v, &m2))
   253  	assert.NoError(t, json.Unmarshal(v, &m3))
   254  
   255  	encrypted = encryptMap(m2)
   256  	assert.True(t, encrypted)
   257  
   258  	{
   259  		auth1 := m2["auth"].(map[string]interface{})
   260  		auth2 := m2["data"].(map[string]interface{})["auth"].(map[string]interface{})
   261  		{
   262  			_, ok1 := auth1["secret"]
   263  			_, ok2 := auth1["secret_encrypted"]
   264  			assert.False(t, ok1)
   265  			assert.True(t, ok2)
   266  		}
   267  		{
   268  			_, ok1 := auth2["password"]
   269  			_, ok2 := auth2["credentials_encrypted"]
   270  			assert.False(t, ok1)
   271  			assert.True(t, ok2)
   272  		}
   273  	}
   274  
   275  	encrypted = encryptMap(m3)
   276  	decrypted = decryptMap(m3)
   277  	assert.True(t, encrypted)
   278  	assert.True(t, decrypted)
   279  	assert.EqualValues(t, m1, m3)
   280  
   281  	{
   282  		auth1 := m3["auth"].(map[string]interface{})
   283  		auth2 := m3["data"].(map[string]interface{})["auth"].(map[string]interface{})
   284  		{
   285  			_, ok1 := auth1["secret"]
   286  			_, ok2 := auth1["secret_encrypted"]
   287  			assert.True(t, ok1)
   288  			assert.False(t, ok2)
   289  		}
   290  		{
   291  			_, ok1 := auth2["password"]
   292  			_, ok2 := auth2["credentials_encrypted"]
   293  			assert.True(t, ok1)
   294  			assert.False(t, ok2)
   295  		}
   296  	}
   297  }