@@ -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