{"id":51948334,"url":"https://github.com/ilchen/cryptopals","last_synced_at":"2026-07-29T02:31:51.055Z","repository":{"id":45706809,"uuid":"164209050","full_name":"ilchen/cryptopals","owner":"ilchen","description":"Solutions to https://cryptopals.com problems","archived":false,"fork":false,"pushed_at":"2025-04-21T21:32:04.000Z","size":1410,"stargazers_count":28,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-21T21:37:43.079Z","etag":null,"topics":["cryptography","cryptopals","diffie-hellman","dsa","dual-ec-drbg","ecdh","ecdsa","elligator2","elliptic-curves","galois-fields","gcm","ghash","montgomery-curve","pohlig-hellman","pollard-kangaroo","polynomial-rings","rsa","srp","weierstrass-curves"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ilchen.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-01-05T11:33:01.000Z","updated_at":"2025-04-21T20:43:19.000Z","dependencies_parsed_at":"2023-12-22T14:01:12.435Z","dependency_job_id":"8620bd35-bd12-4835-a0ca-44e6ea7743a5","html_url":"https://github.com/ilchen/cryptopals","commit_stats":{"total_commits":187,"total_committers":2,"mean_commits":93.5,"dds":0.0267379679144385,"last_synced_commit":"c5bb3c2d597f95f49f7168e71885c4609518c2c0"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ilchen/cryptopals","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilchen%2Fcryptopals","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilchen%2Fcryptopals/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilchen%2Fcryptopals/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilchen%2Fcryptopals/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ilchen","download_url":"https://codeload.github.com/ilchen/cryptopals/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilchen%2Fcryptopals/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36014947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-29T02:00:04.910Z","response_time":95,"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":["cryptography","cryptopals","diffie-hellman","dsa","dual-ec-drbg","ecdh","ecdsa","elligator2","elliptic-curves","galois-fields","gcm","ghash","montgomery-curve","pohlig-hellman","pollard-kangaroo","polynomial-rings","rsa","srp","weierstrass-curves"],"created_at":"2026-07-29T02:31:50.956Z","updated_at":"2026-07-29T02:31:51.026Z","avatar_url":"https://github.com/ilchen.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cryptopals\nSolutions to all cryptopals problems: [Sets 1-7](https://cryptopals.com), [Set 8](https://toadstyle.org/cryptopals/).\n\nThe only dependency on top of standard JRE 17 runtime is that on [Lombok](https://projectlombok.org). The project runs\non all subsequent versions of the Java platform such as Java 21 and Java 23.\n\nAdditional dependencies:\n* [Spring Boot 3.x](https://spring.io/projects/spring-boot) for problems requiring a server side part in addition to a client part\n* [The GNU Multiple Precision Arithmentic Library (GMP)](https://gmplib.org)\nand [the JNA-GMP wrapper](https://github.com/square/jna-gmp/tree/master/jnagmp) for it for a more optimized implementation\nof modular exponentiation than that provided by [BigInteger::modPow](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/BigInteger.html#modPow(java.math.BigInteger,java.math.BigInteger)).\nThis is relevant only for a few challenges starting with [Challenge 48](https://github.com/ilchen/cryptopals#challenge-48-bleichenbachers-pkcs-15-padding-oracle-complete-case).\n* [GraalJS engine](https://www.graalvm.org/javascript/) for [Challange 50](https://cryptopals.com/sets/7/challenges/50).\nIn the past JRE provided a JavaScript engine called Nashorn, but this was discontinued in Java15.\n\n## How to run\nThe majority of the challenges of a set can be run by executing the `com.cryptopals.Setx.main` method of the set or\nby running the JUnit5 tests found under [src/test/java/com/cryptopals/SetXTests](https://github.com/ilchen/cryptopals/tree/master/src/test/java/com/cryptopals).\nRequired dependencies are defined in the project's `pom.xml`.\n\nSome challenges ([31](https://cryptopals.com/sets/4/challenges/31), \n[32](https://cryptopals.com/sets/4/challenges/32), [34](https://cryptopals.com/sets/5/challenges/34),\n[35](https://cryptopals.com/sets/5/challenges/35), [36](https://cryptopals.com/sets/5/challenges/36),\n[37](https://cryptopals.com/sets/5/challenges/37), [49](https://cryptopals.com/sets/7/challenges/49),\n[57](https://toadstyle.org/cryptopals/57.txt), [58](https://toadstyle.org/cryptopals/58.txt),\n[59](https://toadstyle.org/cryptopals/59.txt), [60](https://toadstyle.org/cryptopals/60.txt),\n[66](https://toadstyle.org/cryptopals/66.txt)) require a server-side application.\nThis can be produced with `mvn install` and executed with\n```\njava -jar cryptopals_server-0.2.0.jar\n```\nas a typical SpringBoot 3.x application. This application provides either a RESTful API or an RMI component depending on\na challenge.\n\nFor the more advanced problems I created a proper explanation about the implementation of each of these attacks, which you\ncan find in the Table of Contents below.\n\n## Table of Contents\n* [Set 6: RSA and DSA](https://github.com/ilchen/cryptopals#set-6-rsa-and-dsa)\n  * [Challenge 48. Bleichenbacher's PKCS 1.5 Padding Oracle (Complete Case)](https://github.com/ilchen/cryptopals#challenge-48-bleichenbachers-pkcs-15-padding-oracle-complete-case)\n  \n* [Set 7: Hashes](https://github.com/ilchen/cryptopals#set-7-hashes)\n  * [Challenge 52. Iterated Hash Function Multicollisions](https://github.com/ilchen/cryptopals#challenge-52-iterated-hash-function-multicollisions)\n  * [Challenge 54. Kelsey and Kohno's Nostradamus Attack](https://github.com/ilchen/cryptopals#challenge-54-kelsey-and-kohnos-nostradamus-attack)\n  * [Challenge 55. MD4 Collisions](https://github.com/ilchen/cryptopals#challenge-55-md4-collisions)\n  * [Challenge 56. RC4 Single-Byte Biases](https://github.com/ilchen/cryptopals#challenge-56-rc4-single-byte-biases)\n  \n* [Set 8: Abstract Algebra](https://github.com/ilchen/cryptopals#set-8-abstract-algebra)\n  * [Challenge 57. Diffie-Hellman Revisited: Small Subgroup Confinement](https://github.com/ilchen/cryptopals#challenge-57-diffie-hellman-revisited-small-subgroup-confinement)\n  * [Challenge 58. Pollard's Method for Catching Kangaroos](https://github.com/ilchen/cryptopals#challenge-58-pollards-method-for-catching-kangaroos)\n  * [Challenge 59. Elliptic Curve Diffie-Hellman and Invalid-Curve Attacks](https://github.com/ilchen/cryptopals#challenge-59-elliptic-curve-diffie-hellman-and-invalid-curve-attacks)\n  * [Challenge 60. Single-Coordinate Ladders and Insecure Twists](https://github.com/ilchen/cryptopals#challenge-60-single-coordinate-ladders-and-insecure-twists)\n  * [Challenge 61. Duplicate-Signature Key Selection in ECDSA (and RSA)](https://github.com/ilchen/cryptopals#challenge-61-duplicate-signature-key-selection-in-ecdsa-and-rsa)\n  * [Challenge 62. Key-Recovery Attacks on ECDSA with Biased Nonces](https://github.com/ilchen/cryptopals#challenge-62-key-recovery-attacks-on-ecdsa-with-biased-nonces)\n  * [Challenge 63. Key-Recovery Attacks on GCM with Repeated Nonces](https://github.com/ilchen/cryptopals#challenge-63-key-recovery-attacks-on-gcm-with-repeated-nonces)\n  * [Challenge 64. Key-Recovery Attacks on GCM with a Truncated MAC](https://github.com/ilchen/cryptopals#challenge-64-key-recovery-attacks-on-gcm-with-a-truncated-mac)\n  * [Challenge 65. Truncated-MAC GCM Revisited: Improving the Key-Recovery Attack via Ciphertext Length Extension](https://github.com/ilchen/cryptopals#challenge-65-truncated-mac-gcm-revisited-improving-the-key-recovery-attack-via-ciphertext-length-extension)\n  * [Challenge 66. Exploiting Implementation Errors in Diffie-Hellman](https://github.com/ilchen/cryptopals#challenge-66-exploiting-implementation-errors-in-diffie-hellman)\n\n* [Additional problems](https://github.com/ilchen/cryptopals/blob/master/src/docs/newproblems.md)\n\n## [Set 6: RSA and DSA](https://cryptopals.com/sets/6)\n### Challenge 48. Bleichenbacher's PKCS 1.5 Padding Oracle (Complete Case)\n[Challenge 48](https://cryptopals.com/sets/6/challenges/48) is fairly straightforward to implement by following the steps\nin Section _3.1 Description of the Attack_ of Bleichenbacher's\n[Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1 paper](http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf).\nAn important observation is that thanks to Step 2c the attack runs in O(log(n)), where n is the size of the RSA modulus.\n\nI created [a helper class](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_6/PaddingOracleHelper.java) to aid this process.\nWhen following the paper, one needs to pay particular attention to rounding in all the equalities.\nFor example I ended up waisting a lot of time with Inequality (2) in Step 2c:\n```\n2B + ri·n         3B + ri·n\n--------- \u003c= si \u003c ---------    (2)\n   b                 a\n\n```\nInitially I implemented it by letting `si` iterate from the lower bound until (not including) the upper bound. However\nthat resulted in an incorrect implementation. The term on the right of Inequality (2) will most likely **not be an integer**\nvalue. Therefore, when computed using infinite precision integers, it will be less than its counterpart computed over reals.\nAs a result the correct way to implement Step 2c is to let `si` go to (including) the upper bound when the upper bound\nis rounded down to an integer:\n```java\nwhile (true) {\n    BigInteger   lower = divideAndRoundUp(_2B.add(rn), interval.upper),\n                 upper = _3B.add(rn).divide(interval.lower);\n    for (BigInteger nextS=lower; nextS.compareTo(upper) \u003c= 0; nextS = nextS.add(ONE)) {\n        if (paddingOracle.test(pubKey.encrypt(nextS).multiply(cipherText)))  return  s = nextS;\n    }\n    rn = rn.add(pubKey.getModulus());\n}\n```\n\n#### Practical optimization to tackle real world length RSA moduli\nThe challenge suggests to go all the way up to 768-bits moduli. With my first implementation using Java's BigInteger\nit takes about 30 seconds. Yet, in the real world such small RSA moduli are long a relic of the past. Trying to go\nfor 1024-bits moduli and longer let the implementation spin for longer than I wanted to wait. To address that I switched\nto an optimized implementation of infinite precision integers based on [The GNU Multiple Precision Arithmentic Library (GMP)](https://gmplib.org).\nThanks to [the JNA-GMP wrapper](https://github.com/square/jna-gmp/tree/master/jnagmp) this was very easy to do.\nIf you are on macOS, you probably already installed gmp when you installed Python with [Homebrew](https://brew.sh).\n\nWith tiny changes to the [RSAHelper](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_5/RSAHelper.java#L25-L27)\nand [RSAHelperExt](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_6/RSAHelperExt.java#L66-L67) classes the speedup was\nremarkable. With GMP 6.2.0 I was able to go all the way to 2048-bits moduli within just a couple of minutes:\n\n| RSA modulus size | Average duration of attack (20 tries) |\n| ---------------- |:-------------------------------------:|\n| 256 bits         | 2 s 262 ms                            |\n| 768 bits         | 7 s 207 ms                            |\n| 1024 bits        | 19 s 271 ms                           |\n| 1536 bits        | 39 s 213 ms                           |\n| 2048 bits        | 1 m 54s 607 ms                        |\n\n\nThis difference between the performance of JRE's implementation of BigIntegers and that of GMP is quite remarkable and\ngoes somewhat against Joshua Bloch's advice given in \"Item 66: Use native methods judiciously\" of his excellent\n\"Effective Java, 3\u003csup\u003erd\u003c/sup\u003e edition\" book. [His reply to my tweet](https://twitter.com/joshbloch/status/1125530136927821824?s=20) confirmed that.\n\n```java\n/**\n * @param numBits  number of bits in each prime factor of an RSA modulus, i.e. the modulus is thus {@code 2*numBits} long\n */\n@DisplayName(\"https://cryptopals.com/sets/6/challenges/47 and https://cryptopals.com/sets/6/challenges/48\")\n@ParameterizedTest @ValueSource(ints = { 128, 384, 512, 768, 1024 })\nvoid  challenges47and48(int numBits)  {\n    RSAHelperExt rsa = new RSAHelperExt(BigInteger.valueOf(17), numBits);\n    BigInteger   plainText = RSAHelperExt.pkcs15Pad(CHALLANGE_47_PLAINTEXT.getBytes(),\n                                                    rsa.getPublicKey().getModulus().bitLength());\n    BigInteger   cipherTxt = rsa.encrypt(plainText);\n    BigInteger   crackedPlainText = PaddingOracleHelper.solve(cipherTxt, rsa.getPublicKey(), rsa::paddingOracle);\n    assertArrayEquals(CHALLANGE_47_PLAINTEXT.getBytes(), rsa.pkcs15Unpad(crackedPlainText));\n}\n```\n\n#### Conclusions\nBleichenbacher’s attack clearly demonstrates that RSA-PKCS1 v1.5 encryption is not CCA-secure. A truly CCA-secure public key\nencryption system cannot be broken even given a _full_ decryption oracle (this is by definition of CCA security for\npublic key encryption), while Bleichenbacher’s attack merely uses a _partial_ oracle. Is the fix that was implemented\nin TLS 1.0 sufficient to make RSA-PKCS1 v1.5 CCA secure? Likely, but there's no security proof. This is the main reason\nv2.0 of RSA-PKCS1 adopted Optimal Asymmetric Encryption Padding (OAEP) for RSA encryption, for which there's a security\nproof that the resulting scheme is CCA secure under certain assumptions.\n\n## [Set 7: Hashes](https://cryptopals.com/sets/7)\n### Challenge 49. CBC-MAC Message Forgery\nThe second part of [this challenge](https://cryptopals.com/sets/7/challenges/49), which deals with a message length extension attack for a multiple transactions request:\n\u003e Your mission: capture a valid message from your target user. Use length extension to add a transaction paying the attacker's account 1M spacebucks.\n\nassumes that the attacker and the victim share the same authentication key, which is quite a stretch.\n\n### Challenge 52. Iterated Hash Function Multicollisions\n[Challenge 52](https://cryptopals.com/sets/7/challenges/52) is one of the best demonstrations of the birthday paradox\nI've seen. **NB:** the way this challenge defines the compression function containts\n[a mistake](https://twitter.com/spdevlin/status/1134220310109024257). The correct definition should\nbe\n```\nfunction MD(M, H, C):\n  for M[i] in pad(M):\n    H := C(M[i], H) ^ H\n  return H\n```\n\nFor the purposes of this task it makes sense to choose a cipher whose key size is 8 bytes. It will also be easier\nif the cipher's key and block sizes are the same. I opted for Blowfish, which is present in all JREs through\n`com.sun.crypto.provider.SunJCE provider`. I used a 16 bit hash for the easier hash function f, and a 32 bit hash for g.\nThis way I needed to find 2\u003csup\u003e16\u003c/sup\u003e messages colliding in f to ensure there's a pair among them colliding in g. \n\n### Challenge 54. Kelsey and Kohno's Nostradamus Attack\n[Challenge 54](https://cryptopals.com/sets/7/challenges/54) shows an ingenious way of finding _a target collision_ between a Merkle–Damgård\nhash of two messages m\u003csub\u003e0\u003c/sub\u003e and m\u003csub\u003e1\u003c/sub\u003e, where m\u003csub\u003e0\u003c/sub\u003e is chosen arbitrarily by the attacker while\nm\u003csub\u003e1\u003c/sub\u003e is not and needs to be augmented with a suffix that would make its hash match that of m\u003csub\u003e0\u003c/sub\u003e.\nThe only requirement is that |m\u003csub\u003e0\u003c/sub\u003e| \u003e |m\u003csub\u003e1\u003c/sub\u003e| by a few blocks. The number of blocks\nby which the length of m\u003csub\u003e0\u003c/sub\u003e exceeds the length of m\u003csub\u003e1\u003c/sub\u003e is referred to as `k`. The way this challenge\nis presented is in the form of using hashes to produce _commitments_.\n\nThe attack is explained at length by John Kelsey and Tadayoshi Kohno in\n[their _Herding Hash Functions and the Nostradamus Attack_ paper](https://eprint.iacr.org/2005/281.pdf). The most \ninvolved part of the attack is building the diamond structure. I decided to represent it as a multi dimensional array.\nThe first dimension (i) is the tree level, the second (j) contains 2\u003csup\u003ek-i\u003c/sup\u003e two-element arrays in which the first element\nis the starting hash h[i, j] (i.e. the chaining variable) and the second element is a message block whose hash collides\nwith that of the message starting at either h[i, j+1] (when j is even) or h[i, j-1] (when j is odd). I demonstrate this\nin the following picture:\n![alt text](https://raw.githubusercontent.com/ilchen/cryptopals/master/src/docs/challenge54_diamond_structure.png)\nTo make working with the diamond structure easier I created the [DiamondStrcuture class](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_7/DiamondStructure.java),\nwhich encapsulates it.\n\nLevel 0 of the array is special in that all the hashes stored in h[0, j, 0] are the initial chaining variables\nand can be set at will. I decided to populate the elements h[0, j, 0] in such as way as to ensure that they are sorted.\nThis allows me to make use of a binary search when I need to construct a k-blocks long suffix for m\u003csub\u003e1\u003c/sub\u003e. The\nconstruction of level `i` of the diamond structure calls for finding 2\u003csup\u003ek-i\u003c/sup\u003e message blocks whose hash matches\na given target. I created [MDHelper::findCollisionsWith](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_7/MDHelper.java#L135-L163)\nhelper method to make it easier. To speed up the construction of a given level, I observed that this task\n[lends itself to parallelization](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_7/DiamondStructure.java#L83-L114).\nThis sped up the process a lot.\n\nSome other notes worth mentioning. I use m\u003csub\u003e0\u003c/sub\u003e of 14 blocks long and m\u003csub\u003e1\u003c/sub\u003e of 4 blocks. This gives me\na diamond structure with 10 levels and 2\u003csup\u003e10\u003c/sup\u003e different initial hashes at level 0. I make use of the easy 16-bit hash `f`\nfrom Challenge 52. With this setup I am able to construct the desired Nostradamus message in about 11 minutes on\nmy MacBook Pro (with 8 virtual cores).\n\n```java\n@Test\nvoid challenge54() throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, InvalidKeyException, IllegalBlockSizeException, ExecutionException, InterruptedException {\n    byte[] H = {0, 1}, H2 = {0, 1, 2};\n    MDHelper mdHelper = new MDHelper(H, H2, \"Blowfish\", 8);\n    String originalCommittedToMsg = /* 14 blocks, 2^10 */\n            \"3-5, 0-0, 1-6, 4-2, 2-2, 4-3, 1-1 dummy prediction that will be replaced\"\n                    + \"1234567887654321012345677654321012345678\",\n            nostradamusMsg = \"3-1, 0-1, 2-6, 2-2, 3-1, 1-1,0-3\"; /* 4 blocks */\n\n    byte[] hash = mdHelper.mdEasy(originalCommittedToMsg.getBytes()),\n            trgtHash = mdHelper.mdInnerLast(originalCommittedToMsg.getBytes(), H,\n                    0, originalCommittedToMsg.length() / 8), sfx;\n    DiamondStructure ds = new DiamondStructure(\n            originalCommittedToMsg.length() - nostradamusMsg.length() \u003e\u003e 3,\n            trgtHash, \"Blowfish\", 8);\n\n    sfx = ds.constructSuffix(mdHelper.mdInnerLast(nostradamusMsg.getBytes(), H, 0, 4));\n    if (sfx != null) {\n        assertEquals(originalCommittedToMsg.length(), nostradamusMsg.length() + sfx.length);\n        byte longMsg[] = Arrays.copyOf(nostradamusMsg.getBytes(), nostradamusMsg.length() + sfx.length);\n        System.arraycopy(sfx, 0, longMsg, nostradamusMsg.length(), sfx.length);\n        assertArrayEquals(hash, mdHelper.mdEasy(longMsg));\n    } else {\n        fail(\"Too few leaves in the diamond structure :-(\");\n    }\n}\n```\n\n#### Conclusions\nIt is not safe to produce a commitment just by hashing a secret message `m` with a collision-resistant hash function. There's no\nsecurity proof that such a construction is safe. The correct way to produce a commitment for a secret message `m`\nis to generate a uniformly distributed random number `r` of, say 512 bits if SHA256 is used as a collision-resistant hash function.\nThen compute `h = SHA256(r || m)`. The commitment is a pair `(r, h)`, of which `h` is revealed while `r` is kept secret until\nit comes time to prove knowledge of `m`. However the attack presented in this challenge will still work with this correct setup\nsince the person making the prediction is in control of `r`.\n\nThis attack shows that producing a commitment by hashing a secret message `m` with a hash function that is built using\nthe Merkle–Damgård construction may not guarantee the _binding_ property of the commitment, which a cryptographically secure\ncommitment scheme must possess (in addition to that of _hiding_ `m`). If a hash function is _target collision resistant_,\nusing it to produce commitments would be safe.\n\nWhy does this attack work? The main reason is that hash functions employing\nthe Merkle–Damgård construction are vulnerable to message-length extension attacks. That's the main reason the recently \nstandardized by NIST [SHA3 hash standard](https://csrc.nist.gov/publications/detail/fips/202/final) uses the sponge\nconstruction instead of Merkle–Damgård. Using SHA3 for making commitments is immune from this attack. So would be using\nHMAC\u003csub\u003e0\u003c/sub\u003e with SHA256 as the underlying hash:\nHMAC\u003csub\u003e0\u003c/sub\u003e(m) := HMAC(0\u003csup\u003el\u003c/sup\u003e, m) = H(opad || H(ipad || m))\n\nHow feasible would mounting this attack be against SHA256? In their original paper the authors indicate that it reduces\nthe effort required for finding a collision with the target hash from O(2\u003csup\u003e256\u003c/sup\u003e) to O(2\u003csup\u003e172\u003c/sup\u003e) when\n`k=84`, i.e. |m\u003csub\u003e0\u003c/sub\u003e| is greater than |m\u003csub\u003e1\u003c/sub\u003e| by 84 512-bit blocks (or by 5.25 KiB). The space complexity\nof such a diamond structure would be huge. Level 0 alone would take up 2\u003csup\u003e84\u003c/sup\u003e * (64 + 32) bytes, which is\n1536 YiB yobibyte (1 yobibyte == 2\u003csup\u003e80\u003c/sup\u003e bytes) \u0026mdash; a mind-boggling number. This makes this attack infeasible\nagainst SHA-256 in my opinion. Using shorter hashes from the MD family for making commitments is indeed risky.\n\n\n### Challenge 55. MD4 Collisions\n[Challenge 55](https://cryptopals.com/sets/7/challenges/55) is probably one of the most interesting to work on in the first\n7 sets. I succeeded in implementing it in a uniform Object-Oriented way, which aids readability and maintainability.\nThe implementation is also blazingly fast \u0026mdash; it finds a collison within a few seconds. Here is one found with it:\n```$xslt\nCollision found between\n\t683E10B651E9185B4D9886D90B7634AE7C4D753533F75041C388E6ACF20CF8B12BA9C27368F09B22EDCE3445BBFED7E8636EDB70070DF0EB7449FA54E421D246\n\t683E10B651E918DB4D9886490B7634AE7C4D753533F75041C388E6ACF20CF8B12BA9C27368F09B22EDCE3445BBFED7E8636EDA70070DF0EB7449FA54E421D246\nMD4: B9B0031B30D53E826B80CBDDBE7354D9\n```\nI succeeded in fully enforcing all constraints from the first round of MD4 as well as all constraints from the first two steps of the second round.\nI didn't figure out how to apply the constraints from the 3\u003csup\u003erd\u003c/sup\u003e step of the second round of MD4. X. Wang et al. give some\nhints in their paper, yet they are not easy to follow\n\u003e Utilize more precise modification to correct some other conditions. For example, we can use the internal collision in Table 2 in which there are three message words are changed to correct c5,i, i = 26, 27, 29, 32. The precise modification should add some extra conditions in the first rounds (see Table 2) in advance. There are many other precise modifications.\nc5,30 can be corrected by other modification. By various modifications, besides two conditions in the third round, almost all the conditions in rounds 1-2 will be corrected. The probability can be among 2^6 ∼ 2^2.\n\nIt is interesting to note that X. Wang et al. used differential cryptanalysis to discover the conditions that lead\nto collisions in MD4. MD4 was developed in 1990 by Ron Rivest, which is also the year in which Eli Biham and Adi Shamir introduced\ndifferential cryptanalysis. Obviously the designer of MD4 didn't take it into account while designing MD4. Interestingly, the NSA\ndiscovered differential cryptanalysis as early as in the 1970s, which is one of the reasons why DES is immune to it\n(see [this paper](https://ieeexplore.ieee.org/abstract/document/5389567) or Section 12.4 in Bruce Schneier's Applied Cryptography\n2\u003csup\u003end\u003c/sup\u003e edition for details).\n\n### Challenge 56. RC4 Single-Byte Biases\n[Challenge 56](https://cryptopals.com/sets/7/challenges/56) is an excellent demonstration of how even a tiny bias that\nmakes the distribution of a secure PRF slightly different from uniform might be enough to break it. In the case of RC4\nbytes 2 to 255 of RC4 keystream have biases on the order of 1/2\u003csup\u003e16\u003c/sup\u003e or higher.\n\nThis challenge is based on the attack outlined in Section 4.1 of [this paper](http://www.isg.rhul.ac.uk/tls/RC4biases.pdf).\nIn my solution I used the biases in the 16\u003csup\u003eth\u003c/sup\u003e (Z\u003csub\u003e16\u003c/sub\u003e) and 32\u003csup\u003end\u003c/sup\u003e (Z\u003csub\u003e32\u003c/sub\u003e) bytes of RC4's keystream,\nwhich are elucidated in Section 3.1 of the paper.\n\nThe essence of this attack is fairly simple \u0026mdash; the biases in the distributions of Z\u003csub\u003e16\u003c/sub\u003e and Z\u003csub\u003e32\u003c/sub\u003e make\nthe frequency of a few values much higher than 1/256 (`0x00`, `0xF0`, `0x10` for Z\u003csub\u003e16\u003c/sub\u003e;\nand `0x00`, `0xE0`, `0x20` for Z\u003csub\u003e32\u003c/sub\u003e). If we ensure that we encrypt the same plaintext bytes in these positions\nrepeatedly, certain ciphertext values for C\u003csub\u003e16\u003c/sub\u003e and C\u003csub\u003e32\u003c/sub\u003e will also occur more frequently than others.\nBy encrypting on the order of 2\u003csup\u003e24\u003c/sup\u003e values, we construct the distribution of C\u003csub\u003e16\u003c/sub\u003e and C\u003csub\u003e32\u003c/sub\u003e,\nwhich (like the distribution of Z\u003csub\u003e16\u003c/sub\u003e and Z\u003csub\u003e32\u003c/sub\u003e) will not be uniform. This is enough to recover the\noriginal plaintext bytes P\u003csub\u003e16\u003c/sub\u003e and P\u003csub\u003e32\u003c/sub\u003e using the maximum-likelihood estimation.\n\nSince the biases in Z\u003csub\u003e16\u003c/sub\u003e and Z\u003csub\u003e32\u003c/sub\u003e, while non-negligible, are still fairly small, I used 2\u003csup\u003e27\u003c/sup\u003e\nRC4 keystreams (with independent 128-bit keys) to construct their frequency distributions. With smaller values such as \n2\u003csup\u003e25\u003c/sup\u003e or less, the recovered plaintext cookie will contain errors, particularly for P\u003csub\u003e32\u003c/sub\u003e. BTW: In the paper Nadhem J. AlFardan et al.\nused 2\u003csup\u003e44\u003c/sup\u003e RC4 keystreams to determine the disributions of Z\u003csub\u003e16\u003c/sub\u003e and Z\u003csub\u003e32\u003c/sub\u003e. For this challenge\nthis would be an overkill.\n\nFor the maximum-likelihood estimation of the plaintext bytes I used 2\u003csup\u003e24\u003c/sup\u003e ciphertexts. This is enough to recover\nP\u003csub\u003e16\u003c/sub\u003e and P\u003csub\u003e32\u003c/sub\u003e and fully corraborates the results in Figure 4 in the paper.\n\n## [Set 8: Abstract Algebra](https://toadstyle.org/cryptopals/)\n\nThis set of problems is amazingly interesting, however it took me approximately twice as long as the previous sets combined.\nIt also calls for creating some handy software that might be of use beyond solving these challenges: such as code for elliptic curve\ncryptography, various ways for calculating dlog, code for GCM along with its GHASH one-time-hash, code for finding roots of polynomials\nover different fields, matrix operations over GF(2), matrix operations over R, etc.\n\n### Challenge 57. Diffie-Hellman Revisited: Small Subgroup Confinement\n[Challenge 57](https://toadstyle.org/cryptopals/57.txt) introduces the Pohlig-Hellman\nalgorithm. The best general-purpose algorithm for taking discrete logs in Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e is\n[the General Number Field Sieve (GNFS)](https://en.wikipedia.org/wiki/General_number_field_sieve). The running time\nof the GNFS is O(\u0026#x221B;p), where `p` is the group's prime.\n\nThe Pohlig-Hellman algorithm lets take discrete logs potentially faster than that for groups where `n = p-1` (the order of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e)\nhas many small factors. Moreover it works for any cyclic group \u0026mdash; the fact that will come in handy in [Challenge 59](https://github.com/ilchen/cryptopals#challenge-59-elliptic-curve-diffie-hellman-and-invalid-curve-attacks).\nIf n = p\u003csub\u003e1\u003c/sub\u003e\u003csup\u003ee1\u003c/sup\u003e · p\u003csub\u003e2\u003c/sub\u003e\u003csup\u003ee2\u003c/sup\u003e · ... · p\u003csub\u003er\u003c/sub\u003e\u003csup\u003eer\u003c/sup\u003e, then\nthe computational complexity of taking dlog with Pohlig-Hellman is O{\u0026Sigma;[e\u003csub\u003ei\u003c/sub\u003e · (lg`n` + \u0026Sqrt;p\u003csub\u003ei\u003c/sub\u003e)]}.\nWell, the way @spdevlin proposes to go about the solution, which is the path I took:\n\u003e Friendly tip: maybe avoid any repeated factors. They only complicate things.\n\nthe complexity will be O[\u0026Sigma;(lg`n` + \u0026Sqrt;p\u003csub\u003ei\u003c/sub\u003e)], where n \u003c= p\u003csub\u003e1\u003c/sub\u003e · p\u003csub\u003e2\u003c/sub\u003e · ... · p\u003csub\u003er\u003c/sub\u003e.\n                                                                     \nProbably the most involved part of Pohlig-Hellman is a need to\n[implement Garner's algorithm](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/Set8.java#L131-L167) to\nreconstruct Bob's private key from its residues per subset of the moduli of p-1.\n\nAll in all the challenge presents an attack that can bypass DH implementations where Bob makes some rudimentary checks\non the offered subgroup description (p, q, g):\n* Are both p and q prime?\n* Does q divide p-1?\n* Is g different from 1?\n* Is g\u003csup\u003eq\u003c/sup\u003e equal 1?\n\nThe challenge does make two big assumptions though, namely that\n* Bob will naively hang on to the same private key across all new sessions with Alice.\n* That group Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e contains a large number of subgroups with small order. The attack will for example\nnot work if p is [a safe prime](https://en.wikipedia.org/wiki/Safe_prime).\n\n### Challenge 58. Pollard's Method for Catching Kangaroos\n[Challenge 58](https://toadstyle.org/cryptopals/58.txt) makes the attack from the previous challenge yet more realistic.\nIt can be mounted against a group where `p-1` has at least one large factor in addition to `q` (the order of a generator used).\n\nThe attack makes use of J.M. Pollard's Lambda Method for Catching Kangaroos, as outlined in\n[Section 3 of Pollard's paper](https://www.ams.org/journals/mcom/1978-32-143/S0025-5718-1978-0491431-9/S0025-5718-1978-0491431-9.pdf).\nWhile not as efficient as the GNFS, Pollard's kangaroo algorithm can be applied to any cyclic group (e.g. not only to Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e\nbut also to elliptic curve groups) \u0026mdash; a fact that will be of use in [a later elliptic curve challenge](https://github.com/ilchen/cryptopals#challenge-60-single-coordinate-ladders-and-insecure-twists).\nIts running time is O(\u0026Sqrt;`q`), where `q` is the order of the generator used.\n\nPollard's method employs a pseudo-random mapping function f that maps from set {1, 2, ..., p-1} to set {0, 1, ... k-1}.\nThe challenge suggested the following simplistic definition for f (which is similar to what Pollard gives in one of his examples):\n```\nf(y) = 2^(y mod k)\n```\nI used ceil(log\u003csub\u003e2\u003c/sub\u003e\u0026radic;b + log\u003csub\u003e2\u003c/sub\u003elog\u003csub\u003e2\u003c/sub\u003e\u0026radic;b - 2) for calculating `k`, which is based on\nthe suggestion in Section 3.1 of [this paper by Ravi Montenegro and Prasad Tetali](https://arxiv.org/pdf/0812.0789.pdf). \n\nWhen deciding on the amount of jumps N that the tame kangaroo is to make, I used the suggestion from the challenge\ndescription and set N to the mean of range of f multiplied by 4. With this choice of the constant the probability of\nPollard's method finding the dlog is 98%.\n\nI generate group Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e as follows:\n* `p` is a 1024-bit prime meeting the following  requirement: `p = Nq + 1`, where `q` is a 42-bit prime. This\nis based on the advice from Section 11.6 of \"Cryptography Engineering, 2\u003csup\u003end\u003c/sup\u003e edition\" by Niels Ferguson,\nBruce Schneier, and Tadayoshi Kohno.\n* The generator `g` is a random member of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e that has an order of `q`.\n\nThe only deviation from the book is that I use fewer than 256 bits for `q`, which obviously weakens the group. Unfortunately\nPollard's kangaroo algorithm doesn't lend itself to parallelisation so choosing `q` to be much larger than 42 bits makes the\nattack impractical. E.g. with a 42-bit q the attack takes on the order of 20 minutes on my MacBook Pro.\n\nTo make the attack more realistic I establish only one session to Bob to find `b mod r`, where `r` is one factor of `N`.\nThis no longer assumes that Bob uses the same private key across all new sessions with Alice. The attack thus works\nin a realistic setting where Bob generates a new private key for each new session.\n\n**NB:** The attack will still be infeasible if `p` is chosen to be a safe prime. However such choices of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e\nare rare as they lead to more computationally intensive exponentiation in the group.\n\n\n### Challenge 59. Elliptic Curve Diffie-Hellman and Invalid-Curve Attacks\n[Challenge 59](https://toadstyle.org/cryptopals/59.txt) is based on the Weierstrass form of representing\nelliptic curves: y\u003csup\u003e2\u003c/sup\u003e = x\u003csup\u003e3\u003c/sup\u003e + ax + b\n\nWhen implementing the group operation in E(F\u003csub\u003ep\u003c/sub\u003e), division should be carried out as multiplication by\nthe multiplicative inverse mod p, e.g.:\n```\nfunction combine(P1, P2):\n    if P1 = O:\n        return P2\n\n    if P2 = O:\n        return P1\n\n    if P1 = invert(P2):\n        return O\n\n    x1, y1 := P1\n    x2, y2 := P2\n\n    if P1 = P2:\n        m := ( (3*x1^2 + a) * modInv(2*y1, p) ) mod p\n    else:\n        m := ( (y2 - y1) * modInv(x2 - x1, p) ) mod p\n\n    x3 := ( m^2 - x1 - x2 ) mod p\n    y3 := ( m*(x1 - x3) - y1 ) mod p\n\n    return (x3, y3)\n```\n\nFor convenience's sake I implemented the class that represents elements of the curve so that each coordinate \nof a point (x, y) is positive, i.e. `x` and `y` are stored `mod p`. This makes the implementation simpler.\n\nFor the rest the attack is pretty similar to [Challenge 57](https://toadstyle.org/cryptopals/57.txt) except\nthat the group given in the challenge\n```\nECGroup(modulus=233970423115425145524320034830162017933, a=-95051, b=11279326, order=233970423115425145498902418297807005944)\n```\ndoesn't have an order with many small factors. Therefore instead of finding generators of the small subgroups of this \nelliptic curve group, the attack hinges on Alice foisting on Bob bogus public keys that are not on the original\nelliptic curve but are rather on specially crafted curves\n```\nECGroup(modulus=233970423115425145524320034830162017933, a=-95051, b=210, order=233970423115425145550826547352470124412)\nECGroup(modulus=233970423115425145524320034830162017933, a=-95051, b=504, order=233970423115425145544350131142039591210)\nECGroup(modulus=233970423115425145524320034830162017933, a=-95051, b=727, order=233970423115425145545378039958152057148)\n```\n\nThe orders of these elliptic curves do have many small factors. Interestingly all the three crafted curves are required\nto recover Bob's private key. This is because the product of the small factors of each of these curves is less than\nthe order of the generator given for the challenge `(182, 85518893674295321206118380980485522083)`. You need the distinct\nsmall factors collected from all the crafted curves.\n\n**NB** the algorithm suggested in Challenge 57 and this one for finding subgroups of required order\n\u003e Suppose the\n  group has order q. Pick some random point and multiply by q/r. If you\n  land on the identity, start over.\n\nonly works for _cyclic_ groups. For Challenge 57 it didn't matter much because Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e*\u003c/sup\u003e is always\ncyclic. This doesn't always hold for elliptic curve groups though, i.e. not every elliptic curve group is cyclic. In fact you\nwill not be able to find a generator of order 2 for `y^2 = x^3 - 95051*x + 210` if you use the order of the group\n233970423115425145550826547352470124412. The correct way to find generators of required order is to use the order\nof the largest cyclic subgroup of an elliptic curve. For this curve it is 116985211557712572775413273676235062206.\nSee [my discussion with @spdevlin](https://twitter.com/_ilchen_/status/1174045790748254210?s=20). Worth noting here\nthat any group of a prime order is cyclic. That's one of the reasons why some popular elliptic curves such as \nsecp256r1 or secp256k1 have prime orders. The converse is not always true, i.e. there can be groups of non-prime order\nthat are cyclic. \n\nThe attack in this challenge does make two assumptions though, namely that\n* Bob will hang on to the same private key across all new sessions with Alice. This is the same as in Challenge 57.\n* Bob will not check whether Alice's public key lies on the expected elliptic curve. How big of an assumption\n is that? Unfortunately not too big because in many implementations of ECDH Bob is only sent the x coordinate of\n Alice's public key for the sake of efficiency, and the implementation doesn't check if x\u003csup\u003e3\u003c/sup\u003e + ax + b is\n a quadratic residue. In fact such an attack can be pulled off on the ubiquitous NIST P256 curve. It takes\n a twist-secure elliptic curve such as 25519 to foil this attack. Or one can just check if Alice's public key\n is on the expected curve, e.g. the following check by Bob will render this attack harmless:\n```java\npublic Set8.Challenge59ECDHBobResponse initiate(ECGroup.ECGroupElement g, BigInteger q, ECGroup.ECGroupElement A) throws RemoteException {\n\n    // A bit contrived for Bob to hang on to the same private key across new sessions, however this is what\n    // Challenge 59 calls for.\n    if (ecg == null  ||  !ecg.equals(g.group())  ||  !this.g.equals(g)) {\n        ecg = g.group();\n        this.g = g;\n        privateKey = new DiffieHellmanHelper(ecg.getModulus(), q).generateExp().mod(q);\n    }\n    // Is Alice's public key on the curve?\n    if (!ecg.containsPoint(A)) {\n        throw  new RemoteException(\"Public key presented not on the expected curve\");\n    }\n```\n\n### Challenge 60. Single-Coordinate Ladders and Insecure Twists\n[Challenge 60](https://toadstyle.org/cryptopals/60.txt) is based on the Montgomery form of representing\nelliptic curves: Bv\u003csup\u003e2\u003c/sup\u003e = u\u003csup\u003e3\u003c/sup\u003e + Au\u003csup\u003e2\u003c/sup\u003e + u\n\nA Montgomery form curve equation can always be changed into the Weierstrass form, the converse is not always true.\nGiven isomorphism between EC groups of the same order regardless of their form, I abstracted the concept of\nan EC point into an interface and refactored the rest of the classes accordingly. This ensured [a shared implementation\nof the `scale` and `dlog` methods](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/ECGroupElement.java#L15-L91):\n```java\npublic interface ECGroupElement {\n    BigInteger  getX();\n    BigInteger  getY();\n    ECGroupElement  getIdentity();\n    ECGroupElement  inverse();\n    ECGroupElement  combine(ECGroupElement that);\n    ECGroup  group();\n    \n    /** Returns the x coordinate of kP where P is this point */\n        BigInteger  ladder(BigInteger k);\n\n    default ECGroupElement  scale(BigInteger k) {\n        ECGroupElement res = getIdentity(),  x = this;\n        while (k.compareTo(BigInteger.ZERO) \u003e 0) {\n            if (Set5.isOdd(k))  res = res.combine(x);\n            x = x.combine(x);\n            k = k.shiftRight(1);\n        }\n        return  res;\n    }\n}\n```\nAnalogously for [the concept of an EC group](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/ECGroup.java#L14-L84):\n```java\npublic interface ECGroup {\n\n    /** Returns the order of field F\u003csub\u003ep\u003c/sub\u003e */\n    BigInteger  getModulus();\n\n    /** Returns the order of this curve, i.e. the number of points on it. */\n    BigInteger  getOrder();\n\n    /** If this group is cyclic, returns its order. Otherwise returns the order of the largest cyclic subgroup. */\n    BigInteger  getCyclicOrder();\n\n    /** Returns the identity element of this group */\n    ECGroupElement  getIdentity();\n\n    /**\n     * Returns the order of the quadratic twist of this curve\n     */\n    default BigInteger  getTwistOrder() {\n        return  getModulus().multiply(TWO).add(TWO).subtract(getOrder());\n    }\n\n    /**\n     * Calculates the y coordinate of a point on this curve using its x coordinate\n     */\n    BigInteger  mapToY(BigInteger x);\n\n    /** Checks if the point {@code elem} is on this curve */\n    boolean  containsPoint(ECGroupElement elem);\n\n    /** Creates a point on this curve with designated coordinates */\n    ECGroupElement createPoint(BigInteger x, BigInteger y);\n\n    BigInteger  ladder(BigInteger x, BigInteger k);\n}\n```\n**NB** For a Montgomery curve the point at infinity O is always (0, 1). Each Montgomery curve has at least one point of order 2,\nit is always (0, 0).\n\nThis challenge turned out to be one of the toughest so far. Here Alice sends Bob only the x-coordinate of her public key.\nBob then derives the DH symmetric key using the Montgomery ladder: `group.ladder(xA, b)`, where xA is the\nx-coordinate of Alice's public key and b is Bob's private key. Bob also sends back to Alice only the x-coordinate of\nhis public key: `g.ladder(privateKey)`, where g is the generator of the EC group.\n\nWhat makes this challenge much more computationally intensive is that when the protocol uses only the x-coordinates\nof Alice's public key, Alice never learns the exact residues of Bob's private key when she foists public keys that\nare in fact generators of small subgroups. @spdevlin, the author of the challenge, gives a small hint:\n\u003e HINT: You may come to notice that k*u = -k*u, resulting in a\n  combinatorial explosion of potential CRT outputs. Try sending extra\n  queries to narrow the range of possibilities.\n  \nBy way of illustration. In this challenge we work with a Montgomery curve\n```\nMontgomeryECGroup(modulus=233970423115425145524320034830162017933, A=534, B=1, order=233970423115425145498902418297807005944, cyclicOrder=233970423115425145498902418297807005944)\n```\nwhich is isomorphic to \n```\nWeierstrassECGroup(modulus=233970423115425145524320034830162017933, a=-95051, b=11279326, order=233970423115425145498902418297807005944, cyclicOrder=233970423115425145498902418297807005944)\n```\nfrom the previous challenge.\n\nThe twist of our Montgomery curve has order\n2·modulus + 2 - order-of-curve = 233970423115425145549737651362517029924. The first gotcha is that the twist is\nnot a _cyclic_ group and just taking small factors of its order will not do (it does have subgroups that are cyclic though).\n@spdevlin says:\n\u003e Calculate the order of the twist and find its small factors. This one should have a bunch under 2^24.\n\nWell, the small factors are [2, 11, 107, 197, 1621, 105143, 405373, 2323367]. However you will not be able to find\na generator of order 2 if you use assume the twist has a cyclic order of 233970423115425145549737651362517029924.\nYou will be able to find generators for the other small factors: [11, 107, 197, 1621, 105143, 405373, 2323367].\nFor a randomly generated Bob's private key, sending the generators of these subgroups disguised as Alice's public keys\nreveals the following facts about Bob's private key b:\n```\nGenerator of order 11 found: 76600469441198017145391791613091732004\nFound b mod 11: 4 or 11-4=7\nGenerator of order 107 found: 215154098129284057249603159073175023533\nFound b mod 107: 24 or 107-24=83\nGenerator of order 197 found: 94955123407611383099634454718224635806\nFound b mod 197: 44 or 197-44=153\nGenerator of order 1621 found: 90340124320150600231802526508276130439\nFound b mod 1621: 390 or 1621-390=1231\nGenerator of order 105143 found: 226695433509445480278297098756629724558\nFound b mod 105143: 6979 or 105143-6979=98164\n...\n```\nYou thus have 2\u003csup\u003e7\u003c/sup\u003e=128 combinations of Bob's private key modulo the product of the\n[11, 107, 197, 1621, 105143, 405373, 2323367] moduli. And then you'll need to take a DLog for each of these combinations\nto end up with 128 guesses of Bob's private key. This will probably take a few days to compute on a typical laptop. \nCan we do better? Yes, it is possible to whittle down the number of combinations to just two with one additional \ncall to Bob. What you need to do is find a generator on the twist curve of order which is the composite of these\nsmall moduli. Staying with the above example, it would mean finding a generator of order \n\nr = 11 · 107 · 197 · 1621 · 105143 · 405373 · 2323367 = 37220200115549684379403037\n\nand then initiating a DH exchange with Bob giving him this generator as Alice's public key. Poor Bob will then\ncalculate a symmetric key (i.e. the mac key in the context of this challenge) by raising this generator to his\nprivate key exponent and send his Mac response. \n\nWe will then try to calculate 2\u003csup\u003e7\u003c/sup\u003e different symmetric keys ourselves each based on one of the 2\u003csup\u003e7\u003c/sup\u003e combinations \nof Bob's private key modulo `r` = 37220200115549684379403037. Those combinations that\nresult in the identical Mac to that returned by Bob are the ones that are worth taking a DLog on to recover Bob's full private key. There'll\nbe only two unique candidates of Bob's private key modulo `r` = 37220200115549684379403037 to try:\nk and 37220200115549684379403037 - k. Initially I had a less elegant way of going about this wrinkle. The current\nimplementation is thanks to the idea shared with me by [Gregory Morse](https://github.com/GregoryMorse).\n\nI ended up creating [a class dedicated to generating different possible values of Bob's private key](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/CRTCombinations.java)\nmodulo `r` (i.e. modulo 11 · 107 · 197 · 1621 · 105143 · 405373 · 2323367 = 37220200115549684379403037).\nThe class implements [Iterable\u003cBigInteger\u003e](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) and thus allows iterating\nthrough all possible combinations of the private key modulo `r`. Each candidate is constructed using \nGarner's formula.\n\nThere are more intricacies to tackle along the way. Some small, others bigger:\n\n1. A fairly small complication is that finding `b mod small-prime` requires\nploughing through large ranges for the bigger subgroups. For example to find b mod 2323367 requires wading through\nthe [0, 2323367/2] range, and for each element of the range you need to calculate a DH key and derive a MAC.\nWithout parallelizing this easily take a few minutes. I implemented [logic to carry such scans in parallel](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/Set8.java#L309-L355)\nto save time.\n\n2. Once you know Bob's private key `b` modulo the product of small primes `r` = 37220200115549684379403037 (b mod r = n),\ntaking a DLog in E(GF(p)) to recover the full private ket will take a few hours of time. The larger `r`, the less effort DLog will take.\nAre there any other small factors to use? I searched up to 2\u003csup\u003e32\u003c/sup\u003e\nand didn't find any. However there's a small improvement possible. Remember that the order of the twist has a divisor of 2\nbut that you cannot find a subgroup of order 2 if you assume the twist is a cyclic group of order 233970423115425145549737651362517029924?\nThe smallest subgroup you'll find has order 11. However you can find a subgroup of order 22. So instead of\nfinding residues of Bob's private key modulo these primes [11, 107, 197, 1621, 105143, 405373, 2323367] I switched to\nsearching for residues of moduli [22, 107, 197, 1621, 105143, 405373, 2323367] instead. Garner's algorithm still\nworks fine as its only requirement is that moduli be pairwise co-prime. This let me learn Bob's key modulo \n`r` = 74440400231099368758806074 instead of modulo 37220200115549684379403037, roughly halving the time needed to take DLog later on.\n\n   A still cleaner way to address this would be to spend more time analyzing the twist and figuring out the order of its largest\n   cyclic group, which is obviously less than the order of the twist 233970423115425145549737651362517029924. And then\n   to search for generators of small subgroups relative to this cyclic subgroup. This challenge reveals an interesting \n   fact \u0026mdash; **the twist of a cyclic Elliptic Curve group need not be cyclic**.\n\n3. Applying the the kangaroo attack from Challenge 58 correctly also warrants a couple of explanations. If Bob's private\nkey is the same number of bits as the legit generator of the curve, you might easily trip up. In this problem the generator is \n   ```\n   MontgomeryECGroup.ECGroupElement(u=4, v=85518893674295321206118380980485522083, order=29246302889428143187362802287225875743)\n   ```\n   I implemented Bob's part so that it [ensures that its private key has the same number of bits as the generator](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/ECDiffieHellmanBobService.java#L24-L33).\n   By now we know Bob's private key `b` mod `r` is equal `n`. That means that b = n + m·r and the only thing we miss\n   to reconstruct Bob's pk `b` is finding `m`. Applying the maths of the kangaroo attack from Challenge 58:\n   ```\n   y = g^b = g^(n + m·r)\n   y = g^n · g^(m·r)\n   y' = y · g^-n = g^(m·r)\n   g' = g^r\n   y' = (g')^m\n   ```\n   shows that we have everything needed to calculate `m` including  `y`, which is Bob's public key (typically designated as B).\n   How do we find `y`? It is returned by Bob in every DH response it sends back including the last one we received when\n   we searched for the generator of the subgroup of order 74440400231099368758806074. Here's a relevant piece of server-side\n   code representing Bob, with an appropriate comment added.\n   ```java\n    public Set8.Challenge60ECDHBobResponse initiate(ECGroupElement g, BigInteger q, BigInteger xA) {\n        init(g, q);\n\n        macKey = Set8.generateSymmetricKey(g.group(), xA, privateKey, 32, Set8.MAC_ALGORITHM_NAME);\n        mac.init(macKey);\n        return  new Set8.Challenge60ECDHBobResponse(g.ladder(privateKey), // this is Bob's public key\n                                                    Set8.CHALLENGE56_MSG,\n                                                    mac.doFinal(Set8.CHALLENGE56_MSG.getBytes()) );\n    }\n   ```\n   \n   Now we can do the rest:\n   ```java\n    ECGroupElement   gPrime = base.scale(r),\n                     y = base.group().createPoint(resp.xB, base.group().mapToY(resp.xB));\n    List\u003cBigInteger\u003e   ret = new ArrayList\u003c\u003e();\n\n    for (BigInteger n : cands) {\n        System.out.printf(\"Trying b mod %d = %d as Bob's private key%n\", r, n);\n        ECGroupElement   yPrime = y.combine(base.scale(order.subtract(n)));\n        BigInteger   m = gPrime.dlog(yPrime, order.subtract(ONE).divide(r), ECGroupElement::f);\n        n = n.add(m.multiply(r));\n        ret.add(n);\n        System.out.println(\"Possible private key: \" + n);\n    }\n   ```\n\nTime for the final run of the test:\n```java\n@ParameterizedTest @ValueSource(strings = { \"rmi://localhost/ECDiffieHellmanBobService\" })\nvoid challenge60(String bobUrl) throws RemoteException, ... {\n    MontgomeryECGroup   mgroup = new MontgomeryECGroup(new BigInteger(\"233970423115425145524320034830162017933\"),\n            valueOf(534), ONE, new BigInteger(\"233970423115425145498902418297807005944\"));\n    MontgomeryECGroup.ECGroupElement   mbase = mgroup.createPoint( // The base point, aka the generator\n            valueOf(4), new BigInteger(\"85518893674295321206118380980485522083\"));\n    BigInteger   q = new BigInteger(\"29246302889428143187362802287225875743\"); // Order of the base point.\n\n    ECDiffieHellman   ecBob = (ECDiffieHellman) Naming.lookup(bobUrl);\n    boolean   recovered = false;\n    for (BigInteger b : breakChallenge60(mbase, q, bobUrl)) {\n        boolean  isValid = ecBob.isValidPrivateKey(b);\n        System.out.printf(\"Recovered Bob's secret key: %d? %b%n\", b, isValid);\n        recovered |= isValid;\n    }\n    assertTrue(recovered, \"Didn't succeed in recovering Bob's secret key :-(\");\n}\n```\n\nRecall that we have two candidates of Bob's private key modulo 74440400231099368758806074:\n```\nTrying b mod 74440400231099368758806074 = 23977054913240415887527048 as Bob's private key\nk=26, N=11184810\nxt=28871456718421, upperBound=29264338846924\nyt=MontgomeryECGroup.ECGroupElement(u=66018503796393609535400154879727009901, v=75220517950417414937371017845721779515)\nPossible private key: 28218217810951813013557371685215994592\n\nTrying b mod 74440400231099368758806074 = 50463345317858952871279026 as Bob's private key\nk=26, N=11184810\nxt=28871456718421, upperBound=29264338846924\nyt=MontgomeryECGroup.ECGroupElement(u=66018503796393609535400154879727009901, v=75220517950417414937371017845721779515)\nPossible private key: 50463345317858952871279026\n\nRecovered Bob's secret key: 28218217810951813013557371685215994592? true\nRecovered Bob's secret key: 50463345317858952871279026? false\n```\n\nThis challenge is an excellent demonstration of the extra safety that one obtains by using only the x-coordinates\nof Alice's and Bob's public keys when implementing DH on an elliptic curve group. If Alice and Bob go a step further\nand also ensure that they use a twist secure elliptic curve group E(GF(p)) such as\n[the curve 25519](https://en.wikipedia.org/wiki/Curve25519), their implementation will be almost bullet-proof. E.g.\na twist secure elliptic curve group is one whose quadratic twist Ē(GF(p)) has a prime order or an order without any\nsmall subgroups. The challenge also highlights the importance of choosing large private keys, ideally the same number\nof bits as the order of the generator.\n\n\n### Challenge 61. Duplicate-Signature Key Selection in ECDSA (and RSA)\nThe first part of [Challenge 61](https://toadstyle.org/cryptopals/61.txt) that concerns itself with Duplicate Signature\nKey Selection (DSKS) for ECDSA is almost trivial compared to anything else in Sets 7 and 8.\n[The implementation is quite compact](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/ECDSA.java#L15-L63)\nand simpler than DSA atop of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e since there's only one cyclic group of points on E(F\u003csub\u003ep\u003c/sub\u003e) to deal with\nrather than two groups Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e and Z\u003csub\u003eq\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e as is the case in the classical DSA.\n[The effort to produce a DSKS for ECDSA is negligible](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/Set8.java#L460-L475),\neven for an industry standard curve such as [the curve 25519](https://en.wikipedia.org/wiki/Curve25519):\n```java\n@Test\nvoid challenge61ECDSA() {\n    MontgomeryECGroup   curve25519 = new MontgomeryECGroup(CURVE_25519_PRIME,\n            valueOf(486662), ONE, CURVE_25519_ORDER.shiftRight(3), CURVE_25519_ORDER);\n    MontgomeryECGroup.ECGroupElement   curve25519Base = curve25519.createPoint(\n            valueOf(9), curve25519.mapToY(valueOf(9)));\n    BigInteger   q = curve25519.getCyclicOrder();\n    ECDSA   ecdsa = new ECDSA(curve25519Base, q);\n    DSAHelper.Signature   signature = ecdsa.sign(CHALLENGE56_MSG.getBytes());\n    ECDSA.PublicKey   legitPk = ecdsa.getPublicKey(),\n            forgedPk = Set8.breakChallenge61ECDSA(CHALLENGE56_MSG.getBytes(), signature, ecdsa.getPublicKey());\n    assertTrue(legitPk.verifySignature(CHALLENGE56_MSG.getBytes(), signature));\n    assertTrue(forgedPk.verifySignature(CHALLENGE56_MSG.getBytes(), signature));\n    assertNotEquals(legitPk, forgedPk);\n    \n    // ECDSA is not strongly secure, i.e. if (r, s) is a valid ECDSA signature on m, then so is (r, -s).\n    DSAHelper.Signature   altSignature = new DSAHelper.Signature(signature.getR(), q.subtract(signature.getS()));\n    assertTrue(legitPk.verifySignature(CHALLENGE56_MSG.getBytes(), altSignature));\n    assertTrue(forgedPk.verifySignature(CHALLENGE56_MSG.getBytes(), altSignature));\n}\n```\n\nAn important point about ECDSA worth mentioning is that ECDSA signatures are not _strongly secure_ in the sense that\nif (r, s) is a valid signature on message `m` then it is easy to come up with another valid signature on the same message.\nFor ECDSA that is (r, -s). The last three statements in the above test demonstrate this in action.\nWhy does signature (r, -s) work too? This is easy to see from how `r` is constructed:\n```\nfunction sign(m, d):\n   k := random_scalar(1, n)\n   r := (k * G).x\n   s := (H(m) + d*r) * k^-1\n   return (r, s)\n```\nThere's another point on the curve whose x coordinate matches that of k · G, it is point -k · G. Plugging -k · G\nin the verification formulas shows the desired outcome.\n\nMounting a DSKS attack on RSA is much more laborious. I implemented it for relatively small RSA moduli of 320 bits.\nThe biggest effort went into finding primes `p` and `q` that meet the requirements for 1) `p-1` and `q-1` being smooth, 2)\nboth `s` and `pad(m)` (`s^e = pad(m) mod N`) being generators of the entire Zp* and Zq* groups, and 3) `gcd(p-1, q-1)=2`.\nI used PKCS#1 v1.5 mode 1 padding with SHA-1, just like in [Challenge 42](https://cryptopals.com/sets/6/challenges/42).\n\nTo remind, PKCS#1 v1.5 mode 1 paddding looks as follows:\n```\n0x00 || 0x01 || PS || 0x00 || ASN.1 || HASH\n```\nwhere the padding string `PS` must consist of at least eight 0xff bytes. Given SHA-1 hashes take up 20 bytes, and ASN.1\ndesignation of SHA-1 another 15, this all leads to a minimum RSA modulus length of 20+15+3+8=46 bytes or 368 bits.\nThis length of RSA moduli is rather difficult to work with, so I implemented this challenge by allowing the padding string\n`PS` to consist of two rather than 8 0xff bytes. This reduces the overhead of PKCS#1 padding with SHA-1 to \n20+15+3+2=40 bytes, implying a minimum RSA modulus of 320 bits.\n\nI ended up writing [quite a bit of concurrent code](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/Set8.java#L539-L605)\nto tackle this, and pre-calculated all small primes less than 2\u003csup\u003e20\u003c/sup\u003e\nso as to be able to find primes meeting the criterion 1) above in linear time. Even with such relatively small moduli\n(both p and q are around 160 bits), finding them takes on the order of 20 minutes on my MacBook Pro with all cores searching.\n**NB** it is vital that p*q is larger than the modulus of the original public key, so I search for primes that are 161 bits\nlong to play it safe.\n```\nSuitable primes found:\nDiffieHellmanUtils.PrimeAndFactors(p=2252226720431925817465020447075111488063403846689, factors=[2, 7, 277, 647, 2039, 2953, 14633, 139123, 479387, 904847]),\nDiffieHellmanUtils.PrimeAndFactors(p=2713856776699319359494147955700110393372009838087, factors=[2, 13, 17, 23, 26141, 56633, 80429, 241567, 652429, 1049941])]\n```\n\nAfter that I calculate ep=log\u003csub\u003es\u003c/sub\u003e(pad(m)) mod p and eq=log\u003csub\u003es\u003c/sub\u003e(pad(m)) mod q using [a combination of\nPohlig-Hellman and J.M. Pollard's Lambda Method](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/Set8.java#L485-L530) using a technique from [Challenge 59](https://toadstyle.org/cryptopals/58.txt).\nTo make Pollard's Lambda Method tractable I ensured that the product\nof all prime factors for each of `p-1` and `q-1` is at least 3700000000000000000000000000000000. I arrived at this \nnumber heuristically, for DLogs in a group whose prime is around 160 bits long Pollard's Lambda Method works reasonably fast.\nFor smaller moduli I divide it by 2\u003csup\u003e0.7·(320 - RSA_Modulus_bit_length)\u003c/sup\u003e.\n\nThe following part of the problem description deserves a word of caution\n\u003e 4\\. Use the Chinese Remainder Theorem to put ep and eq together:\n\n         e' = crt([ep, eq], [p-1, q-1])\nThe reasoning behind this formula is pretty straightforward: we know that s\u003csup\u003eep\u003c/sup\u003e\u0026equiv;pad(m) mod p and that\ns\u003csup\u003eeq\u003c/sup\u003e\u0026equiv;pad(m) mod q. Since the computations are in GF(p) and GF(q) by Fermat's theorem this is equivalent to\ns\u003csup\u003eep mod (p-1)\u003c/sup\u003e\u0026equiv;pad(m) mod p and s\u003csup\u003eeq mod (q-1)\u003c/sup\u003e\u0026equiv;pad(m) mod q. Thus we need to find e such that \ne \u0026equiv; ep mod (p-1) and e \u0026equiv; eq mod (q-1). However plugging it into the CRT formula\n\n\u003e e = ( ((ep−eq) ((q-1)\u003csup\u003e−1\u003c/sup\u003e mod (p-1) )) mod (p-1) )·(q-1) + eq\n\nwill fail because (q-1) is not invertible mod (p-1) as they are both even. I used the approach delineated\nin Section 4.1 of [this paper](http://mpqs.free.fr/corr98-42.pdf) to correctly tackle it.\n\nOne interesting nuance that @spdevlin doesn't explain is why the forged `e` is a valid RSA exponent. We made sure that\nwe found two suitable smooth primes `p` and `q`. You will recall that one of the conditions for candidate primes was\nthat both `s` and `pad(m)` be generators of the entire Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e and Z\u003csub\u003eq\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e groups\n(i.e. that they are both primitive roots of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e and Z\u003csub\u003eq\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e respectively):\n\u003e s shouldn't be in any subgroup that pad(m) is not in. If it is,\nthe discrete logarithm won't exist. The simplest thing to do is\nmake sure they're both primitive roots.\n\nThis implies that `ep` (in `s^ep = pad(m) mod p`) is relatively prime with `p-1` \u0026mdash; the order of Z\u003csub\u003ep\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e.\nAnalogously `eq` is relatively prime with `q-1`. This, in turn, implies that `e` is relatively prime with `(p-1)·(q-1)` the order\nof group Z\u003csub\u003eN\u003c/sub\u003e\u003csup\u003e\\*\u003c/sup\u003e and hence a valid RSA exponent.\n\nThwarting DSKS attacks is trivial, the signer needs to attach their public key to the message before signing it. While \nthe verifier should do an extra check to ensure the public key they use to verify corresponds to the one added\nto the message. This way, the signing public key is authenticated along with the message. On top of it, it makes sense\nto pay attention to the public keys of RSA and be suspicious of public exponents `e` that are not among the commonly\nused ones: { 3, 5, 17, 65537 }.\n\nThe challenge ends with the following invitation:\n\u003e Since RSA signing and decryption are equivalent operations, you can\nuse this same technique for other surprising results. Try generating a\nrandom (or chosen) ciphertext and creating a key to decrypt it to a\nplaintext of your choice!\n\nI tackled it by:\n1. Generating an RSA key-pair with a 304-bit modulus. Let's call the resulting key-pair `legitRsa`.\n2. PKCS#1 v1.5 padding the plaintext message `id135: credentials invalid` using mode 2 encryption, as explained in [Challenge 47](https://cryptopals.com/sets/6/challenges/47).\nLet's call the result of this operation `padm`. This will play the role of a legit plaintext. To remind, PKCS#1 v1.5 padding\nfor encryption adds randomness so that padding the same plaintext message twice results in two different pads.\n3. PKCS#1 v1.5 padding the plaintext message `id135: credentials valid!` using mode 2 encryption. Let's call the result\nof this operation `forgedPadm`. This will play the role of a forged plaintext.\n4. Encrypting `padm` using `legitRsa` public key. Let's call the result of this operation `cTxt`\n5. Searching for an RSA key-pair of approximately the same size as the modulus of `legitRsa` and for which the following equation\nholds: cTxt^forgedD\u0026equiv;forgedPadm (mod forgedN).  \n  Where `forgedD` is the private key of the forged RSA key-pair and\n`forgedN` is its RSA modulus. This search is done in exactly the same way as for the DSKS attack on RSA earlier in this challenge.\n\nAnd it all works:\n```java\n@Test\nvoid challenge61RSAEncryption() {\n    RSAHelperExt rsa = new RSAHelperExt(RSAHelper.PUBLIC_EXPONENT, 152);\n    String   plainTxt = \"id135: credentials invalid\",  forgedPlainTxt = \"id135: credentials valid!\";\n    BigInteger   padm = RSAHelperExt.pkcs15Pad(plainTxt.getBytes(), rsa.getPublicKey().getModulus().bitLength()),\n    cTxt = rsa.encrypt(padm),\n    forgedPadm = RSAHelperExt.pkcs15Pad(forgedPlainTxt.getBytes(),\n    rsa.getPublicKey().getModulus().bitLength());\n    assertArrayEquals(rsa.pkcs15Unpad(rsa.decrypt(cTxt)), plainTxt.getBytes());\n\n    RSAHelperExt   forgedRsa = Set8.breakChallenge61RSA(forgedPadm, cTxt,\n    rsa.getPublicKey().getModulus().bitLength(), false);\n\n    byte[]   pTxt = rsa.pkcs15Unpad(forgedRsa.decrypt(cTxt));\n    assertArrayEquals(pTxt, forgedPlainTxt.getBytes());\n    System.out.println(\"Decrypted ciphertext: \" + new String(pTxt) );\n}\n```\n\nThe unit test passes and outputs:\n```\nDecrypted ciphertext: id135: credentials valid!\n```\n\n\n### Challenge 62. Key-Recovery Attacks on ECDSA with Biased Nonces\n[Challenge 62](https://toadstyle.org/cryptopals/62.txt) is an excellent example of what could happen if a cryptographic\nprimitive is used incorrectly. It is an egregious misnomer to call the random integer `k` used in DSA signing a nonce.\nA nonce is a number used once. But there's an important caveat to it \u0026mdash; a proper cryptographic algorithm expecting\na nonce should be secure even if an adversary gets to choose its nonces (provided they are all unique, of course).\nDSA's `k` must be a cryptographically strong (i.e. unpredictable) uniformly distributed random number for the resulting\nsigning scheme to be secure. I will henceforth put DSA's nonce in quotation marks to accentuate that it cannot be treated \nas a real nonce.\n\nIn this attack we get to see what can happen when \"nonce\" `k` is biased: its `l` least significant bits are zero. In this\ncase all signatures end up sharing the same \"nonce\" suffix `00000000`. To make the attack closer to a real-world setting\nI implemented the challenge using [curve secp256k1](https://en.bitcoin.it/wiki/Secp256k1).\nThis curve is used by Bitcoin, Etherium, and Ripple. The authors of [this paper](https://eprint.iacr.org/2019/023.pdf)\nfound multiple cases of signatures with the same key whose \"nonces\" shared the same suffix. To quote:\n\n\u003e _256-bit nonces with shared 128-bit suffixes_. 121 signatures were compromised by nonces that shared a 128-bit suffix\nwith at least one other signature. 55 of these signatures were used with multisignature addresses and 66 were generated\nby non-multisignature addresses. 13 keys were compromised this way, which had generated a total of 224 signatures. There\nwere 20 distinct suffixes that had been used by these keys. The earliest signature of this type that we found was from\nMarch 2015, and the most recent was from August 2018. Some of the keys were used with nonces that all shared the same\nsuffix, and some were used with nonces of varying and occasionally unique suffixes.\n\nEven though this is much more biased than the 8-bit shared suffixes we get to exploit in this challenge, it still highlights\nhow practical this attack is.\n\nThe explanation of the math behind the attack provided by @spdevlin is simply superb. There's one petty inaccuracy in\nthe problem description: the lattice that needs to be constructed should look like\n```\nb1 = [  q  0  0  0  0  0 ...  0  0  0 ]\nb2 = [  0  q  0  0  0  0 ...  0  0  0 ]\nb3 = [  0  0  q  0  0  0 ...  0  0  0 ]\nb4 = [  0  0  0  q  0  0 ...  0  0  0 ]\nb5 = [  0  0  0  0  q  0 ...  0  0  0 ]     (1)\nb6 = [  0  0  0  0  0  q ...  0  0  0 ]\n        ...              ...\nbn = [  0  0  0  0  0  0 ...  q  0  0 ]\nbt = [ t1 t2 t3 t4 t5 t6 ... tn ct  0 ]\nbu = [ u1 u2 u3 u4 u5 u6 ... un  0 cu ]\n```\nand have dimension [n+2 x n+2] (in the problem description it is mistakenly shown to have dimension [n+2 x n+3]).\n\n\nThe main point to fathom is that the vector\n\nbu - d·bt + m\u003csub\u003e1\u003c/sub\u003e·b\u003csub\u003e1\u003c/sub\u003e + m\u003csub\u003e2\u003c/sub\u003e·b\u003csub\u003e2\u003c/sub\u003e + ... + m\u003csub\u003en\u003c/sub\u003e·b\u003csub\u003en\u003c/sub\u003e    (2)\n\nis reasonably short and hence is likely to be present in the reduced basis we obtain for our lattice (1). Why is it short?\nBecause early in the problem description we learnt that \nu - d·t + m·q ~ 0 or less than q/2\u003csup\u003el\u003c/sup\u003e to be precise. This means that each element of (2) is less than\nq/2\u003csup\u003el\u003c/sup\u003e and therefore the length of (2) is much shorter than the length of each of the vectors in our original lattice (1).\n\nThe implementation of the Gram-Schmidt orthogonalization process and the Lenstra-Lenstra-Lovasz basis reduction algorithm\nwas fairly straightforward. I opted for infinite precision floating point arithmetic provided by Java's BigDecimal.\nI created [a class with static methods for matrix operations over a field of reals](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/RealMatrixOperations.java)\nand a simple unit test to verify that the main lattice operations work correctly:\n```java\n@Test\nvoid  matrixOperationsOverFieldOfRealsForChallenge62() {\n    BigDecimal[][]   basis = { { BigDecimal.valueOf(-2), BigDecimal.ZERO, BigDecimal.valueOf(2), BigDecimal.ZERO },\n                               { BigDecimal.valueOf(.5), BigDecimal.valueOf(-1), BigDecimal.ZERO, BigDecimal.ZERO },\n                               { BigDecimal.valueOf(-1), BigDecimal.ZERO, BigDecimal.valueOf(-2), BigDecimal.valueOf(.5) },\n                               { BigDecimal.valueOf(-1), BigDecimal.ONE, BigDecimal.ONE, BigDecimal.valueOf(2) }},\n\n            expectedReducedBasis = { { BigDecimal.valueOf(.5), BigDecimal.valueOf(-1), BigDecimal.ZERO, BigDecimal.ZERO },\n                                     { BigDecimal.valueOf(-1), BigDecimal.ZERO, BigDecimal.valueOf(-2), BigDecimal.valueOf(.5) },\n                                     { BigDecimal.valueOf(-.5), BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.valueOf(2) },\n                                     { BigDecimal.valueOf(-1.5), BigDecimal.valueOf(-1), BigDecimal.valueOf(2), BigDecimal.ZERO  }},\n\n            orthogonalBasis = RealMatrixOperations.gramSchmidt(basis),\n            reducedBasis = RealMatrixOperations.lLL(basis, BigDecimal.valueOf(.99));\n\n    // Is the Gram-Schmidt orthogonalization process implemented correctly?\n    for (int i=0; i \u003c orthogonalBasis.length; i++) {\n        for (int j=i+1; j \u003c orthogonalBasis.length; j++) {\n            assertEquals(0, BigDecimal.ZERO.compareTo( /* The dot product of each pair of distinct vectors must be 0 */\n                    RealMatrixOperations.innerProduct(orthogonalBasis[i], orthogonalBasis[j]).setScale(10, BigDecimal.ROUND_HALF_EVEN)));\n        }\n    }\n\n    // Is L^3-lattice basis reduction algorithm implemented correctly?\n    assertTrue(RealMatrixOperations.equals(expectedReducedBasis, reducedBasis));\n\n}\n```\nCreating a biased ECDSA signer [was trivial](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/BiasedECDSA.java#L25-L30) too.\n\nOne nuance worth pointing out is the number of signatures required to recover the private key. @spdevlin writes:\n\u003e I get good results with as few as 20 signatures. YMMV.\n\nWell, the actual number of signatures required to assuredly recover the private key will depend on the curve chosen.\nSince I chose secp256k1 \u0026mdash; a pretty advanced secure curve, my mileage turned out to be quite different indeed.\nI ended up needing 26 messages signed with the same key and different biased nonces. Moreover I had to increase the\nlength of the shared suffix from 8 bits to 12. And, voilà, within half an hour I am able to recover the key:\n\n```\nExtracted private key: 0x59dc17a4bc3b63a7df0b0cde5d58119caa1b2c711ef46fa59735d8f7fe09e9d1\nActual private key:    0x59dc17a4bc3b63a7df0b0cde5d58119caa1b2c711ef46fa59735d8f7fe09e9d1\n```\n\n[The code of the main test](https://github.com/ilchen/cryptopals/blob/master/src/test/java/com/cryptopals/Set8Tests.java#L229-L260) is pretty compact:\n```java\n@Test\nvoid challenge62() {\n    // Using Bitcoin's secp256k1\n    WeierstrassECGroup   secp256k1 = new WeierstrassECGroup(CURVE_SECP256K1_PRIME, ZERO, valueOf(7), CURVE_SECP256K1_ORDER);\n    BigInteger   baseX = new BigInteger(\"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\", 16);\n    WeierstrassECGroup.ECGroupElement   secp256k1Base = secp256k1.createPoint(baseX, secp256k1.mapToY(baseX));\n    BigInteger   q = secp256k1.getCyclicOrder();\n\n    // Check whether the curve behaves as expected\n    assertEquals(secp256k1Base.inverse(), secp256k1Base.scale(q.subtract(ONE)));\n    assertEquals(secp256k1.getIdentity(), secp256k1Base.scale(q));\n    assertEquals(secp256k1.getIdentity(), secp256k1Base.combine(secp256k1Base.inverse()));\n\n    int   l = 12;   /* The number of least significant bits in k that will be 0 */\n    BiasedECDSA   ecdsa = new BiasedECDSA(secp256k1Base, q, l);\n    int   numMsgs = 26;                      // Each call to getPlainText(6) returns random plaintext 2^6 bytes long\n    BigInteger[][]   tuPairs = IntStream.range(0, numMsgs).mapToObj(x -\u003e Set8.getPlainText(6)).map(m -\u003e {\n        BigInteger[]   tuPair = new BigInteger[2];\n        DSAHelper.Signature  sign = ecdsa.sign(m);\n        // t = r / (s*2^l)\n        tuPair[0] = sign.getR().multiply(sign.getS().multiply(ONE.shiftLeft(l)).modInverse(q)).mod(q);\n        // u = H(m) / (-s*2^l)\n        tuPair[1] = hashAsBigInteger(m).multiply(sign.getS().negate().multiply(ONE.shiftLeft(l)).modInverse(q)).mod(q);\n        return  tuPair;\n    }).toArray(BigInteger[][]::new);\n\n    LatticeAttackHelper   helper = new LatticeAttackHelper(tuPairs, q, l);\n    BigInteger   pk = helper.extractKey();\n    System.out.printf(\"Extracted private key:\\t0x%x%nActual private key:\\t\\t0x%x%n\", pk, ecdsa.getPrivateKey());\n    assertEquals(ecdsa.getPrivateKey(), pk);\n}\n```\n\n### Challenge 63. Key-Recovery Attacks on GCM with Repeated Nonces\n[Challenge 63](https://toadstyle.org/cryptopals/63.txt) consists of six parts:\n1. Implementing GF(2\u003csup\u003e128\u003c/sup\u003e) \u0026mdash; Polynomial Galois field over GF(2)\n2. Implementing Galois Counter Mode (GCM) where the earlier devised GF(2\u003csup\u003e128\u003c/sup\u003e) is used to calculate \nthe one-time-MAC \u0026mdash; GMAC\n3. Implementing a polynomial ring over GF(2\u003csup\u003e128\u003c/sup\u003e)\n4. Solving the problem of factoring polynomials\n5. Realising the actual attack of recovering the authentication key of GMAC provided a nonce was repeated\n6. Asking yourself a question of what you can do with the recovered authentication key\n\nAll in all it is a fairly laborious challenge that took me quite some time to complete. The effort is commensurate\nto a university coursework. On the other hand it helped me consolidate my understanding of finite fields\nand polynomial rings like no text book would ever permit.\n\n#### Implementing GF(2\u003csup\u003e128\u003c/sup\u003e)\nI came up with a fairly straightforward implementation of GF(2\u003csup\u003e128\u003c/sup\u003e) using [Java's BigInteger](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html).\nSee [com.cryptopals.set_8.PolynomialGaloisFieldOverGF2](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/PolynomialGaloisFieldOverGF2.java)\nfor details.\n\n#### Implementing Galois Counter Mode (GCM)\nA correct implementation of GCM turned out a bit more tricky to get right. Here are a couple of important nuances to\nbear in mind:\n* When preparing a buffer over which to calculate the GMAC `a0 || a1 || c0 || c1 || c2 || len(AD) || len(C)` everything\nmust be encoded using a big-endian ordering. Padding is done with zero bits appended.\nI found [this document from NIST](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) to\nto be a good reference.\n* When converting blocks of plain text into elements of GF(2\u003csup\u003e128\u003c/sup\u003e) and vice versa, the following enjoinder\nfrom @spdevlin is crucial\n\u003e We can convert a block into a field element trivially; the leftmost bit is the coefficient of x^0, and so on.\n\nAt the end all fell into place and I was able to confirm my implementation of the GCM to produce the same results\nas that from the JRE:\n```java\n@Test\nvoid GCM() {\n    KeyGenerator aesKeyGen = KeyGenerator.getInstance(\"AES\");\n    SecretKey key = aesKeyGen.generateKey();\n    GCM   gcm = new GCM(key);\n    byte[]   nonce = new byte[12],  plnText = CHALLENGE56_MSG.getBytes(),  cTxt1,  cTxt2,  assocData = new byte[0];\n    new SecureRandom().nextBytes(nonce);\n    cTxt1 = gcm.cipher(plnText, assocData, nonce);\n\n    // Confirm that we get the same ciphertext as that obtained from a reference implementation.\n    Cipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n    // Create GCMParameterSpec\n    GCMParameterSpec   gcmParameterSpec = new GCMParameterSpec(16 * 8, nonce);\n    cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);\n    cTxt2 = cipher.doFinal(plnText);\n    assertArrayEquals(cTxt2, cTxt1);\n\n    // Confirm that decrypting will produce the original plain text\n    assertArrayEquals(plnText, gcm.decipher(cTxt1, assocData, nonce));\n\n    // Confirm that garbling a single byte of cipher text will result in the bottom symbol\n    cTxt1[0] ^= 0x03;\n    assertArrayEquals(null, gcm.decipher(cTxt1, assocData, nonce));\n}\n```\n\n#### Implementing a polynomial ring over a finite field\nInstead of implementing a polynomial ring over GF(2\u003csup\u003e128\u003c/sup\u003e) I decided to implement it as\n[a generic class](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/PolynomialRing2.java)\nover [any finite field](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/FiniteFieldElement.java):\n```java\npublic interface FiniteFieldElement {\n    FiniteFieldElement  add(FiniteFieldElement e);\n    FiniteFieldElement  subtract(FiniteFieldElement e);\n    /**\n     * Computes this + this + ... + this {@code k} times\n     * @return  an object of the implementing class.\n     */\n    FiniteFieldElement  times(BigInteger k);\n    FiniteFieldElement  multiply(FiniteFieldElement e);\n    FiniteFieldElement  modInverse();\n    /**\n     * Computes this * this * ... * this {@code k} times, i.e. computes this\u003csup\u003ek\u003c/sup\u003e\n     * @return  an object of the implementing class.\n     */\n    FiniteFieldElement  scale(BigInteger k);\n    FiniteFieldElement  getAdditiveIdentity();\n    FiniteFieldElement  getMultiplicativeIdentity();\n    BigInteger  getOrder();\n    BigInteger  getCharacteristic();\n}\n```\n\nSo as to test my implementation of polynomial rings, I wrote a class representing\n[GF(Z\u003csub\u003ep\u003c/sub\u003e) fields](https://github.com/ilchen/cryptopals/blob/master/src/test/java/com/cryptopals/ZpField.java).\nIt is much easier to reason about Z\u003csub\u003ep\u003c/sub\u003e arithmetic than arithmetic in GF(2\u003csup\u003e128\u003c/sup\u003e).\n\n#### Solving the problem of factoring polynomials\nThis entailed working out:\n* Division of polynomials: https://en.wikipedia.org/wiki/Polynomial_long_division#Pseudocode\n* Differentiation of polynomials\n* GCD for polynomials\n* Square-free factorization of polynomials: https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Square-free_factorization\n* Distinct-degree factorization of polynomials: https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Distinct-degree_factorization\n* Equal-degree factorization of polynomials: https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Equal-degree_factorization\n\n##### Distinct-degree factorization\nOf these problems I spent the most time getting distinct-degree factorization to work. The first obstacle I faced was my earlier\ndecision to represent polynomials as [arrays of coefficients](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/PolynomialRing.java#L14-L22).\nThis algorithm requires dealing with polynomials whose degree is the order of the field and higher, which turns out\nto be 2\u003csup\u003e128\u003c/sup\u003e for this field. E.g. a polynomial like this one:\nx\u003csup\u003e2\u003csup\u003e128\u003c/sup\u003e\u003c/sup\u003e - x = x\u003csup\u003e340282366920938463463374607431768211456\u003c/sup\u003e + x in GF(2\u003csup\u003e128\u003c/sup\u003e).\nTo tackle it I switched to representing polynomials in [a way that stores only their non-zero coefficients](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/PolynomialRing2.java#L13-L25).\n\nThe second obstacle was the awful running time of [the Distinct-degree factorization algorithm from Wikipedia](https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields#Distinct-degree_factorization).\nIt has a running time of O(q) where q is the order of GF(2\u003csup\u003e128\u003c/sup\u003e), which takes forever. I tackled it by\nadopting [a Distinct-degree factorization algorithm that uses repeated squaring](https://www.cmi.ac.in/~ramprasad/lecturenotes/comp_numb_theory/lecture10.pdf).\n\n##### Equal-degree factorization\nEqual-degree factorization, while fairly well delineated by @spdevlin, presented a couple of difficulties too. To start\nwith, calculating\n```g := h^((q^d - 1)/3) - 1 mod f```\nas specified in the problem description will take forever for the very same reason as I indicated above \u0026mdash;\nthe order (q\u003csup\u003ed\u003c/sup\u003e-1)/3 will be too large. You need to raise to this high a power by constantly taking modulus\nof f in your exponentiation routine. I solved it by implementing [a scaleMod method](https://github.com/ilchen/cryptopals/blob/master/src/main/java/com/cryptopals/set_8/PolynomialRing2.java#L235-L257) on my Polynomial Ring class.\n\nThe other difficulty is that the square-free polynomial without distinct-degree factors that you pass to your edf implementation \nmight not have factors of the degree you specify. This would lead to the algorithm running ad infinitum...\nFor example in my approach I always call edf with a desired degree of factors being 1. While the setting guarantees\nthe presence of at least one factor of degree 1 for the original polynomial, there's no guarantee that each polynomial\nspewed out by distinct-degree factorization can be factoed in one-degee polynomials. I dealt with this predicament\nby setting a heuristic limit on the maximum number of passes through the loop in my edf implementation. When the polynomial\npassed to edf  has factors of requested degree, this heuristic limit will not halt the loop without finding the factors\nwith a probability close to 1.\n```java\n// maxPasses ensures the method doesn't hang if this polynomial can't be factored into d-degree polynomials\nint   maxPasses = 5 * (int) Math.ceil((32 - Integer.numberOfLeadingZeros(r)) * 2.5);\n```\n\n#### Realising the actual attack of recovering the authentication key\nAll the hard work on implementing square-free factorization, distinct-degree factorization, end equal-degree factorization\ncan finally be brought to bear:\n```java\nKeyGenerator aesKeyGen = KeyGenerator.getInstance(\"AES\");\nSecretKey key = aesKeyGen.generateKey();\nGCM   gcm = new GCM(key);\nbyte[]   nonce = new byte[12],  plnText = \"crazy flamboyant for the rap enjoyment\".getBytes(),\n                               plnText2 = \"dummy text to try\".getBytes(),\n         cTxt1,  cTxt2,  assocData = \"valid assoc.Data\".getBytes();\nnew SecureRandom().nextBytes(nonce);\n// a0 || a1 || c0 || c1 || c2 || (len(AD) || len(C)) || t\ncTxt1 = gcm.cipher(plnText, assocData, nonce);\n// Reusing the same nonce, thereby making ourselves vulnerable to the attack.\ncTxt2 = gcm.cipher(plnText2, assocData, nonce);\n\n\nPolynomialRing2\u003cPolynomialGaloisFieldOverGF2.FieldElement\u003e   poly1 = GCM.toPolynomialRing2(cTxt1, assocData),\n                                                             poly2 = GCM.toPolynomialRing2(cTxt2, assocData),\n                                                             equation = poly1.add(poly2).toMonicPolynomial();\nSystem.out.println(\"cTxt1 polynomial: \" + poly1);\nSystem.out.println(\"cTxt2 polynomial: \" + poly2);\nSystem.out.println(\"Equation: \" + equation);\n\nList\u003cPolynomialRing2\u003cPolynomialGaloisFieldOverGF2.FieldElement\u003e\u003e\n        allFactors = equation.squareFreeFactorization().stream().map(PolynomialRing2.PolynomialAndPower::getFactor)\n                .flatMap(x -\u003e x.distinctDegreeFactorization().stream()).collect(Collectors.toList()),\n\n        oneDegreeFactors = allFactors.stream().filter(x -\u003e x.intDegree() == 1).collect(Collectors.toList()),\n\n        oneDegreeFactorsThroughEdf = allFactors.stream().filter(x -\u003e x.intDegree() \u003e 1)\n            .flatMap(x -\u003e x.equalDegreeFactorization(1).stream()).collect(Collectors.toList());\n\nSystem.out.println(\"Actual authentication key: \" + gcm.getAuthenticationKey());\nSystem.out.println(\"Candidates found after distinct-degree factorization: \" + oneDegreeFactors);\nSystem.out.println(\"Additional candidates found after equal-degree factorization: \" + oneDegreeFactorsThroughEdf);\n\noneDegreeFactors.addAll(oneDegreeFactorsThroughEdf);\nList\u003cPolynomialGaloisFieldOverGF2.FieldElement\u003e   candidateAuthenticationKeys =\n        oneDegreeFactors.stream().map(x -\u003e x.getCoef(0)).collect(Collectors.toList());\nassertTrue(candidateAuthenticationKeys.contains(gcm.getAuthenticationKey()));\n```\n\nRunning it produces the following output:\n```\ncTxt1 polynomial: 862e862274c6f6cece8604269636866ex^5 + a2c92e99dba07ce117b3bb3665fedff9x^4 + f4551f6d035a339b2e5b061ca2830ce4x^3 + 320f10f267edx^2 + c800000000000000100000000000000x + 5aafa98bf7b25cfe22f5e630f97d59e9\ncTxt2 polynomial: 862e862274c6f6cece8604269636866ex^4 + c291acf103e2e47987fbbb368dce3f19x^3 + 7ex^2 + 11000000000000000100000000000000x + b3180499d9b2e60566ac9c204aad7ff\nEquation: x^5 + 597419e85ea532a59c4d0eed034a9044x^4 + 812f7801991ead15d455a70fcb83086fx^3 + 87c24da8d5f25ea3e9f92b8c9b319712x^2 + 10ec19974d245b5f16890e6a1effeec8x + e4cb0203ef19430f3c13947f6f6d17a7\nActual authentication key: 67cf01239432c85151d9f7c021bfd121\nCandidates found after square-free and distinct-degree factorization: []\nAdditional candidates found after equal-degree factorization: [x + 67cf01239432c85151d9f7c021bfd121, x + e8cde43205a09d05379422572e11dfb5]\n```\n\n#### Asking yourself a question of what you can do with the recovered authentication key\nHaving gone to the lengths of completing this Herculean labour of recovering the GMAC authentication key from a victim\nwho naively encrypted two different plain texts with the same nonce, you might wonder what you can do with it. Well,\nyou can forge a piece of distinct cipher text that the cryptosystem you attack will authenticate. In other words you can\nmount an _existential forgery_ attack.\n\nImagine that `t0` is the last block of the first cipher text you have `cTxt1`. Looking at the way it was calculated\n\n```t0 = a0*h^5 + c0*h^4 + c1*h^3 + c2*h^2 + l0*h + s```\n\nand noting that you have both `a0` and `h`, you can go far. Say `a'0` is a block of your bogus associated data you want to swap for\nthe legitimate block `a0`. What you do is first subtract `a0*h^5` from `t0` and then replace it with a block of bogus\nassociated data by adding `a'0*h^5` to `t0`. Here's how it looks in my code:\n```java\n/**\n * Forges valid cipher text from legit cipher text and associated data coupled with a recovered authentication key.\n * @param additionalBogusAssocData  blocksize-long buffer, must be the same size as padded {@code legitAssocData}\n */\npublic static byte[]  forgeCipherText(byte[] legitCipherText, byte[] legitAssocData, byte[] additionalBogusAssocData,\n                                      PolynomialGaloisFieldOverGF2.FieldElement authenticationKey) {\n    int    plainTextLen = legitCipherText.length - BLOCK_SIZE,\n           assocDataPaddedLen = (legitAssocData.length / BLOCK_SIZE + (legitAssocData.length % BLOCK_SIZE != 0  ?  1 : 0)) * BLOCK_SIZE,\n           plainTextPaddedLen = (plainTextLen / BLOCK_SIZE + (plainTextLen % BLOCK_SIZE != 0  ?  1 : 0)) * BLOCK_SIZE,\n           lastPower = plainTextPaddedLen / BLOCK_SIZE + 1,\n           last = additionalBogusAssocData.length / BLOCK_SIZE;\n\n    if (additionalBogusAssocData.length != assocDataPaddedLen) {\n        throw new IllegalArgumentException(\"additionalBogusAssocData must be of same length as padded legit associated data and not \"\n                                           + additionalBogusAssocData.length);\n    }\n\n    // We start with the original legit tag...\n    PolynomialGaloisFieldOverGF2.FieldElement   forgedTag = toFE(Arrays.copyOfRange(\n            legitCipherText, legitCipherText.length - BLOCK_SIZE, legitCipherText.length));\n\n    byte[]   buf = new byte[assocDataPaddedLen];\n    System.arraycopy(legitAssocData, 0, buf, 0, legitAssocData.length);\n\n    // ... and then subtract from it the legit associated data and\n    //     add to it bogus associated data.\n    for (int i=last; i \u003e 0; i-=1) {\n        lastPower++;\n\n        // Remove the summand of the legit associated data\n        forgedTag = forgedTag.subtract(\n                toFE( Arrays.copyOfRange(legitAssocData, (last - 1) * BLOCK_SIZE, last * BLOCK_SIZE))\n                        .multiply(authenticationKey.scale(valueOf(lastPower))) );\n\n        // And then add the summand of the bogus associate data\n        forgedTag = forgedTag.add(\n                toFE( Arrays.copyOfRange(additionalBogusAssocData, (last - 1) * BLOCK_SIZE, last * BLOCK_SIZE))\n                    .multiply(authenticationKey.scale(valueOf(lastPower))) );\n    }\n    byte[]  res = legitCipherText.clone();\n    System.arraycopy(forgedTag.asArray(), 0, res, legitCipherText.length - BLOCK_SIZE, BLOCK_SIZE);\n    return  res;\n}\n```\n\nGiving it a spin:\n```\ncTxt1 polynomial: 862e862274c6f6cece8604269636866ex^5 + a2c92e99dba07ce117b3bb3665fedff9x^4 + f4551f6d035a339b2e5b061ca2830ce4x^3 + 320f10f267edx^2 + c800000000000000100000000000000x + 5aafa98bf7b25cfe22f5e630f97d59e9\ncTxt2 polynomial: 862e862274c6f6cece8604269636866ex^4 + c291acf103e2e47987fbbb368dce3f19x^3 + 7ex^2 + 11000000000000000100000000000000x + b3180499d9b2e60566ac9c204aad7ff\nEquation: x^5 + 597419e85ea532a59c4d0eed034a9044x^4 + 812f7801991ead15d455a70fcb83086fx^3 + 87c24da8d5f25ea3e9f92b8c9b319712x^2 + 10ec19974d245b5f16890e6a1effeec8x + e4cb0203ef19430f3c13947f6f6d17a7\nActual authentication key: 67cf01239432c85151d9f7c021bfd121\nCandidates found after square-free and distinct-degree factorization: []\nAdditional candidates found after equal-degree factorization: [x + 67cf01239432c85151d9f7c021bfd121, x + e8cde43205a09d05379422572e11dfb5]\n\nRecovered authentication key: 67cf01239432c85151d9f7c021bfd121\nLegit associated data: valid assoc.Data\nBogus associated data: bogus assoc.Data\nLegit  cipher text: 9FFB7FA66CDDCDE8873E05DB997493452730C1453860DA74D9CC5AC0B6F8AA2FB7E64F08F04C979ABE9F0C67AF447F3A4DEFD195F55A\nForged cipher text: 9FFB7FA66CDDCDE8873E05DB997493452730C1453860DA74D9CC5AC0B6F8AA2FB7E64F08F04CDE9E643787D35A6765241FEC4324627A\nDecrypted by the crypto system under attack into: crazy flamboyant for the rap enjoyment\n\nRecovered authentication key: e8cde43205a09d05379422572e11dfb5\nLegit associated data: valid assoc.Data\nBogus associated data: bogus assoc.Data\nLegit  cipher text: 9FFB7FA66CDDCDE8873E05DB997493452730C1453860DA74D9CC5AC0B6F8AA2FB7E64F08F04C979ABE9F0C67AF447F3A4DEFD195F55A\nForged cipher text: 9FFB7FA66CDDCDE8873E05DB997493452730C1453860DA74D9CC5AC0B6F8AA2FB7E64F08F04CD3355D1A58A0B6E730F4356A7027481F\nDecrypted by the crypto system under attack into: ⊥\n```\nI am able to commit an existential forgery attack!\n\n\n### Challenge 64. Key-Recovery Attacks on GCM with a Truncated MAC\n[Challenge 64](https://toadstyle.org/cryptopals/64.txt) implements an attack first outlined by Niels Ferguson in his \n[Authentication weaknesses in GCM](https://csrc.nist.gov/csrc/media/projects/block-cipher-techniques/documents/bcm/comments/cwc-gcm/ferguson2.pdf)\npaper. GCM is the most popular standard for authenticated encryption and is used in TLS 1.2 and higher. To aid efficient\nfast implementations of GCM Intel even added [a special new instruction PCLMULQDQ](https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf),\nwhich makes it easy to implement GCM's GHASH hash function. Of the different modes of authenticated encryption not\nencumbered by patents and certified by NIST, GCM is the fastest. It is faster than CCM and EAX. Moreover, with the help of Intel's PCLMULQDQ\ninstruction for GHASH, it can be implemented with less code than would otherwise be required. All of these are the main\nreasons for GCM's popularity.\n\nNiels's paper shows that the actual authentication security of GCM will be less than the number of bits in its authentication tag.\nGiven the maximum tag size of 128 bits, the best possible authentication security of GCM can be `128 - k` bits where k\nis  \u0026lfloor;log\u003csub\u003e2\u003c/sub\u003e(number-of-blocks-encrypted)\u0026rfloor;. Niels's paper shows that for smaller authentication tag\nsizes, it will be worse than `n - k` bits because of some peculiarities of the GHASH one-time hash function that GCM uses.\n\nLike Challenge 63, this challenge shows how to succeed at an existential forgery attack on GCM. This time without your\nadversary having made any mistakes in using GCM apart from choosing a small authentication tag size. Namely the minimum\nsize allowed for GCM's authentication tag by NIST of 32 bits.\n\nTo tackle this challenge you will need to implement the following parts.\n\n1. Linear algebra routines for GF(2) and GF(2\u003csup\u003e128\u003c/sup\u003e):\n   * Implementing a vector representation for elements of GF(2\u003csup\u003e128\u003c/sup\u003e);\n   * Implementing a matrix representation for multiplication by a constant in GF(2\u003csup\u003e128\u003c/sup\u003e) and for squaring in GF(2\u003csup\u003e128\u003c/sup\u003e);\n   * Implementing basic operations for matrices in GF(2): addition, multiplication, scaling, transposition,\n   Gaussian elimination, finding a kernel. \n2. Extraction and replacement of 2\u003csup\u003ei-th\u003c/sup\u003e blocks of ciphertext counting from the end, i.e. all blocks of\n   the ciphertext that are the coefficients of x\u003csup\u003e2^i\u003c/sup\u003e (where i = 1, 2, ..., n) in the GHASH polynomial\n   in the indeterminate x over GF(2\u003csup\u003e128\u003c/sup\u003e).\n3. Calculation of matrix A\u003csub\u003ed\u003c/sub\u003e = \u0026sum;M\u003csub\u003eDi\u003c/sub\u003e(M\u003csub\u003eS\u003c/sub\u003e)\u003csup\u003ei\u003c/sup\u003e, where M\u003csub\u003eDi\u003c/sub\u003e are \n   matrix representations of the differences between the 2\u003csup\u003ei-th\u003c/sup\u003e element of ciphertext and its forged counterpart.\n   Along with the calculation of the dependency matrix T, as explained in the challenge.\n4. Finding the kernel of the matrix T, whose elements represent all the possible manipulations to the 2\u003csup\u003ei-th\u003c/sup\u003e\n   blocks of ciphertext that don't change the most significant 16 bits of GHASH.\n5. Attempting an existential forgery attack on the smallest allowed GHASH tag size of 32 bits.\n6. Recovering the authentication key.\n\n#### Linear algebra\n##### Implementing a vector representation for elements of GF(2\u003csup\u003e128\u003c/sup\u003e)\nI added two new methods to my class for representing GF(2\u003csup\u003e128\u003c/sup\u003e) elements:\n[PolynomialGaloisFieldOverGF2::FieldElement::asVector](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/PolynomialGaloisFieldOverGF2.java#L150-L158)\nand [PolynomialGaloisFieldOverGF2::createElement](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/PolynomialGaloisFieldOverGF2.java#L47-L58)\n\n##### Implementing a matrix representation for multiplication by a constant and for squaring\nAnalogously to vector representation, I wrote these as methods of my class for GF(2\u003csup\u003e128\u003c/sup\u003e):\n[PolynomialGaloisFieldOverGF2::FieldElement::asMatrix](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/PolynomialGaloisFieldOverGF2.java#L160-L173)\nand [PolynomialGaloisFieldOverGF2::getSquaringMatrix](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/PolynomialGaloisFieldOverGF2.java#L64-L80)\n\n##### Implementing basic operations for matrices in GF(2)\nI felt it would be an overkill to create a whole new class to represent matrices over GF(2), instead I went for\na simple representation as `boolean[][]` and the [BooleanMatrixOperations class](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/BooleanMatrixOperations.java)\nwith static methods that accept matrices and vectors in GF(2).\n\nTime for some tests to validate that everything works correctly:\n```java\n@DisplayName(\"Linear algebra over GF(2)\") @Test\nvoid  linearAlgebraForChallenge64()  {\n    BigInteger   modulus = ONE.shiftLeft(128).or(valueOf(135));\n    PolynomialGaloisFieldOverGF2   gf = new PolynomialGaloisFieldOverGF2(modulus);\n    PolynomialGaloisFieldOverGF2.FieldElement   c = gf.createElement(valueOf(3)),  y = gf.createElement(valueOf(15));\n\n    assertEquals(c.multiply(y), gf.createElement(multiply(c.asMatrix(), y.asVector())) );\n    assertEquals(y.multiply(y), gf.createElement(multiply(gf.getSquaringMatrix(), y.asVector())) );\n\n    assertEquals(c, gf.createElement(multiply(c.asMatrix(), gf.getMultiplicativeIdentity().asVector())) );\n    assertEquals(y, gf.createElement(y.asVector()));\n\n    boolean[][][]   mss = new boolean[18][][];\n    mss[0] = gf.getSquaringMatrix();\n    for (int i=1; i \u003c 18; i++) {\n        mss[i] = multiply(mss[i-1], mss[0]);\n    }\n    assertEquals(y.scale(valueOf(2)), gf.createElement(multiply(mss[0],  y.asVector()) ));\n    assertEquals(y.scale(valueOf(4)), gf.createElement(multiply(mss[1],  y.asVector()) ));\n    assertEquals(y.scale(valueOf(8)), gf.createElement(multiply(mss[2],  y.asVector()) ));\n    assertEquals(y.scale(valueOf(16)), gf.createElement(multiply(mss[3],  y.asVector()) ));\n\n    // Mc * Ms^i * y) = c * y^4\n    assertEquals(c.multiply(y.scale(valueOf(16))),\n            gf.createElement(multiply(multiply(c.asMatrix(), mss[3]), y.asVector())) );\n\n    // Confirm matrix representation of GHASH works correctly\n    PolynomialGaloisFieldOverGF2.FieldElement    c1 = gf.createRandomElement(),  c2 = gf.createRandomElement(),\n            c4 = gf.createRandomElement(),  c8 = gf.createRandomElement(),  h = gf.createRandomElement(),  tag1,  tag2;\n    // t = c1*h + c2*h^2 + c4*h^4 + c8*h^8\n\n    // First calculate the tag using plain GF(2^128)\n    tag1 = c1.multiply(h).add(c2.multiply(h.scale(valueOf(2)))).add(c4.multiply(h.scale(valueOf(4)))).add(c8.multiply(h.scale(valueOf(8))));\n    // Then do the same using a matrix-based representation of GF(2^128) operations\n    tag2 = gf.createElement(multiply(add(add(add(c1.asMatrix(), multiply(c2.asMatrix(), mss[0])), multiply(c4.asMatrix(), mss[1])), multiply(c8.asMatrix(), mss[2])), h.asVector()));\n    assertEquals(tag1, tag2);\n}\n```\n\nGaussian elimination is a little trickier. I started with [an algorithm on Wikipedia](https://en.wikipedia.org/wiki/Gaussian_elimination#Pseudocode)\nand adapted it for GF(2). A similar algorithm, albeit with a small omission, can be found in [this paper](http://www.hyperelliptic.org/tanja/SHARCS/talks06/smith_revised.pdf\").\nThe result is [this static method](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/BooleanMatrixOperations.java#L44-L95).\n\nThe hardest part is finding the kernel of a matrix. I made use of [this algorithm from Wikipedia](https://en.wikipedia.org/wiki/Kernel_(linear_algebra)#Computation_by_Gaussian_elimination),\nwhich is essentially the same as given by @spdevlin in the problem description:\n\u003e Finding a basis for the null space is not too hard. What you want to\n  do is transpose T (i.e. flip it across its diagonal) and find the\n  reduced row echelon form using Gaussian elimination. Now perform the\n  same operations on an identity matrix of size n*128. The rows that\n  correspond to the zero rows in the reduced row echelon form of T transpose form a basis for N(T).\n\nMy implementation is captured in [this method](https://github.com/ilchen/cryptopals/blob/788dbe6e75a9d97bcac32a45295e3592c47258ec/src/main/java/com/cryptopals/set_8/BooleanMatrixOperations.java#L96-L127).\n\n#### Extraction and replacement of the 2\u003csup\u003ei-th\u003c/sup\u003e blocks of ciphertext\nThis is the easiest part. The only thing to pay attention to is that the blocks to extract are the coefficients of\nh\u003csup\u003e2^i\u003c/sup\u003e (where i = 1, 2, ..., n) in the polynomial in the indeterminate h over GF(2\u003csup\u003e128\u003c/sup\u003e):\nt = s + c\u003csub\u003e1\u003c/sub\u003e·h + c\u003csub\u003e2\u003c/sub\u003e·h\u003csup\u003e2\u003c/sup\u003e + c\u003csub\u003e3\u003c/sub\u003e·h\u003csup\u003e3\u003c/sup\u003e + ... + c\u003csub\u003en\u003c/sub\u003e·h\u003csup\u003en\u003c/sup\u003e. c\u003csub\u003e2\u003c/sub\u003e is the last block of the ciphertext\nbefore the tag, and c\u003csub\u003en\u003c/sub\u003e is the first (assuming that the plain text was 2\u003csup\u003en\u003c/sup\u003e blocks long).\n\nFor efficiency's sake I convert the extracted coefficients into elements of GF(2\u003csup\u003e128\u003c/sup\u003e). The relevant code is here:\n[extraction of coefficients](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/GCM.java#L201-L222),\n[replacement of coefficients](https://github.com/ilchen/cryptopals/blob/e984f1f793f158570087d1c4b0e1fa70b7947253/src/main/java/com/cryptopals/set_8/GCM.java#L224-L237).\n\n\n#### Calculation of matrix A\u003csub\u003ed\u003c/sub\u003e = \u0026sum;M\u003csub\u003eDi\u003c/sub\u003e(M\u003csub\u003eS\u003c/sub\u003e)\u003csup\u003ei\u003c/sup\u003e and a dependency matrix\nWith the routines for linear algebra described in the beginning of this section calculating A\u003csub\u003ed\u003c/sub\u003e is trivial.\nStill a good test is in order to gain confidence before going further:\n```java\n// Generate random coefficients to replace the legit ones for h\u003csup\u003e2^i\u003c/sup\u003e (i=1..17)\ncoeffsPrime = h.getRandomPowerOf2Blocks();\n// Confirm that ad is calculated correctly\nboolean[][]   ad = h.calculateAd(coeffsPrime);\nPolynomialGaloisFieldOverGF2.FieldElement   hash1 = gcm.ghashPower2BlocksDifferences(h.getPowerOf2Blocks(), coeffsPrime),\n            hash2 = coeffs[0].group().createElement(multiply(ad, gcm.getAuthenticationKey().asVector()));\nassertEquals(hash1, hash2);\n```\nAs you can see, I calculate the hash1 over d\u003csub\u003ei\u003c/sub\u003e differences using standard GHASH arithmetic in GF(2\u003csup\u003e128\u003c/sup\u003e)\nand then hash2 by A\u003csub\u003ed\u003c/sub\u003e·h. The two must be the same.\n\nThe dependency matrix is more involved. Before setting out on a path to calculate it, it helps to understand what purpose it\nserves and how it comes about. In his original description of the attack Niels Ferguson writes:\n\u003e It is now easy to force bits of the error polynomial to zero. Write equations setting each of the bits in a single row\n  of A\u003csub\u003eD\u003c/sub\u003e to zero. Each equation imposes a single linear constraint on the choice we have for the bits of the D\u003csub\u003ei\u003c/sub\u003e values.\n  To force a single result bit to zero we have to create 128 linear constraints. If we have n different D\u003csub\u003ei\u003c/sub\u003e coefficients\n  to choose, we have 128 · n free variables and we can force n − 1 bits of the result to zero.\n \nThe depdendency matrix is the A\u003csub\u003ei,j\u003c/sub\u003e coefficients of these equations. Here's how the dependency matrix looks:\n![alt text](https://raw.githubusercontent.com/ilchen/cryptopals/master/src/docs/challenge64_equations.png)\n\nTo calculate it you first need to generate arbitrary coefficients of h\u003csup\u003e2^i\u003c/sup\u003e (i=1..17). They are the starting\npoint for deriving properly forged coefficients. There will be exactly 17·128=2176 bits that you can flip, 128 per each coefficient.\nIn the above equations they are denoted with d\u003csub\u003e0\u003c/sub\u003e, d\u003csub\u003e1\u003c/sub\u003e, ..., d\u003csub\u003e2175\u003c/sub\u003e. The way you then\ncalculate the dependency matrix \u0026mdash; all the different A\u003csub\u003ei,j\u003c/sub\u003e in the above equations \u0026mdash; is precisely\nas described by @spdevlin:\n\u003e Iterate over the columns. Build the hypothetical Ad you'd get by\n  flipping only the corresponding bit. Iterate over the first (n-1)*128\n  cells of Ad and set the corresponding cells in this column of T.\n\nSo you flip d\u003csub\u003e0\u003c/sub\u003e in the coefficient of h\u003csup\u003e2\u003c/sup\u003e that you just generated and then calculate A\u003csub\u003ed\u003c/sub\u003e over your\ngenerated coefficients. The first 16·128 cells of this A\u003csub\u003ed\u003c/sub\u003e ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filchen%2Fcryptopals","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filchen%2Fcryptopals","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filchen%2Fcryptopals/lists"}