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 }