{"id":36483438,"url":"https://github.com/qua3k/cryptopals","last_synced_at":"2026-01-12T01:05:34.004Z","repository":{"id":57692068,"uuid":"472993596","full_name":"qua3k/cryptopals","owner":"qua3k","description":"This is a repo providing solutions written in Go to the Matasano Cryptopals Challenges.","archived":false,"fork":false,"pushed_at":"2024-09-15T13:56:47.000Z","size":77,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-09-15T15:13:57.661Z","etag":null,"topics":["cryptography","cryptopals"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qua3k.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-23T01:18:09.000Z","updated_at":"2024-09-15T13:56:50.000Z","dependencies_parsed_at":"2022-08-27T19:41:46.714Z","dependency_job_id":null,"html_url":"https://github.com/qua3k/cryptopals","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/qua3k/cryptopals","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qua3k%2Fcryptopals","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qua3k%2Fcryptopals/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qua3k%2Fcryptopals/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qua3k%2Fcryptopals/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qua3k","download_url":"https://codeload.github.com/qua3k/cryptopals/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qua3k%2Fcryptopals/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28330233,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T00:36:25.062Z","status":"ssl_error","status_checked_at":"2026-01-12T00:36:15.229Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["cryptography","cryptopals"],"created_at":"2026-01-12T01:05:29.006Z","updated_at":"2026-01-12T01:05:33.999Z","avatar_url":"https://github.com/qua3k.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cryptopals\n\nThis is a repo providing solutions written in Go to the\n[Matasano Cryptopals Challenges](https://cryptopals.com/).\n\n## Challenge 1\n\nThis challenge asks us to decode a hex-encoded string into a byte slice and\nre-encode it as base64.\n\n## Challenge 2\n\nThis challenge asks us to xor two hex-encoded strings together. This can be\naccomplished by decoding the strings into byte slices and xoring them together.\n\nHere is a tiny truth table for XOR :)\n\n| A \t| B \t| Output \t|\n|---\t|---\t|--------\t|\n| 0 \t| 0 \t| 0      \t|\n| 0 \t| 1 \t| 1      \t|\n| 1 \t| 0 \t| 1      \t|\n| 1 \t| 1 \t| 0      \t|\n\n## Challenge 3\n\nThis challenge asks us to decode a hex-encoded string xored with a single\ncharacter, which should look something like `hello ^ XXXXX`. Since we know that\nthe value of the byte type can only be 0..255 we can brute force the ciphertext\nagainst each value to derive the plaintext and key.\n\nHowever, we want to return the correct plaintext, not 256 potential candidates,\nso we will need additional infrastructure for this challenge. For each decrypted\ncandidate we can perform\n[frequency analysis](https://en.wikipedia.org/wiki/Frequency_analysis). We can\nconstruct a map of the frequency of each letter as it appears in English texts,\nkeeping global state of which decrypted string has the highest score. This\nallows us to return only the highest scoring string rather than all potential\ncandidates, which will prove essential in later challenges.\n\n## Challenge 4\n\nThis challenge asks us to open a file (conveniently located at `testdata/4.txt`)\nand apply our logic from challenge 3. We will need to brute force each string\nand return the highest scoring plaintext.\n\n## Challenge 5\n\nThis challenge asks us to look into decrypt a ciphertext encrypted with\nrepeating-key XOR. We can modify our existing XOR code to instead do `x[i]` ⊕\n`y[i%len(y)]` (modulo length of y).\n\n## Challenge 6\n\nThis challenge is definitely the hardest to understand without a background in\ncryptography. To make it easier we can break it into multiple sections.\n\nFirstly, we will need to understand the concept of\n[hamming distance](https://en.wikipedia.org/wiki/Hamming_distance) at the bit\nlevel. In essence, we are counting the number of differing bits between two\nstrings of equal length. This can be accomplished by xoring the strings together\nand counting the number of bits that are set to one.\n\nAs mentioned in the example, we can validate that our code works correctly by\ntesting it against the strings `this is a test` and `wokka wokka!!!`; it should\ncome out to 37.\n\nThe concept of hamming distance can be applied to help us solve this challenge\nwhen we understand how it interacts with English ASCII. The entire English\nalphabet (uppercase and lowercase) is represented in 52 out of the 256 possible\nvalues in ASCII, and it helps that the letters are conveniently located right\nnext to each other and thus have a normalized average hamming distance far lower\nthan legitimately random data.\n\nA nice thing to note is that when XOR encrypted with the same key, two strings\nhave the same hamming distance as their plaintext forms; it is only a matter of\nus finding the key length through brute force; the noticeably lower normalized\nhamming distance will be our key length :)\n\nThe example algorithm as described on the challenge page advises us to attempt\nto guess the key length from 2 to 40, which entails taking the first two chunks\nof the text of size KEYSIZE (the first block would be `slice[:keysize]`, the\nsecond would be `slice[keysize:keysize*2]`). However, this sample size is too\nsmall to be accurate, which is why they advise taking up to the first four\nchunks of the ciphertext and finding the mean among those. In my testing, I\nfound this to yield the wrong key size still (although it could have just been\nbuggy code), but I was still determined to figure it out without magic\nnumbers.[^1]\n\nIn my research, I happened upon\n[this site discussing this topic in detail](https://carterbancroft.com/breaking-repeating-key-xor-theory)\nas well as another set of cryptopals solutions written in\n[Python](https://github.com/vijithassar/cryptopals-literate-python/blob/master/challenge06.py.md)\nwhich prompted me to begin working on code that would iterate through the\nlength of the ciphertext and compare each chunk (`slice[size*(i+1):size*(i+2)]`)\nagainst the first chunk. This worked as expected, which allowed us to determine\nthe key size.\n\nArmed with the key size, we attempt to split the ciphertext into chunks of\n`len(keysize)`. We transpose the blocks (create a slice of byte slices) by\nplacing the first byte of each chunk in one slice, the second byte in the\nsecond, and so on. We can then attempt to brute force each byte slice with\nscoring + single character XOR; we are then able to reconstruct the key and\nultimately solve the challenge.\n\n## Challenge 7\n\nThis challenge asks us to decrypt a ciphertext encrypted in ECB mode. We use the\n[crypto/aes](https://pkg.go.dev/crypto/aes) package, decrypting the\nciphertext 16 bytes at a time with `Block.Decrypt`.\n\n## Challenge 8\n\nThis challenge takes advantage of the fact that AES-ECB encrypts the same\nplaintext block into the same ciphertext (lack of diffusion). We track the\nencrypted blocks in a map so we can easily lookup the existence of the\nciphertext and determine whether a certain string was encrypted with ECB mode.\n\n## Challenge 9\n\nThis challenge asks us to pad a message to a specific block size with\n[PKCS#7](https://datatracker.ietf.org/doc/html/rfc5652#section-6.3). In essence,\nthis means padding to a specified uint8 length while ensuring the value of the\nadded bytes is equivalent to the number of bytes added.\n\n## Challenge 10\n\nThis challenge asks us to decrypt a AES-CBC encrypted ciphertext. CBC is a\nconfidentiality-only mode whose encryption can be formalized as\n`C[i] = E(P[i] ^ C[i-1])`, with decryption being `P[i] = D(C[i]) ^ C[i-1]`.\n\n## Challenge 11\n\nThis challenge asks us to construct an oracle that encrypts a given input with\nCBC mode 50% of the time and ECB mode the other half of the time. The key should\nbe securely generated with the operating system's CSPRNG (see `crypto/rand`) and\nit should prepend 5-10 random bytes **and** append 5-10 random bytes to the\nplaintext before encryption.\n\nWe will use our previous function to detect ECB mode by crafting 3 contiguous\nblocks and sending it to the oracle. 3 blocks ensures that no matter how many\nbytes are prepended/appended, we will always encrypt two blocks of identical\nplaintext.\n\n## Challenge 12\n\nhttps://book-of-gehn.github.io/articles/2018/06/10/Breaking-ECB.html\n\n## Challenge 13\n\nThis challenge asks us to construct a function that will take an arbitrary input\nfor email (the below map)\n\n    {\n        \"email\": \"foo@bar.com\",\n        \"uid\":   \"10\",\n        \"role\":  \"user\",\n    }\n\nand encode it into \"URL encoded form\" like `email=foo@bar.com\u0026role=user\u0026uid=10`.\nThis input is then encrypted with ECB mode and the oracle is provided to the\nattacker. We aren't able to directly encode the bytes '\u0026' and '=', so we have to\nplay some clever tricks with padding. We know that the block size is 16 bytes,\nso we just have to separate the `key=` and the literal value into separate\nblocks. Using a 4 byte email allows us to get exactly this.\n\n    email=AAAA\u0026role= user\u0026uid=10\n\nWe can then craft a large email so that the word admin is not in the first\nblock, and then replace the first block. The plaintext should ultimately look\nlike the below.\n\n    email=AAAA\u0026role= admin\u0026role=user\u0026 uid=10\n\n## Challenge 14\n\nThis challenge asks us to extend the previous append-only oracle in Challenge 12\nby prepending a consistent, but random number of random bytes. We will need to\npad the prepended bytes to a full block and then solve like Challenge 12.\n\nFor instance, if we had the setup\n\n    PPPP PPPP PPAT TACK ERCO NTRO LLED \u003c- appended bytes go here\n\nwe should pad the prepended bytes to a full block, like so:\n\n    PPPP PPPP PPXX ATTA CKER CONT ROLL ED \u003c- appended bytes go here\n                ^^ padding\n\nthen we should craft an oracle that will append the number of padding bytes\nconsistently when run and solve like 12, so we can have something like the\nbelow:\n\n    PPPP PPPP PPXX \u003c- delete these bytes by slicing\n    ATTA CKER CONT ROLL ED \u003c- appended bytes go here\n\nWe then solve like normal.\n\n## Challenge 15\n\nThis challenge just asks us to modify our unpadding PKCS#7 function to take the\nlast byte and verify that the added bytes are of the correct number and value.\n\n## Challenge 16\n\nThis challenge takes advantage of the lack of authentication of CBC mode to flip\na couple of bits in the ciphertext to get our desired result in the plaintext.\n\nWe should think back to our CBC implementation and remember that the result of\nthe AES decryption pass for each block is the plaintext xored by the previous\nciphertext block. If we can change the previous ciphertext block we can create a\nblock that looks like `C[i] ^ P[i+1] ^ DESIRED_BYTE`. We can just craft a\nplaintext block that looks like `XadminXtrue`, replacing the desired bytes with\nthe capital X and changing the ciphertext byte in the block immediately prior so\nat decryption time it will look like\n`C[i-1] (which is really P[i] ^ C[i-1] ^ DESIRED_BYTE) ^ P[i]`, giving us\n`DESIRED_BYTE` in the final decrypted plaintext.\n\n## Challenge 17\n\nBack from a hiatus :) I'll try to write my own AES implementation in the near\nfuture. Now, onto the challenge.\n\nWe are given the IV and a padding oracle that tells us whether some padding was\nvalid. If we look at CBC decryption, we see the ciphertext of the previous block\nis xored with the decryption of the current block. Thus, changing a bit of the\nciphertext allows us to influence the decryption of the current block. We know\nthat for any given `m ^ m = 0`, so if we modify the last byte, we xor our guess\n`m` with `0x1`; if `m` is correct, then the padding will likely* be valid. Then\nwe modify the next one for a padding of 2, and so on. But as padding gets longer\nwe run into the risk of a collision, and to rectify this you could also\ntemporarily modify the previous one and see if the padding remains valid. I\nhaven't implemented that here, but it would be a trivial addition.\n\n\n[^1]: I looked at Filippo Valsorda's solutions @\n[mostly-harmless/](https://github.com/FiloSottile/mostly-harmless/blob/main/cryptopals/set1.go#L97)\nas well as his livestream but it turns out he just guessed the magic number;\nthis isn't good enough for me.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqua3k%2Fcryptopals","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqua3k%2Fcryptopals","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqua3k%2Fcryptopals/lists"}