diff --git a/app/src/main/kotlin/me/leon/BigInteger.kt b/app/src/main/kotlin/me/leon/BigInteger.kt index 61dbf4a9e1..10213c1a2b 100644 --- a/app/src/main/kotlin/me/leon/BigInteger.kt +++ b/app/src/main/kotlin/me/leon/BigInteger.kt @@ -1,16 +1,19 @@ package me.leon import java.math.BigInteger +import java.math.BigInteger.ONE +import java.math.BigInteger.TWO import java.util.* +import me.leon.ctf.rsa.THREE import me.leon.ext.fromJson import me.leon.ext.readFromNet // this = p -fun BigInteger.phi(q: BigInteger) = (this - BigInteger.ONE) * (q - BigInteger.ONE) +fun BigInteger.phi(q: BigInteger) = (this - ONE) * (q - ONE) fun BigInteger.lcm(other: BigInteger) = this * other / gcd(other) -fun BigInteger.mutualPrime(other: BigInteger) = gcd(other) == BigInteger.ONE +fun BigInteger.mutualPrime(other: BigInteger) = gcd(other) == ONE // this = e fun BigInteger.invert(phi: BigInteger): BigInteger = modInverse(phi) @@ -45,8 +48,7 @@ fun List.product(): BigInteger = reduce { acc, int -> acc * int } fun List.propN(n: BigInteger) = filter { it < BigInteger.ZERO }.fold(n) { acc, bigInteger -> acc / bigInteger.abs() } -fun BigInteger.eulerPhi(n: Int) = - if (n == 1) minus(BigInteger.ONE) else minus(BigInteger.ONE) * pow(n - 1) +fun BigInteger.eulerPhi(n: Int) = if (n == 1) minus(ONE) else minus(ONE) * pow(n - 1) fun getPrimeFromFactorDb(digit: BigInteger) = getPrimeFromFactorDb(digit.toString()) @@ -68,10 +70,10 @@ fun BigInteger.root(n: Int = 2): Array { val sig = this.signum() val v = this.abs() - var high = BigInteger.ONE + var high = ONE while (high.pow(n) <= v) high = high.shiftLeft(1) var low = high.shiftRight(1) - var mid = BigInteger.ONE + var mid = ONE var midCount = 0 while (low < high) { mid = (low + high).shiftRight(1) @@ -97,8 +99,8 @@ fun BigInteger.continuedFraction(another: BigInteger): MutableList { /** 渐进分数线 */ fun List.convergent(): MutableList> { - var (pbe, paf) = BigInteger.ZERO to BigInteger.ONE - var (qbe, qaf) = BigInteger.ONE to BigInteger.ZERO + var (pbe, paf) = BigInteger.ZERO to ONE + var (qbe, qaf) = ONE to BigInteger.ZERO val convergent = mutableListOf>() for (int in this) { pbe = paf.also { paf = int * paf + pbe } @@ -117,8 +119,8 @@ fun BigInteger.wiener(n: BigInteger): BigInteger? { return wienerPQ } println("wiener attack slow") - var q0 = BigInteger.ONE - val m = BigInteger.TWO + var q0 = ONE + val m = TWO val c1 = m.modPow(this, n) val convergent = this.continuedFraction(n).convergent() for ((_, q1) in convergent) { @@ -153,7 +155,7 @@ fun BigInteger.wienerPQ(n: BigInteger): BigInteger? { // d[0] = 1 val q0 = x / y val n0 = q0 - val d0 = BigInteger.ONE + val d0 = ONE var temp = x x = y y = temp.add(y.multiply(q0).negate()) @@ -163,7 +165,7 @@ fun BigInteger.wienerPQ(n: BigInteger): BigInteger? { // n[1] = q[0] * q[1] + 1 // d[1] = q[1] var qI = x / y - val n1 = q0 * qI + BigInteger.ONE + val n1 = q0 * qI + ONE val d1 = qI // i = 2 var dI2 = d0 @@ -211,7 +213,7 @@ fun BigInteger.wienerPQ(n: BigInteger): BigInteger? { // phi(n) = (edg) / k val phiN = this * guessDg / guessK // (p+q)/2 = (pq - (p-1)*(q-1) + 1)/2 - val pPlusQDiv2 = (n - phiN + BigInteger.ONE) / BigInteger.TWO + val pPlusQDiv2 = (n - phiN + ONE) / TWO val subtract = pPlusQDiv2.pow(2).subtract(n) if (subtract < BigInteger.ZERO) break val root = subtract.root() @@ -234,13 +236,13 @@ fun BigInteger.wienerPQ(n: BigInteger): BigInteger? { /** 乘法逆元 (n * m) % p == 1. m = n^-1 =n % p */ fun BigInteger.multiplyInverse(modular: BigInteger): BigInteger { val (gcd, x, _) = gcdExt(modular) - require(gcd == BigInteger.ONE) { "has no multiplicative inverse" } + require(gcd == ONE) { "has no multiplicative inverse" } return x.mod(modular) } private val RANDOM = Random() -fun BigInteger.random(from: BigInteger = BigInteger.ONE): BigInteger { +fun BigInteger.random(from: BigInteger = ONE): BigInteger { val bits = bitLength() var r = BigInteger(bits, RANDOM) while (r < from || r > this) { @@ -248,3 +250,41 @@ fun BigInteger.random(from: BigInteger = BigInteger.ONE): BigInteger { } return r } + +val FOUR = 4.toBigInteger() +val FIVE = 5.toBigInteger() +val SIX = 6.toBigInteger() +val SEVEN = 7.toBigInteger() +val EIGHT = 8.toBigInteger() +val MAP = + mapOf( + THREE to TWO, + FOUR to THREE, + FIVE to THREE, + SIX to FIVE, + SEVEN to FIVE, + ) + +/** todo performance */ +fun BigInteger.preProbablePrime(): BigInteger { + if (this < EIGHT) return MAP[this]!! + var pre: BigInteger + val nn = (this / SIX) * SIX + if (this - nn <= ONE) { + pre = nn - ONE + if (pre.isProbablePrime(100)) { + return pre + } + pre -= FOUR + } else { + pre = nn + ONE + } + while (true) { + if (pre.isProbablePrime(100)) break + pre -= TWO + if (pre.isProbablePrime(100)) break + pre -= FOUR + } + + return pre +} diff --git a/app/src/main/kotlin/me/leon/ctf/rsa/RsaSolver.kt b/app/src/main/kotlin/me/leon/ctf/rsa/RsaSolver.kt index 55273257cb..eb50d0f4c2 100644 --- a/app/src/main/kotlin/me/leon/ctf/rsa/RsaSolver.kt +++ b/app/src/main/kotlin/me/leon/ctf/rsa/RsaSolver.kt @@ -2,6 +2,7 @@ package me.leon.ctf.rsa import java.math.BigInteger import kotlin.math.ln +import kotlin.math.pow import me.leon.* import me.leon.ext.findParallel @@ -9,6 +10,7 @@ object RsaSolver { private val modeNECP = listOf("n", "e", "c", "p") private val modeNEC = listOf("n", "e", "c") + private val modeDEC_P_NEXT_Q = listOf("d", "e", "c", "nbits") private val modeN2EC = listOf("n1", "n2", "e", "c") private val modeNCD = listOf("n", "c", "d") private val modePQREC = listOf("p", "q", "r", "e", "c") @@ -48,6 +50,7 @@ object RsaSolver { } ) params.containKeys(modeDpDq) -> solveDpDq(params) + params.containKeys(modeDEC_P_NEXT_Q) -> solveDEC(params) params.containKeys(modePQREC) || params.containKeys(modePQRnEC) -> solvePQREC(params) params.containKeys(modeNCD) -> solveNCD(params) params.containKeys(modeN2E2C2) -> solveN2E2C2(params) @@ -67,6 +70,32 @@ object RsaSolver { else -> error("wrong parameters!!!") } + private fun solveDEC(params: MutableMap): String { + println("solve D E C nbits") + val d = requireNotNull(params["d"]) + val e = requireNotNull(params["e"]) + val nLen = requireNotNull(params["nbits"]).toInt() + val kPhi = e * d - BigInteger.ONE + val kBits = kPhi.bitLength() - nLen + + val kStart = 2.0.pow(kBits.toDouble() - 1.0).toInt() + val kEnd = 2.0.pow(kBits.toDouble()).toInt() + for (k in kEnd downTo kStart) { + if (kPhi % k.toBigInteger() == BigInteger.ZERO) { + val phi = kPhi / k.toBigInteger() + val p = phi.root(2).first().preProbablePrime() + val q = p.nextProbablePrime() + if (p.phi(q) == phi) { + println("got p=$p q =$q") + params["p"] = p + params["q"] = q + return solvePQEC(params) + } + } + } + return "" + } + private fun solvePQREC(params: MutableMap): String { println("solve P Q R E C") val e = requireNotNull(params["e"]) diff --git a/app/src/test/kotlin/me/leon/ctf/RsaTest.kt b/app/src/test/kotlin/me/leon/ctf/RsaTest.kt index 955dc9ad52..562ac804bb 100644 --- a/app/src/test/kotlin/me/leon/ctf/RsaTest.kt +++ b/app/src/test/kotlin/me/leon/ctf/RsaTest.kt @@ -211,4 +211,10 @@ class RsaTest { val params = "rsa_amm2.txt".parseRsaParams() println(solve(params)) } + + @Test + fun dec() { + val params = "rsa_dec_p_next_q.txt".parseRsaParams() + assertEquals("NCTF{70u2_nn47h_14_v3ry_gOO0000000d}", solve(params)) + } } diff --git a/testdata/ctf/rsa/rsa_dec_p_next_q.txt b/testdata/ctf/rsa/rsa_dec_p_next_q.txt new file mode 100644 index 0000000000..191265ffa0 --- /dev/null +++ b/testdata/ctf/rsa/rsa_dec_p_next_q.txt @@ -0,0 +1,4 @@ +d = 19275778946037899718035455438175509175723911466127462154506916564101519923603308900331427601983476886255849200332374081996442976307058597390881168155862238533018621944733299208108185814179466844504468163200369996564265921022888670062554504758512453217434777820468049494313818291727050400752551716550403647148197148884408264686846693842118387217753516963449753809860354047619256787869400297858568139700396567519469825398575103885487624463424429913017729585620877168171603444111464692841379661112075123399343270610272287865200880398193573260848268633461983435015031227070217852728240847398084414687146397303110709214913 +c = 5382723168073828110696168558294206681757991149022777821127563301413483223874527233300721180839298617076705685041174247415826157096583055069337393987892262764211225227035880754417457056723909135525244957935906902665679777101130111392780237502928656225705262431431953003520093932924375902111280077255205118217436744112064069429678632923259898627997145803892753989255615273140300021040654505901442787810653626524305706316663169341797205752938755590056568986738227803487467274114398257187962140796551136220532809687606867385639367743705527511680719955380746377631156468689844150878381460560990755652899449340045313521804 +e = 0x10001 +nBits = 2048 \ No newline at end of file