diff --git a/network/src/main/kotlin/com/bitwarden/network/model/SyncResponseJson.kt b/network/src/main/kotlin/com/bitwarden/network/model/SyncResponseJson.kt index d830ad2131..999d0b2574 100644 --- a/network/src/main/kotlin/com/bitwarden/network/model/SyncResponseJson.kt +++ b/network/src/main/kotlin/com/bitwarden/network/model/SyncResponseJson.kt @@ -142,6 +142,8 @@ data class SyncResponseJson( * @property isEmailVerified If the profile has a verified email. * @property isTwoFactorEnabled If the profile has two factor authentication enabled. * @property privateKey The private key of the profile (nullable). + * @property accountKeys The account keys associated with the profile. This is temporarily + * nullable to maintain backwards compatibility. * @property isPremium If the profile is premium. * @property culture The culture of the profile (nullable). * @property name The name of the profile (nullable). @@ -175,9 +177,13 @@ data class SyncResponseJson( @SerialName("twoFactorEnabled") val isTwoFactorEnabled: Boolean, + @Deprecated("Use accountKeys instead", ReplaceWith("accountKeys")) @SerialName("privateKey") val privateKey: String?, + @SerialName("accountKeys") + val accountKeys: AccountKeys?, + @SerialName("premium") val isPremium: Boolean, @@ -410,6 +416,77 @@ data class SyncResponseJson( @SerialName("managePolicies") val shouldManagePolicies: Boolean, ) + + /** + * Represents private keys in the vault response. + * + * @property signatureKeyPair The signature key pair of the profile. + * @property publicKeyEncryptionKeyPair The public key encryption key pair of the profile. + * @property securityState The security state of the profile (nullable). + */ + @Serializable + data class AccountKeys( + @SerialName("signatureKeyPair") + val signatureKeyPair: SignatureKeyPair?, + + @SerialName("publicKeyEncryptionKeyPair") + val publicKeyEncryptionKeyPair: PublicKeyEncryptionKeyPair, + + @SerialName("securityState") + val securityState: SecurityState?, + ) { + + /** + * Represents a signature key pair in the vault response. + * + * @property wrappedSigningKey The wrapped signing key of the signature key pair. + * @property verifyingKey The verifying key of the signature key pair. + */ + @Serializable + data class SignatureKeyPair( + @SerialName("wrappedSigningKey") + val wrappedSigningKey: String, + + @SerialName("verifyingKey") + val verifyingKey: String, + ) + + /** + * Represents a public key encryption key pair in the vault response. + * + * @property wrappedPrivateKey The wrapped private key of the public key encryption key + * pair. + * @property publicKey The public key of the public key encryption key pair. + * @property signedPublicKey The signed public key of the public key encryption key pair + * (nullable). + */ + @Serializable + data class PublicKeyEncryptionKeyPair( + @SerialName("wrappedPrivateKey") + val wrappedPrivateKey: String, + + @SerialName("publicKey") + val publicKey: String, + + @SerialName("signedPublicKey") + val signedPublicKey: String?, + ) + + /** + * Represents security state in the vault response. + * + * @property securityState The security state of the profile. + * @property securityVersion The security version of the profile. + */ + @Serializable + data class SecurityState( + @SerialName("securityState") + val securityState: String, + + @SerialName("securityVersion") + val securityVersion: Int, + ) + } } /** diff --git a/network/src/test/kotlin/com/bitwarden/network/service/SyncServiceTest.kt b/network/src/test/kotlin/com/bitwarden/network/service/SyncServiceTest.kt index af4eda61cd..577df5ab8f 100644 --- a/network/src/test/kotlin/com/bitwarden/network/service/SyncServiceTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/service/SyncServiceTest.kt @@ -51,6 +51,23 @@ private const val SYNC_SUCCESS_JSON = """ "twoFactorEnabled": false, "key": "mockKey-1", "privateKey": "mockPrivateKey-1", + "accountKeys": { + "signatureKeyPair": { + "wrappedSigningKey": "mockWrappedSigningKey-1", + "verifyingKey": "mockVerifyingKey-1" + }, + "publicKeyEncryptionKeyPair": { + "wrappedPrivateKey": "mockWrappedPrivateKey-1", + "publicKey": "mockPublicKey-1", + "signedPublicKey": "mockSignedPublicKey-1", + "object": "publicKeyEncryptionKeyPair" + }, + "securityState": { + "securityState": "mockSecurityState-1", + "securityVersion": 1 + }, + "object": "privateKeys" + }, "securityStamp": "mockSecurityStamp-1", "forcePasswordReset": false, "usesKeyConnector": false, diff --git a/network/src/testFixtures/kotlin/com/bitwarden/network/model/SyncResponseProfileUtil.kt b/network/src/testFixtures/kotlin/com/bitwarden/network/model/SyncResponseProfileUtil.kt index 527713709b..80bc753c2f 100644 --- a/network/src/testFixtures/kotlin/com/bitwarden/network/model/SyncResponseProfileUtil.kt +++ b/network/src/testFixtures/kotlin/com/bitwarden/network/model/SyncResponseProfileUtil.kt @@ -18,6 +18,7 @@ fun createMockProfile( isEmailVerified: Boolean = false, isTwoFactorEnabled: Boolean = false, privateKey: String? = "mockPrivateKey-$number", + accountKeys: SyncResponseJson.Profile.AccountKeys? = createMockPrivateKeys(number = number), isPremium: Boolean = false, culture: String? = "mockCulture-$number", name: String? = "mockName-$number", @@ -43,6 +44,7 @@ fun createMockProfile( isEmailVerified = isEmailVerified, isTwoFactorEnabled = isTwoFactorEnabled, privateKey = privateKey, + accountKeys = accountKeys, isPremium = isPremium, culture = culture, name = name, @@ -57,6 +59,60 @@ fun createMockProfile( creationDate = creationDate, ) +/** + * Create a mock set of private keys with a given [number]. + */ +fun createMockPrivateKeys( + number: Int, +): SyncResponseJson.Profile.AccountKeys = + SyncResponseJson.Profile.AccountKeys( + signatureKeyPair = createMockSignatureKeyPair(number = number), + publicKeyEncryptionKeyPair = createMockPublicKeyEncryptionKeyPair(number = number), + securityState = createMockSecurityState(number = number), + ) + +/** + * Create a mock [SyncResponseJson.Profile.AccountKeys.SecurityState] with a given [number]. + */ +fun createMockSecurityState( + number: Int, + securityState: String = "mockSecurityState-$number", + securityVersion: Int = number, +): SyncResponseJson.Profile.AccountKeys.SecurityState = + SyncResponseJson.Profile.AccountKeys.SecurityState( + securityState = securityState, + securityVersion = securityVersion, + ) + +/** + * Create a mock [SyncResponseJson.Profile.AccountKeys.PublicKeyEncryptionKeyPair] with a given + * number. + */ +fun createMockPublicKeyEncryptionKeyPair( + number: Int, + publicKey: String = "mockPublicKey-$number", + wrappedPrivateKey: String = "mockWrappedPrivateKey-$number", + signedPublicKey: String? = "mockSignedPublicKey-$number", +): SyncResponseJson.Profile.AccountKeys.PublicKeyEncryptionKeyPair = + SyncResponseJson.Profile.AccountKeys.PublicKeyEncryptionKeyPair( + publicKey = publicKey, + wrappedPrivateKey = wrappedPrivateKey, + signedPublicKey = signedPublicKey, + ) + +/** + * Create a mock [SyncResponseJson.Profile.AccountKeys.SignatureKeyPair] with a given number. + */ +fun createMockSignatureKeyPair( + number: Int, + wrappedSigningKey: String = "mockWrappedSigningKey-$number", + verifyingKey: String = "mockVerifyingKey-$number", +): SyncResponseJson.Profile.AccountKeys.SignatureKeyPair = + SyncResponseJson.Profile.AccountKeys.SignatureKeyPair( + wrappedSigningKey = wrappedSigningKey, + verifyingKey = verifyingKey, + ) + /** * Create a mock [SyncResponseJson.Profile.Organization] with a given [number]. */