{"id":19131747,"url":"https://github.com/mwerner/coin","last_synced_at":"2025-07-25T16:10:36.874Z","repository":{"id":74449323,"uuid":"231164085","full_name":"mwerner/coin","owner":"mwerner","description":null,"archived":false,"fork":false,"pushed_at":"2020-01-01T01:39:54.000Z","size":8,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-22T17:36:58.954Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mwerner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-01T01:37:15.000Z","updated_at":"2020-01-01T01:39:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"d4986fb3-85af-4666-972d-e41f58a3e3ff","html_url":"https://github.com/mwerner/coin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mwerner/coin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwerner%2Fcoin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwerner%2Fcoin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwerner%2Fcoin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwerner%2Fcoin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mwerner","download_url":"https://codeload.github.com/mwerner/coin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwerner%2Fcoin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267029423,"owners_count":24024202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T06:15:57.287Z","updated_at":"2025-07-25T16:10:36.854Z","avatar_url":"https://github.com/mwerner.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Coin\n\nThis repository is to break the encryption of the message:\n\n```\nVm gp vdqvzh ljjmuwaxc yfw lipxbh li qc zrukvauo xikhxoljkhuhk, du zzzyc li vgvvdjjx dsz wuqvboi kopjttdtgh.\n```\n\n## Vigenère Cipher\n\nThe first attempt was a simple rot encryption, then a Vigenère.\n\n```ruby\nload './vigenere.rb'\nclient = Vigenere.new('erudite')\nencrypted = client.encrypt([13, 2, 22, 4]) #=\u003e \"rpyzvri\"\nclient = Vigenere.new(encrypted)\ndecrypted = client.decrypt([13, 2, 22, 4])\n```\n\nThese didn't yield much that looked correct so I moved on to brute force\n\n## Breaker\n\nThe `breaker.rb` is an attempt to use the Vigenère Scheme to brute force the message. It's configured to accept whatever scheme you like. However, I suspect it may only work correctly with Vigenère. Iterating over all the permuations within four letters, we collect every possible deciphered text and compare it to a dictionary of words. If there is a small set of words that match, we list them as possible keys used for the enciphered text.\n\n```ruby\nload './vigenere.rb'\nload './breaker.rb'\nclient = Vigenere.new('njczrlm')\nbreaker = Breaker.new(client)\nbreaker.run\n```\n\n```\nBrute force breaking: njczrlm\nComparing all 4 keyed polyalphabetic encryptions of njczrlm to 234371 words\nChecking 1 range:\n26 words\nMatches Found: 0\nChecking 2 range:\n676 words\nMatches Found: 0\nChecking 3 range:\n17576 words\nMatches Found: 0\nChecking 4 range:\n456976 words\nMatches Found: 1\n[17, 8, 18, 4] erudite\n```\n\nHere we were able to break the cipher without knowing the keyword. The breaker takes a few seconds at four letters, but beyond that it takes several minutes.\n\n## Porta Cipher\n\nI was then informed that the cipher used was a Porta Filter. Here's the usage:\n\n```ruby\nload './porta.rb'\nmessage = 'defendtheeastwallofthecastle'\nscheme = Porta.new('defendtheeastwallofthecastle')\n\nmessage = 'doanotherthingforthecastle'\nscheme = Porta.new(message)\nresult = scheme.encrypt('fortification')\nscheme = Porta.new(result)\nscheme.decrypt('fortification')\n```\n\nUsing this, we'll try breaking the message\n\n```ruby\nload './porta.rb'\nmessage = \"Vm gp vdqvzh ljjmuwaxc yfw lipxbh li qc zrukvauo xikhxoljkhuhk du zzzyc li vgvvdjjx dsz wuqvboi kopjttdtgh\"\nscheme = Porta.new(message)\nscheme.decrypt('love')\n#=\u003e \"dtqadxgghovyotkhsdzjxcvxkdywqpgrhkkzdukmfpuwfhvypokwpxkkhfbrqplvdbnyodndhckbdvexphfybmneyo\"\n```\n\nThis isn't right. It could be due to the key we're providing. Let's adapt breaker to make use of the `Porta` scheme. Making the Porta scheme accept an array of characters instead of a word. Let's test the breaker with a previous message, with a smaller key of course:\n\n```ruby\nload './porta.rb'\nmessage = 'defend'\nscheme = Porta.new(message)\nresult = scheme.encrypt('fort')\n#=\u003e \"synnlx\"\n\nload './breaker.rb'\nclient = Porta.new(result)\nbreaker = Breaker.new(client)\nbreaker.run\n```\n\n```\nBrute force breaking: synnlx\nComparing all 4 keyed polyalphabetic encryptions of synnlx to 234371 words\nChecking 1 range:\n13 words\nMatches Found: 0\nChecking 2 range:\n169 words\nMatches Found: 0\nChecking 3 range:\n2197 words\nMatches Found: 0\nChecking 4 range:\n28561 words\nMatches Found: 5\n[5, 15, 3, 1] demand\n[5, 15, 17, 19] defend\n[5, 15, 25, 11] debind\n[7, 9, 19, 25] chebog\n[15, 23, 7, 11] lakism\n```\n\nLet's use this with one of the words from the original message to check if we've got the wrong key:\n\n```ruby\nload './porta.rb'\nload './breaker.rb'\nclient = Porta.new(\"xikhxoljkhuhk\")\nbreaker = Breaker.new(client)\nbreaker.run\n```\n\nHere we have:\n\n```\nChecking 1 range:\n13 words\nMatches Found: 0\nChecking 2 range:\n169 words\nMatches Found: 0\nChecking 3 range:\n2197 words\nMatches Found: 0\nChecking 4 range:\n28561 words\nMatches Found: 1\n[17, 13, 7, 23] consciousness\n```\n\nSeems promising, let's convert those indexed into a key word (\"comes out to `rnhx` for some reason\"), and then use it to break the entire message:\n\n```ruby\nmessage = \"Vm gp vdqvzh ljjmuwaxc yfw lipxbh li qc zrukvauo xikhxoljkhuhk du zzzyc li vgvvdjjx dsz wuqvboi kopjttdtgh\"\nclient = Porta.new(message)\nclient.decrypt('rnhx')\n```\n\nThis worked correctly. Downloading this code and running it will give you the response.\n\n## Result\n\nI was told the key word was `love`. It seems the source of the tableau I was using was different than the one used when the cipher was created. I took the one [from the ACA](https://www.cryptogram.org/downloads/aca.info/ciphers/Porta.pdf), but it seems there are some other slightly different tableaus you can find on the internet. Fun project!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwerner%2Fcoin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmwerner%2Fcoin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwerner%2Fcoin/lists"}