Skip to content

Instantly share code, notes, and snippets.

@rbobillot
Last active September 13, 2017 16:29
Show Gist options
  • Save rbobillot/47b30d40a489615ff947b16d1c17216d to your computer and use it in GitHub Desktop.
Save rbobillot/47b30d40a489615ff947b16d1c17216d to your computer and use it in GitHub Desktop.
object HideText {
type Homoglyphs = Map[Char, Array[Char]]
val homoglyphsDict: Homoglyphs = Map(
' ' -> Array("2000","2001","2002","2003","2004","2005","2006","2007","2008","2009","200A","2028","2029","202F","205F"),
'!' -> Array("FF01"),
'"' -> Array("FF02"),
'$' -> Array("FF04"),
'%' -> Array("FF05"),
'&' -> Array("FF06"),
'\'' -> Array("FF07"),
'(' -> Array("FF08"),
')' -> Array("FF09"),
'*' -> Array("FF0A"),
'+' -> Array("FF0B"),
',' -> Array("FF0C"),
'-' -> Array("FF0D"),
'.' -> Array("FF0E"),
'/' -> Array("FF0F"),
'0' -> Array("FF10"),
'1' -> Array("FF11"),
'2' -> Array("FF12"),
'3' -> Array("FF13"),
'4' -> Array("FF14"),
'5' -> Array("FF15"),
'6' -> Array("FF16"),
'7' -> Array("FF17"),
'8' -> Array("FF18"),
'9' -> Array("FF19"),
':' -> Array("FF1A"),
';' -> Array("FF1B"),
'<' -> Array("FF1C"),
'=' -> Array("FF1D"),
'>' -> Array("FF1E"),
'?' -> Array("FF1F"),
'@' -> Array("FF20"),
'A' -> Array("FF21","0391","0410"),
'B' -> Array("FF22","0392","0412"),
'C' -> Array("FF23","03F9","216D"),
'D' -> Array("FF24"),
'E' -> Array("FF25","0395","0415"),
'F' -> Array("FF26"),
'G' -> Array("FF27"),
'H' -> Array("FF28","0397","041D"),
'I' -> Array("FF29","0399","0406"),
'J' -> Array("FF2A"),
'K' -> Array("FF2B","039A","212A"),
'L' -> Array("FF2C"),
'M' -> Array("FF2D","039C","041C"),
'N' -> Array("FF2E"),
'O' -> Array("FF2F","039F","041E"),
'P' -> Array("FF30","03A1","0420"),
'Q' -> Array("FF31"),
'R' -> Array("FF32"),
'S' -> Array("FF33"),
'T' -> Array("FF34","03A4","0422"),
'U' -> Array("FF35"),
'V' -> Array("FF36","0474","2164"),
'W' -> Array("FF37"),
'X' -> Array("FF38","03A7","2169"),
'Y' -> Array("FF39","03A5","04AE"),
'Z' -> Array("FF3A"),
'[' -> Array("FF3B"),
'\\' -> Array("FF3C"),
']' -> Array("FF3D"),
'^' -> Array("FF3E"),
'_' -> Array("FF3F"),
'`' -> Array("FF40"),
'a' -> Array("FF41"),
'b' -> Array("FF42"),
'c' -> Array("FF43","03F2","0441"),
'd' -> Array("FF44"),
'e' -> Array("FF45"),
'f' -> Array("FF46"),
'g' -> Array("FF47"),
'h' -> Array("FF48"),
'i' -> Array("FF49","0456","2170"),
'j' -> Array("FF4A"),
'k' -> Array("FF4B"),
'l' -> Array("FF4C"),
'm' -> Array("FF4D"),
'n' -> Array("FF4E"),
'o' -> Array("FF4F","03BF","043E"),
'p' -> Array("FF50"),
'q' -> Array("FF51"),
'r' -> Array("FF52"),
's' -> Array("FF53"),
't' -> Array("FF54"),
'u' -> Array("FF55"),
'v' -> Array("FF56","03BD","2174"),
'w' -> Array("FF57"),
'x' -> Array("FF58","0445","2179"),
'y' -> Array("FF59"),
'z' -> Array("FF5A"),
'{' -> Array("FF5B"),
'|' -> Array("FF5C"),
'}' -> Array("FF5D"),
'~' -> Array("FF5E")
).mapValues(_.map(hex => Integer.parseInt(hex, 16).toChar))
def binToNat(bs: String): BigInt = BigInt(bs, 2)
def padWith[T](e: T)(s: String, len: BigInt) = s.reverse.padTo(len.intValue, e).reverse.mkString
// ensure secretBits are divisible by alphabetBitsSize
def padSecretBits(sb: String, secretAlphabet: String, secretAlphabetBitsSize: Int): String = {
if (BigInt(sb) % secretAlphabetBitsSize == 0) sb
else sb + padWith(0)("0", secretAlphabetBitsSize - (BigInt(sb) % secretAlphabetBitsSize))
}
def secretToBinaryString(secret: String, secretAlphabet: String, secretAlphabetBitsSize: Int): String = {
val secretBits = secret.foldLeft(""){ (sb, c) =>
sb + padWith(0)(secretAlphabet.indexOf(c).toBinaryString, secretAlphabetBitsSize)
}
padSecretBits(secretBits, secretAlphabet, secretAlphabetBitsSize)
}
def replaceChar(c: Char, hg: Array[Char], sbits: String) = {
val hgBitsSize = (hg.size + 1).toBinaryString.size - 1
val chunkToEncode = sbits take hgBitsSize
val nextSecretBits = sbits drop hgBitsSize
val n = binToNat(chunkToEncode).intValue
nextSecretBits -> hg.lift(n-1).getOrElse(c)
}
def cipher(msg: String, sbits: String, dict: Homoglyphs, result: String = ""): String =
dict.get(msg.head) match {
case None => cipher(msg drop 1, sbits, dict, result + msg.head)
case Some(homoglyphs) => (sbits, msg.head) match {
case ("", ' ') => result
case ("", _) => cipher(msg drop 1, sbits, dict, result + msg.head)
case (_, c) => val (nbits, nchar) = replaceChar(c, homoglyphs, sbits)
cipher(msg drop 1, nbits, dict, result + nchar)
}
}
def encode(msg: String, secret: String)(dict: Homoglyphs): String = {
val secretAlphabet: String = " abcdefghijklmnopqrstuvwxyz123456789'0.:/\\%-_?&;" // dict.keys.mkString
val secretAlphabetBitsSize: Int = secretAlphabet.size.toBinaryString.size
val secretBits: String = secretToBinaryString(secret, secretAlphabet, secretAlphabetBitsSize)
cipher(msg, secretBits, dict)
}
val LOREM = """Lorem ipsum nunc risus lacinia nisi feugiat etiam ad id pellentesque sit suscipit sociosqu nullam, turpis euismod velit id aliquam luctus praesent congue luctus mauris hendrerit posuere suscipit fringilla aptent egestas vivamus scelerisque id nulla inceptos facilisis, ac vehicula bibendum tortor aliquet tristique curabitur elit facilisis, quisque primis urna interdum nisl nulla class litora convallis ut tellus."""
def main(av: Array[String]): Unit =
av.toList match {
case str :: sec :: Nil => println(encode(str, sec)(homoglyphsDict))
case sec :: Nil => println(encode(LOREM, sec)(homoglyphsDict)) // error 403
case _ => println("usage: scala hide.jar <string> <secret>")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment