Mask emails so the same input always produces the same output. This is critical for cross-table referential integrity — if alex@gmail.com appears in users.email and orders.contact_email, both must mask to the same value, or joins break.
Use SHA-256 over "$salt:$email", take the first 16 hex chars as local part, append @masked.io.
Tests this task must pass
Determinism — same input gives the same output across calls.
Different inputs → different outputs — alex@gmail.com and maria@yahoo.com must not collide.
Null in → null out — no exception.
Output shape — contains @, ends with masked.io.
No leakage — masked output must not contain the original local part or domain (no "alex", no "gmail").
Salt-sensitivity — same email with a different salt produces a different output.
import java.security.MessageDigest
//sampleStart
fun maskEmail(email: String?, salt: String = "s3cret"): String? {
// Return null if email is null
// Use SHA-256 hash of "$salt:$email"
// Take first 16 hex chars as local part
// Domain: "masked.io"
TODO()
}
//sampleEnd
fun main() {
// Test 1: Determinism
val a = maskEmail("alex@gmail.com")
val b = maskEmail("alex@gmail.com")
check(a == b) { "FAIL: same input must give same output. Got $a and $b" }
println("✅ Test 1: Deterministic — $a")
// Test 2: Different inputs → different outputs
val c = maskEmail("maria@yahoo.com")
check(a != c) { "FAIL: different inputs must give different outputs" }
println("✅ Test 2: Different inputs → different outputs")
// Test 3: Null handling
check(maskEmail(null) == null) { "FAIL: null input should return null" }
println("✅ Test 3: Null → null")
// Test 4: Valid email format
check(a!!.contains("@")) { "FAIL: must contain @" }
check(a.endsWith("masked.io")) { "FAIL: must end with masked.io" }
println("✅ Test 4: Valid format — $a")
// Test 5: Doesn't contain original
check(!a.contains("alex")) { "FAIL: must not contain original name" }
check(!a.contains("gmail")) { "FAIL: must not contain original domain" }
println("✅ Test 5: Original data not present")
// Test 6: Different salt → different output
val d = maskEmail("alex@gmail.com", "other_salt")
check(a != d) { "FAIL: different salt must give different output" }
println("✅ Test 6: Different salt → different output")
println("\n🎉 ALL TESTS PASSED!")
}
Hint
MessageDigest.getInstance("SHA-256").digest(bytes) returns a ByteArray. Convert to hex with joinToString("") { "%02x".format(it) }, then substring(0, 16).
Solution
fun maskEmail(email: String?, salt: String = "s3cret"): String? {
if (email == null) return null
val bytes = MessageDigest.getInstance("SHA-256")
.digest("$salt:$email".toByteArray())
val hex = bytes.joinToString("") { "%02x".format(it) }
return "${hex.substring(0, 16)}@masked.io"
}