2022-06-15 10:10:22 +01:00
//
2024-09-06 16:34:30 +03:00
// C o p y r i g h t 2 0 2 2 - 2 0 2 4 N e w V e c t o r L t d .
2022-06-15 10:10:22 +01:00
//
2025-01-06 11:27:37 +01:00
// S P D X - L i c e n s e - I d e n t i f i e r : A G P L - 3 . 0 - o n l y O R L i c e n s e R e f - E l e m e n t - C o m m e r c i a l
// P l e a s e s e e L I C E N S E f i l e s i n t h e r e p o s i t o r y r o o t f o r f u l l d e t a i l s .
2022-06-15 10:10:22 +01:00
//
@ testable import ElementX
2022-07-06 14:49:05 +01:00
import XCTest
2022-06-15 10:10:22 +01:00
class KeychainControllerTests : XCTestCase {
var keychain : KeychainController !
override func setUp ( ) {
2022-11-21 19:37:13 +03:00
keychain = KeychainController ( service : . tests ,
2023-01-31 17:48:24 +00:00
accessGroup : InfoPlistReader . main . keychainAccessGroupIdentifier )
2022-11-04 13:24:53 +02:00
keychain . removeAllRestorationTokens ( )
2023-10-19 10:42:12 +01:00
keychain . resetSecrets ( )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
func testAddRestorationToken ( ) {
2022-06-15 10:10:22 +01:00
// G i v e n a n e m p t y k e y c h a i n .
2022-11-04 13:24:53 +02:00
XCTAssertTrue ( keychain . restorationTokens ( ) . isEmpty , " The keychain should be empty to begin with. " )
2022-06-15 10:10:22 +01:00
2022-11-04 13:24:53 +02:00
// W h e n a d d i n g a n r e s t o r a t i o n t o k e n .
2022-06-15 10:10:22 +01:00
let username = " @test:example.com "
2023-02-08 19:07:02 +00:00
let restorationToken = RestorationToken ( session : . init ( accessToken : " accessToken " ,
refreshToken : " refreshToken " ,
userId : " userId " ,
deviceId : " deviceId " ,
homeserverUrl : " homeserverUrl " ,
2023-08-22 15:53:27 +01:00
oidcData : " oidcData " ,
2024-08-29 11:50:06 +01:00
slidingSyncVersion : . proxy ( url : " https://my.sync.proxy " ) ) ,
2024-09-11 14:32:03 +01:00
sessionDirectories : . init ( ) ,
2024-01-12 16:45:59 +00:00
passphrase : " passphrase " ,
pusherNotificationClientIdentifier : " pusherClientID " )
2022-11-04 13:24:53 +02:00
keychain . setRestorationToken ( restorationToken , forUsername : username )
2022-06-15 10:10:22 +01:00
2022-11-04 13:24:53 +02:00
// T h e n t h e r e s t o r a t i o n t o k e n s h o u l d b e s t o r e d i n t h e k e y c h a i n .
XCTAssertEqual ( keychain . restorationTokenForUsername ( username ) , restorationToken , " The retrieved restoration token should match the value that was stored. " )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
func testRemovingRestorationToken ( ) {
// G i v e n a k e y c h a i n w i t h a s t o r e d r e s t o r a t i o n t o k e n .
2022-06-15 10:10:22 +01:00
let username = " @test:example.com "
2023-02-08 19:07:02 +00:00
let restorationToken = RestorationToken ( session : . init ( accessToken : " accessToken " ,
refreshToken : " refreshToken " ,
userId : " userId " ,
deviceId : " deviceId " ,
homeserverUrl : " homeserverUrl " ,
2023-08-22 15:53:27 +01:00
oidcData : " oidcData " ,
2024-08-29 11:50:06 +01:00
slidingSyncVersion : . proxy ( url : " https://my.sync.proxy " ) ) ,
2024-09-11 14:32:03 +01:00
sessionDirectories : . init ( ) ,
2024-01-12 16:45:59 +00:00
passphrase : " passphrase " ,
pusherNotificationClientIdentifier : " pusherClientID " )
2022-11-04 13:24:53 +02:00
keychain . setRestorationToken ( restorationToken , forUsername : username )
XCTAssertEqual ( keychain . restorationTokens ( ) . count , 1 , " The keychain should have 1 restoration token. " )
XCTAssertEqual ( keychain . restorationTokenForUsername ( username ) , restorationToken , " The initial restoration token should match the value that was stored. " )
2022-06-15 10:10:22 +01:00
2022-11-04 13:24:53 +02:00
// W h e n d e l e t i n g t h e r e s t o r a t i o n t o k e n .
keychain . removeRestorationTokenForUsername ( username )
2022-06-15 10:10:22 +01:00
// T h e n t h e k e y c h a i n s h o u l d b e e m p t y .
2022-11-04 13:24:53 +02:00
XCTAssertTrue ( keychain . restorationTokens ( ) . isEmpty , " The keychain should be empty after deleting the token. " )
XCTAssertNil ( keychain . restorationTokenForUsername ( username ) , " There restoration token should not be returned after removal. " )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
func testRemovingAllRestorationTokens ( ) {
// G i v e n a k e y c h a i n w i t h 5 s t o r e d r e s t o r a t i o n t o k e n s .
2022-06-15 10:10:22 +01:00
for index in 0. . < 5 {
2023-02-08 19:07:02 +00:00
let restorationToken = RestorationToken ( session : . init ( accessToken : " accessToken " ,
refreshToken : " refreshToken " ,
userId : " userId " ,
deviceId : " deviceId " ,
homeserverUrl : " homeserverUrl " ,
2023-08-22 15:53:27 +01:00
oidcData : " oidcData " ,
2024-08-29 11:50:06 +01:00
slidingSyncVersion : . proxy ( url : " https://my.sync.proxy " ) ) ,
2024-09-11 14:32:03 +01:00
sessionDirectories : . init ( ) ,
2024-01-12 16:45:59 +00:00
passphrase : " passphrase " ,
pusherNotificationClientIdentifier : " pusherClientID " )
2022-11-04 13:24:53 +02:00
keychain . setRestorationToken ( restorationToken , forUsername : " @test \( index ) :example.com " )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
XCTAssertEqual ( keychain . restorationTokens ( ) . count , 5 , " The keychain should have 5 restoration tokens. " )
2022-06-15 10:10:22 +01:00
2022-11-04 13:24:53 +02:00
// W h e n d e l e t i n g a l l o f t h e r e s t o r a t i o n t o k e n s .
keychain . removeAllRestorationTokens ( )
2022-06-15 10:10:22 +01:00
// T h e n t h e k e y c h a i n s h o u l d b e e m p t y .
2022-11-04 13:24:53 +02:00
XCTAssertTrue ( keychain . restorationTokens ( ) . isEmpty , " The keychain should be empty after deleting the token. " )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
func testRemovingSingleRestorationTokens ( ) {
// G i v e n a k e y c h a i n w i t h 5 s t o r e d r e s t o r a t i o n t o k e n s .
2022-06-15 10:10:22 +01:00
for index in 0. . < 5 {
2023-02-08 19:07:02 +00:00
let restorationToken = RestorationToken ( session : . init ( accessToken : " accessToken " ,
refreshToken : " refreshToken " ,
userId : " userId " ,
deviceId : " deviceId " ,
homeserverUrl : " homeserverUrl " ,
2023-08-22 15:53:27 +01:00
oidcData : " oidcData " ,
2024-08-29 11:50:06 +01:00
slidingSyncVersion : . proxy ( url : " https://my.sync.proxy " ) ) ,
2024-09-11 14:32:03 +01:00
sessionDirectories : . init ( ) ,
2024-01-12 16:45:59 +00:00
passphrase : " passphrase " ,
pusherNotificationClientIdentifier : " pusherClientID " )
2022-11-04 13:24:53 +02:00
keychain . setRestorationToken ( restorationToken , forUsername : " @test \( index ) :example.com " )
2022-06-15 10:10:22 +01:00
}
2022-11-04 13:24:53 +02:00
XCTAssertEqual ( keychain . restorationTokens ( ) . count , 5 , " The keychain should have 5 restoration tokens. " )
2022-06-15 10:10:22 +01:00
2022-11-04 13:24:53 +02:00
// W h e n d e l e t i n g o n e o f t h e r e s t o r a t i o n t o k e n s .
keychain . removeRestorationTokenForUsername ( " @test2:example.com " )
2022-06-15 10:10:22 +01:00
// T h e n t h e o t h e r 4 i t e m s s h o u l d r e m a i n u n t o u c h e d .
2022-11-04 13:24:53 +02:00
XCTAssertEqual ( keychain . restorationTokens ( ) . count , 4 , " The keychain have 4 remaining restoration tokens. " )
XCTAssertNotNil ( keychain . restorationTokenForUsername ( " @test0:example.com " ) , " The restoration token should not have been deleted. " )
XCTAssertNotNil ( keychain . restorationTokenForUsername ( " @test1:example.com " ) , " The restoration token should not have been deleted. " )
XCTAssertNil ( keychain . restorationTokenForUsername ( " @test2:example.com " ) , " The restoration token should have been deleted. " )
XCTAssertNotNil ( keychain . restorationTokenForUsername ( " @test3:example.com " ) , " The restoration token should not have been deleted. " )
XCTAssertNotNil ( keychain . restorationTokenForUsername ( " @test4:example.com " ) , " The restoration token should not have been deleted. " )
2022-06-15 10:10:22 +01:00
}
2023-10-19 10:42:12 +01:00
2024-07-24 10:55:40 +01:00
func testSimplifiedSlidingSyncRestorationToken ( ) {
// G i v e n a n e m p t y k e y c h a i n .
XCTAssertTrue ( keychain . restorationTokens ( ) . isEmpty , " The keychain should be empty to begin with. " )
// W h e n a d d i n g a n r e s t o r a t i o n t o k e n t h a t d o e s n ' t c o n t a i n a s l i d i n g s y n c p r o x y ( e . g . f o r S S S ) .
let username = " @test:example.com "
let restorationToken = RestorationToken ( session : . init ( accessToken : " accessToken " ,
refreshToken : " refreshToken " ,
userId : " userId " ,
deviceId : " deviceId " ,
homeserverUrl : " homeserverUrl " ,
oidcData : " oidcData " ,
2024-08-29 11:50:06 +01:00
slidingSyncVersion : . native ) ,
2024-09-11 14:32:03 +01:00
sessionDirectories : . init ( ) ,
2024-07-24 10:55:40 +01:00
passphrase : " passphrase " ,
pusherNotificationClientIdentifier : " pusherClientID " )
keychain . setRestorationToken ( restorationToken , forUsername : username )
// T h e n d e c o d i n g t h e r e s t o r a t i o n t o k e n f r o m t h e k e y c h a i n s h o u l d s t i l l w o r k .
XCTAssertEqual ( keychain . restorationTokenForUsername ( username ) , restorationToken , " The retrieved restoration token should match the value that was stored. " )
}
2023-10-19 10:42:12 +01:00
func testAddPINCode ( ) throws {
// G i v e n a k e y c h a i n w i t h o u t a P I N c o d e s e t .
try XCTAssertFalse ( keychain . containsPINCode ( ) , " A new keychain shouldn't contain a PIN code. " )
XCTAssertNil ( keychain . pinCode ( ) , " A new keychain shouldn't return a PIN code. " )
// W h e n s e t t i n g a P I N c o d e .
try keychain . setPINCode ( " 0000 " )
2023-10-27 10:09:12 +01:00
// T h e n t h e P I N c o d e s h o u l d b e s t o r e d .
2023-10-19 10:42:12 +01:00
try XCTAssertTrue ( keychain . containsPINCode ( ) , " The keychain should contain the PIN code. " )
XCTAssertEqual ( keychain . pinCode ( ) , " 0000 " , " The stored PIN code should match what was set. " )
}
func testUpdatePINCode ( ) throws {
// G i v e n a k e y c h a i n w i t h a P I N c o d e a l r e a d y s e t .
try keychain . setPINCode ( " 0000 " )
try XCTAssertTrue ( keychain . containsPINCode ( ) , " The keychain should contain the PIN code. " )
XCTAssertEqual ( keychain . pinCode ( ) , " 0000 " , " The stored PIN code should match what was set. " )
// W h e n s e t t i n g a d i f f e r e n t P I N c o d e .
try keychain . setPINCode ( " 1234 " )
2023-10-27 10:09:12 +01:00
// T h e n t h e P I N c o d e s h o u l d b e u p d a t e d .
2023-10-19 10:42:12 +01:00
try XCTAssertTrue ( keychain . containsPINCode ( ) , " The keychain should still contain the PIN code. " )
XCTAssertEqual ( keychain . pinCode ( ) , " 1234 " , " The stored PIN code should match the new value. " )
}
func testRemovePINCode ( ) throws {
// G i v e n a k e y c h a i n w i t h a P I N c o d e a l r e a d y s e t .
try keychain . setPINCode ( " 0000 " )
try XCTAssertTrue ( keychain . containsPINCode ( ) , " The keychain should contain the PIN code. " )
XCTAssertEqual ( keychain . pinCode ( ) , " 0000 " , " The stored PIN code should match what was set. " )
// W h e n r e m o v i n g t h e P I N c o d e .
keychain . removePINCode ( )
2023-10-27 10:09:12 +01:00
// T h e n t h e P I N c o d e s h o u l d n o l o n g e r b e s t o r e d .
2023-10-19 10:42:12 +01:00
try XCTAssertFalse ( keychain . containsPINCode ( ) , " The keychain should no longer contain the PIN code. " )
XCTAssertNil ( keychain . pinCode ( ) , " There shouldn't be a stored PIN code after removing it. " )
}
2023-10-27 10:09:12 +01:00
func testAddPINCodeBiometricState ( ) throws {
// G i v e n a k e y c h a i n w i t h o u t a n y b i o m e t r i c s t a t e .
XCTAssertFalse ( keychain . containsPINCodeBiometricState ( ) , " A new keychain shouldn't contain biometric state. " )
XCTAssertNil ( keychain . pinCodeBiometricState ( ) , " A new keychain shouldn't return biometric state. " )
// W h e n s e t t i n g t h e s t a t e .
2024-06-05 07:31:49 +01:00
let data = Data ( " Face ID " . utf8 )
2023-10-27 10:09:12 +01:00
try keychain . setPINCodeBiometricState ( data )
// T h e n t h e s t a t e s h o u l d b e s t o r e d .
XCTAssertTrue ( keychain . containsPINCodeBiometricState ( ) , " The keychain should contain the biometric state. " )
XCTAssertEqual ( keychain . pinCodeBiometricState ( ) , data , " The stored biometric state should match what was set. " )
}
func testUpdatePINCodeBiometricState ( ) throws {
// G i v e n a k e y c h a i n t h a t c o n t a i n s P I N c o d e b i o m e t r i c s t a t e .
2024-06-05 07:31:49 +01:00
let data = Data ( " 😃 " . utf8 )
2023-10-27 10:09:12 +01:00
try keychain . setPINCodeBiometricState ( data )
XCTAssertTrue ( keychain . containsPINCodeBiometricState ( ) , " The keychain should contain the biometric state. " )
XCTAssertEqual ( keychain . pinCodeBiometricState ( ) , data , " The stored biometric state should match what was set. " )
// W h e n s e t t i n g d i f f e r e n t s t a t e .
2024-06-05 07:31:49 +01:00
let newData = Data ( " 😎 " . utf8 )
2023-10-27 10:09:12 +01:00
try keychain . setPINCodeBiometricState ( newData )
// T h e n t h e s t a t e s h o u l d b e u p d a t e d .
XCTAssertTrue ( keychain . containsPINCodeBiometricState ( ) , " The keychain should still contain biometric state. " )
XCTAssertNotEqual ( keychain . pinCodeBiometricState ( ) , data , " The stored biometric state shouldn't match the old value. " )
XCTAssertEqual ( keychain . pinCodeBiometricState ( ) , newData , " The stored biometric state should match the new value. " )
}
func testRemovePINCodeBiometricState ( ) throws {
// G i v e n a k e y c h a i n t h a t c o n t a i n s P I N c o d e b i o m e t r i c s t a t e .
2024-06-05 07:31:49 +01:00
let data = Data ( " Face ID " . utf8 )
2023-10-27 10:09:12 +01:00
try keychain . setPINCodeBiometricState ( data )
XCTAssertTrue ( keychain . containsPINCodeBiometricState ( ) , " The keychain should contain the biometric state. " )
XCTAssertEqual ( keychain . pinCodeBiometricState ( ) , data , " The stored biometric state should match what was set. " )
// W h e n r e m o v i n g t h e s t a t e .
keychain . removePINCodeBiometricState ( )
// T h e n t h e s t a t e s h o u l d n o l o n g e r b e s t o r e d .
XCTAssertFalse ( keychain . containsPINCodeBiometricState ( ) , " The keychain should no longer contain the biometric state. " )
XCTAssertNil ( keychain . pinCodeBiometricState ( ) , " There shouldn't be any stored biometric state after removing it. " )
}
2022-06-15 10:10:22 +01:00
}