Genius suspected their lyrics were being scraped by Google but they needed a way to prove it. So they watermarked some of their lyrics with a message in Morse code. It read Red Handed. It was encoded using different types of apostrophes. I suspect they used U+0027 APOSTROPHE and U+2019 RIGHT SINGLE QUOTATION MARK since those are the most commonly used but I can't be sure.
Let's write up a solution to this problem: encoding a message in apostrophes via Morse code. We'll assume that just those two apostrophes are available. I'm choosing Python because it's nice for string manipulation.
The important parts: our two markers, and the alphabet in Morse.
APOSTROPHE = '\''RIGHT_MARK = '’'morse = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....","..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.","--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-","-.--", "--.."]
First, we need a way of converting text to Morse code.
'-.-..--' (which in a way looks like a sleeping cat!).
# flatten a 2D listflatten = lambda l: [item for sublist in l for item in sublist]# :text: a message made up of [a-zA-Z]def to_morse(message):m = [morse[ord(c)-97] for c in message.lower()]return flatten(m)
I've chosen APOSTROPHE to represent the dot in Morse as it has a lower Unicode value. RIGHT SINGLE QUOTATION MARK represents a dash. Once we have our message in Morse, we convert it to an apostrophe message using substitution. We then iterate over the source text until we hit an apostrophe and exchange it for the next character in our message. Take a look.
# :message: a message to encode# :source: a source text with apostrophesdef insert_morse(message, source):# a list of dots and dashesmorse = to_morse(message)# a list of the two types of apostrophessecret = [APOSTROPHE if m == '.' else RIGHT_MARK for m in morse]inserted = for c in source:if len(secret) > 0 and (c == APOSTROPHE or c == RIGHT_MARK):inserted.append(secret.pop(0))else:inserted.append(c)return ''.join(inserted)
If you're wondering how to decode a source text that has such a message encoded within it's actually far simpler.
# :source: a source encoded via insert_morsedef retrieve_morse(source):morse = for c in source:if c == APOSTROPHE:morse.append('.')elif c == RIGHT_MARK:morse.append('-')return ''.join(morse)
Let's look at an example of this in action.
source_text = 'Hello \'\' World, \'\' no \'\' secrets \' here!'secret = 'cat'encoded_message = insert_morse(secret, source_text)retrieved_message = retrieve_morse(encoded_message)print(encoded_message) # Hello ’' World, ’' no '’ secrets ’ here!print(retrieved_message) # -.-..--
Pretty neat. If you thought that was cool, wait until you find out how people have been using zero-width spaces to get up to more advanced fingerprinting trickery.
The exercise of decoding and understanding a Morse message without spaces is left up to the reader! I'd love to hear your thoughts and resources on hiding data in plain sight on the web.
Grab the code from this article on GitHub.