Skip to content

Commit 8b83cac

Browse files
Solve decoding of greedy sampling in last sentence
1 parent 806ce40 commit 8b83cac

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

app/src/main/java/org/vonderheidt/hips/utils/Steganography.kt

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ object Steganography {
1515
conversionMode: ConversionMode = Settings.conversionMode,
1616
steganographyMode: SteganographyMode = Settings.steganographyMode
1717
): String {
18+
// Step 0: Prepare secret message by appending ASCII NUL character
19+
val preparedSecretMessage = prepare(secretMessage)
20+
1821
// Step 1: Convert secret message to a (compressed) binary representation
1922
val plainBits = when (conversionMode) {
20-
ConversionMode.Arithmetic -> { Arithmetic.decode("", secretMessage) } // Stegasuras: Arithmetic binary conversion is just decoding with empty context
21-
ConversionMode.UTF8 -> { UTF8.encode(secretMessage) }
23+
ConversionMode.Arithmetic -> { Arithmetic.decode("", preparedSecretMessage) } // Stegasuras: Arithmetic binary conversion is just decoding with empty context
24+
ConversionMode.UTF8 -> { UTF8.encode(preparedSecretMessage) }
2225
}
2326

2427
// Step 2: Encrypt binary representation of secret message
@@ -54,11 +57,46 @@ object Steganography {
5457
val plainBits = Crypto.decrypt(cipherBits)
5558

5659
// Invert step 1
57-
val secretMessage = when (conversionMode) {
60+
val preparedSecretMessage = when (conversionMode) {
5861
ConversionMode.Arithmetic -> { Arithmetic.encode("", plainBits) } // Stegasuras: Arithmetic string conversion is just encoding with empty context
5962
ConversionMode.UTF8 -> { UTF8.decode(plainBits) }
6063
}
6164

65+
// Invert step 0
66+
val secretMessage = unprepare(preparedSecretMessage)
67+
68+
return secretMessage
69+
}
70+
71+
/**
72+
* Function to prepare a secret message for binary encoding.
73+
*
74+
* Appends the ASCII NUL character to the original secret message. Needed to remove artefacts from greedy sampling after binary decoding.
75+
*
76+
* @param secretMessage A secret message.
77+
* @return The prepared secret message.
78+
*/
79+
private fun prepare(secretMessage: String): String {
80+
// Kotlin doesn't use NUL-terminated strings, instead makes them immutable and stores length
81+
// So using NUL as a character in a Kotlin string can't cause any collisions
82+
val preparedSecretMessage = secretMessage + '\u0000'
83+
84+
return preparedSecretMessage
85+
}
86+
87+
/**
88+
* Function to unprepare a secret message after binary decoding.
89+
*
90+
* Strips the ASCII NUL character and everything after it. Therefore removes any artefacts from greedy sampling, rendering the original secret message.
91+
*
92+
* @param preparedSecretMessage A prepared secret message.
93+
* @return The secret message.
94+
*/
95+
private fun unprepare(preparedSecretMessage: String): String {
96+
val secretMessage = preparedSecretMessage
97+
.split('\u0000')
98+
.first()
99+
62100
return secretMessage
63101
}
64102
}

0 commit comments

Comments
 (0)