Last active
June 7, 2021 14:48
-
-
Save rob-brown/cdea88cbbdd6749a25db0cca0605498e to your computer and use it in GitHub Desktop.
Encode secret messages into text using zero-width spaces
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule SecretMessage do | |
@zero "\u200C" | |
@one "\u200D" | |
def encode(text, secret) when byte_size(text) > byte_size(secret) do | |
secret | |
|> obfuscate | |
|> Stream.concat(Stream.repeatedly fn -> "" end) | |
|> Stream.zip(String.codepoints text) | |
|> Enum.map(fn {x, y} -> [y, x] end) | |
|> IO.iodata_to_binary | |
end | |
def encode(_text, _secret) do | |
raise "Text must be longer than secret" | |
end | |
def decode(text) do | |
text | |
|> String.codepoints | |
|> Stream.filter(& &1 in [@zero, @one]) | |
|> Stream.map(fn @zero -> <<0::1>>; @one -> <<1::1>> end) | |
|> Stream.chunk_every(8) | |
|> Stream.map(&deobfuscate/1) | |
|> Enum.filter(& bit_size(&1) == 8) | |
|> IO.iodata_to_binary | |
end | |
defp obfuscate(secret) do | |
for << b::1 <- secret >> do | |
case b do | |
0 -> @zero | |
1 -> @one | |
end | |
end | |
|> Enum.chunk_every(8) | |
end | |
defp deobfuscate(bits) do | |
bits |> Enum.reduce(<<>>, fn x, acc -> <<acc::bitstring, x::bitstring>> end) | |
end | |
end | |
text = "This is a test, it is only a test" | |
secret = "Secret secret" | |
IO.puts "Text:" | |
IO.puts text | |
IO.puts "Secret:" | |
IO.puts secret | |
IO.puts "Encoded:" | |
SecretMessage.encode(text, secret) |> IO.puts | |
IO.puts "Decoded:" | |
SecretMessage.encode(text, secret) |> SecretMessage.decode |> IO.puts | |
IO.puts "String length:" | |
SecretMessage.encode(text, secret) |> String.length |> IO.puts | |
IO.puts "Byte size:" | |
SecretMessage.encode(text, secret) |> byte_size |> IO.puts | |
IO.puts "Codepoints:" | |
SecretMessage.encode(text, secret) |> String.codepoints |> IO.inspect(limit: 500) | |
# Run with `elixir SecretMessage.exs` | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment