mirror of
https://github.com/bitwarden/android.git
synced 2026-08-01 18:53:39 -05:00
Add 'Have I Been Pwnd' method that returns a breach count (#281)
This commit is contained in:
committed by
Álison Fernandes
parent
dfc653b72e
commit
912d6b62fe
+7
@@ -5,6 +5,13 @@ package com.x8bit.bitwarden.data.auth.datasource.network.service
|
||||
*/
|
||||
interface HaveIBeenPwnedService {
|
||||
|
||||
/**
|
||||
* Check to see if the given password has been breached. Returns the number of breaches.
|
||||
*/
|
||||
suspend fun getPasswordBreachCount(
|
||||
password: String,
|
||||
): Result<Int>
|
||||
|
||||
/**
|
||||
* Check to see if the given password has been breached. Returns true if breached.
|
||||
*/
|
||||
|
||||
+24
-12
@@ -6,7 +6,7 @@ import java.security.MessageDigest
|
||||
class HaveIBeenPwnedServiceImpl(private val api: HaveIBeenPwnedApi) : HaveIBeenPwnedService {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override suspend fun hasPasswordBeenBreached(password: String): Result<Boolean> {
|
||||
override suspend fun getPasswordBreachCount(password: String): Result<Int> {
|
||||
// Hash the password:
|
||||
val hashedPassword = MessageDigest
|
||||
.getInstance("SHA-1")
|
||||
@@ -18,19 +18,31 @@ class HaveIBeenPwnedServiceImpl(private val api: HaveIBeenPwnedApi) : HaveIBeenP
|
||||
return api
|
||||
.fetchBreachedPasswords(hashPrefix = hashPrefix)
|
||||
.mapCatching { responseBody ->
|
||||
val allPwnedPasswords = responseBody.string()
|
||||
responseBody.string()
|
||||
// First split the response by newline: each hashed password is on a new line.
|
||||
.split("\r\n")
|
||||
.map { pwnedSuffix ->
|
||||
// Then remove everything after the ":", since we only want the pwned hash:
|
||||
// Before: 20d61603aba324bf08799896110561f05e1ad3be:12
|
||||
// After: 20d61603aba324bf08799896110561f05e1ad3be
|
||||
pwnedSuffix.substring(0, endIndex = pwnedSuffix.indexOf(":"))
|
||||
.split("\r\n", "\r", "\n")
|
||||
.associate { pwnedSuffix ->
|
||||
// Then split everything on the ":", since we want to compare the pwned
|
||||
// hash but we also want to return the count:
|
||||
// Pattern: <pwnd_hash_suffix>:<breach_count>
|
||||
// Example: 20d61603aba324bf08799896110561f05e1ad3be:12
|
||||
val split = pwnedSuffix.split(":")
|
||||
split[0] to split[1]
|
||||
}
|
||||
// Then see if any of those passwords match our full password hash:
|
||||
allPwnedPasswords.any { pwnedSuffix ->
|
||||
(hashPrefix + pwnedSuffix).equals(hashedPassword, ignoreCase = true)
|
||||
}
|
||||
.entries
|
||||
.find { (pwnedSuffix, _) ->
|
||||
// Then see if any of those passwords match our full password hash:
|
||||
(hashPrefix + pwnedSuffix).equals(hashedPassword, ignoreCase = true)
|
||||
}
|
||||
// If we found a match, return the value as this is the number of breaches.
|
||||
?.value
|
||||
?.toIntOrNull()
|
||||
?: 0
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun hasPasswordBeenBreached(
|
||||
password: String,
|
||||
): Result<Boolean> = getPasswordBreachCount(password)
|
||||
.map { numberOfBreaches -> numberOfBreaches > 0 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user