-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 51fd60c
Showing
9 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Created by .ignore support plugin (hsz.mobi) | ||
### JetBrains template | ||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm | ||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||
|
||
# User-specific stuff: | ||
.idea/workspace.xml | ||
.idea/tasks.xml | ||
.idea/dictionaries | ||
.idea/vcs.xml | ||
.idea/jsLibraryMappings.xml | ||
|
||
# Sensitive or high-churn files: | ||
.idea/dataSources.ids | ||
.idea/dataSources.xml | ||
.idea/dataSources.local.xml | ||
.idea/sqlDataSources.xml | ||
.idea/dynamic.xml | ||
.idea/uiDesigner.xml | ||
|
||
# Gradle: | ||
.idea/gradle.xml | ||
.idea/libraries | ||
|
||
# Mongo Explorer plugin: | ||
.idea/mongoSettings.xml | ||
|
||
## File-based project format: | ||
*.iws | ||
|
||
## Plugin-specific files: | ||
|
||
# IntelliJ | ||
/out/ | ||
|
||
# mpeltonen/sbt-idea plugin | ||
.idea_modules/ | ||
|
||
# JIRA plugin | ||
atlassian-ide-plugin.xml | ||
|
||
# Crashlytics plugin (for Android Studio and IntelliJ) | ||
com_crashlytics_export_strings.xml | ||
crashlytics.properties | ||
crashlytics-build.properties | ||
fabric.properties | ||
### Scala template | ||
*.class | ||
*.log | ||
|
||
# sbt specific | ||
.cache | ||
target/.history | ||
.lib/ | ||
dist/* | ||
target/ | ||
lib_managed/ | ||
src_managed/ | ||
project/boot/ | ||
project/plugins/project/ | ||
|
||
# Scala-IDE specific | ||
.scala_dependencies | ||
.worksheet | ||
### SBT template | ||
# Simple Build Tool | ||
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control | ||
|
||
target/ | ||
lib_managed/ | ||
src_managed/ | ||
project/boot/ | ||
.history | ||
.cache | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name := "rsaex" | ||
|
||
version := "1.0" | ||
|
||
scalaVersion := "2.11.8" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version = 0.13.8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
logLevel := Level.Warn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import scala.collection.mutable.ArrayBuffer | ||
import scala.util.Random | ||
|
||
object MathHelper { | ||
/** | ||
* Find the multiplicative inverse of a number a over the integers modulo n | ||
* See: | ||
* https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers | ||
* https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Code | ||
*/ | ||
def modularInverse(a: Int, n: Int): Option[Int] = { | ||
var t = 0 | ||
var newT = 1 | ||
var r = n | ||
var newR = a % n | ||
|
||
while (newR != 0) { | ||
val quotient = r / newR | ||
|
||
val thisT = t | ||
t = newT | ||
newT = thisT - quotient * newT | ||
|
||
val thisR = r | ||
r = newR | ||
newR = thisR - quotient * newR | ||
} | ||
|
||
if (r > 1) return None // a is not invertible | ||
|
||
if (t < 0) t += n | ||
|
||
Some(t) | ||
} | ||
|
||
// Euler's totient for n = pq, primes p and q | ||
// https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation | ||
// https://en.wikipedia.org/wiki/Euler%27s_totient_function | ||
def eulerTotient(p: Int, q: Int) = (p - 1) * (q - 1) | ||
|
||
// These algorithms are not guaranteed to succeed | ||
def generatePrimes(num: Int, max: Int): Seq[Int] = { | ||
val primes = new ArrayBuffer[Int](num) | ||
|
||
for (i <- 1 to num) { | ||
var testPrime = 0 | ||
|
||
do { | ||
testPrime = Random.nextInt(max) | ||
} while (!isPrime(testPrime)) | ||
|
||
primes += testPrime | ||
} | ||
|
||
primes | ||
} | ||
def generatePrime(max: Int) = generatePrimes(1, max).head | ||
|
||
def isPrime(n: Int): Boolean = { | ||
if (n < 2) return false | ||
|
||
if (n == 2) return true | ||
|
||
if (n % 2 == 0) return false | ||
|
||
for (i <- 3 to ceilRoot(n) by 2) if (n % i == 0) return false | ||
|
||
true | ||
} | ||
|
||
def ceilRoot(n: Int): Int = Math.ceil(Math.sqrt(n.toDouble)).toInt | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import scala.collection.mutable.ListBuffer | ||
|
||
trait RsaBreaker { | ||
def recoverKey(publicKey: RsaPublicKey): RsaKey | ||
def bruteForceDecrypt(m: String, publicKey: RsaPublicKey): String | ||
} | ||
|
||
object RsaBreaker extends RsaBreaker { | ||
// todo: removeme: Suggest (mostly?) gutting, otherwise the challenge becomes much smaller (math understanding portion removed) | ||
// This presumes n = p * q | ||
override def recoverKey(publicKey: RsaPublicKey): RsaKey = { | ||
val factored = factor(publicKey.n) | ||
|
||
val p = factored.head._1 | ||
val q = factored.tail.head._1 | ||
|
||
val phiN = MathHelper.eulerTotient(p, q) | ||
|
||
val d = MathHelper.modularInverse(RsaCryptosystem.e, phiN).get | ||
|
||
RsaKey(publicKey, d, (p, q), phiN) | ||
|
||
/** | ||
* todo: removeme | ||
* | ||
* Alternate solution (cheating, less fun, does not demonstrate purpose of e): | ||
* | ||
* RsaCryptosystem.generateKey(p, q) | ||
*/ | ||
} | ||
|
||
// todo: removeme: Suggest leaving this and having kids use it; it's just a convenience method anyway | ||
override def bruteForceDecrypt(m: String, publicKey: RsaPublicKey): String = RsaCryptosystem.decrypt(m, recoverKey(publicKey)) | ||
|
||
// todo: removeme: Suggest entirely gutting. This is the crux of the RSA problem. | ||
private def factor(n: Int): Seq[(Int, Int)] = { | ||
val factors = for (i <- 2 until n if n % i == 0) yield i | ||
|
||
val res: ListBuffer[(Int, Int)] = ListBuffer() | ||
|
||
var x = n | ||
for (i <- factors) { | ||
var c = 0 | ||
|
||
while (x % i == 0) { | ||
c += 1 | ||
x /= i | ||
} | ||
|
||
if (c > 0) res += ((i, c)) | ||
} | ||
|
||
res | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* A real cryptosystem doesn't look like this. | ||
* This is not a real RSA implementation. | ||
* But it's sure fun anyway. :) | ||
*/ | ||
trait RsaCryptosystem { | ||
def generateKey(p: Int, q: Int): RsaKey | ||
def generateRandomKey: RsaKey | ||
|
||
def encrypt(m: Int, e: Int, n: Int): Int | ||
def decrypt(m: Int, d: Int, n: Int): Int | ||
|
||
def encrypt(m: Seq[Int], e: Int, n: Int): Seq[Int] | ||
def decrypt(m: Seq[Int], d: Int, n: Int): Seq[Int] | ||
|
||
def encrypt(m: String, e: Int, n: Int): String | ||
def decrypt(m: String, d: Int, n: Int): String | ||
|
||
def encrypt(m: String, key: RsaKey): String | ||
def decrypt(m: String, key: RsaKey): String | ||
} | ||
|
||
object RsaCryptosystem extends RsaCryptosystem { | ||
private final val messageStringJoinVal = " " | ||
|
||
// The best prime number between 16 and 18 | ||
final val e = 17 | ||
|
||
// To keep things sane | ||
final val primeMax = 256 | ||
|
||
// Generate an RSA key using given primes | ||
override def generateKey(p: Int, q: Int): RsaKey = { | ||
val n = p * q | ||
|
||
val phiN = MathHelper.eulerTotient(p, q) | ||
|
||
// Secret exponent d where e * d is congruent to 1 mod phi(n) | ||
// This could fail (on the .get if it's None) cause reasons if things aren't just so (rare but sometimes) | ||
val d = MathHelper.modularInverse(RsaCryptosystem.e, phiN).get | ||
|
||
RsaKey(RsaPublicKey(n, RsaCryptosystem.e), d, (p, q), phiN) | ||
} | ||
|
||
// Generate an RSA key using random primes | ||
override def generateRandomKey: RsaKey = { | ||
val primes = MathHelper.generatePrimes(2, RsaCryptosystem.primeMax) | ||
|
||
generateKey(primes.head, primes.tail.head) | ||
} | ||
|
||
// Encryption and decryption are the same! | ||
override def encrypt(m: Int, e: Int, n: Int): Int = BigInt(m).pow(e).mod(n).toInt | ||
override def decrypt(m: Int, d: Int, n: Int): Int = encrypt(m, d, n) | ||
|
||
override def encrypt(m: Seq[Int], e: Int, n: Int): Seq[Int] = for (x <- m) yield encrypt(x, e, n) | ||
override def decrypt(m: Seq[Int], d: Int, n: Int): Seq[Int] = for (x <- m) yield decrypt(x, d, n) | ||
|
||
override def encrypt(m: String, e: Int, n: Int): String = encrypt(m.toCharArray.map(_.toInt), e, n).mkString(messageStringJoinVal) | ||
override def decrypt(m: String, d: Int, n: Int): String = decrypt(m.split(messageStringJoinVal).map(_.toInt), d, n).map(_.toChar).mkString | ||
|
||
override def encrypt(m: String, key: RsaKey): String = encrypt(m, key.publicKey.e, key.publicKey.n) | ||
override def decrypt(m: String, key: RsaKey): String = decrypt(m, key.privateKey, key.publicKey.n) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
object RsaEx extends App { | ||
/** | ||
* RSA is a common public key encryption algorithm that is based on the idea that we can | ||
* multiply two really big prime numbers together easily, but if you give someone | ||
* the product of two large primes, it's hard to factor it apart again. | ||
* | ||
* We need to learn about modular arithmetic first. This is the arithmetic of a clock: | ||
* 23:00 (11 PM) + 7 hours = 30:00 = 06:00 (6 AM). Another way to say this is that | ||
* 30 is congruent to 6 (mod 24), or that 30 mod 24 equals 6, or in Scala: | ||
* 6 == 30 % 24 | ||
* | ||
* We'll be seeing a lot of numbers "mod" (%) another number. | ||
* | ||
* Here is how RSA works: | ||
* | ||
* Alice wants to send a message to Bob. | ||
* | ||
* So Bob picks two huge random prime numbers, p and q. He keeps these a secret. | ||
* | ||
* Bob multiplies them together to get n = p * q. n will be public. | ||
* | ||
* Bob computes phi(n) = (p - 1) * (q - 1) and keeps this as a secret. Phi(n) is called Euler's | ||
* totient function (read about it later!). | ||
* | ||
* Bob picks a value e between 1 and phi(n) such that phi(n) and e have no common factors; here Bob | ||
* chooses 17. e will be public. | ||
* | ||
* Bob calculates the secret key (which must be kept secret) d = e inverse mod phi(n) -- the secret | ||
* value that will undo his encryption, because e * d = 1 mod phi(n). | ||
* | ||
* Finally, Bob gives Alice (n, e) as the public key and keeps (d, n, e) as the private key for himself. | ||
* | ||
* Alice now takes her message X and encrypts the message as C = Math.pow(X, e) % n. She sends this to Bob. | ||
* | ||
* Bob decrypts the message as M back to | ||
* M = Math.pow(C, d) % n | ||
* = Math.pow(Math.pow(X, e), d) % n | ||
* = Math.pow(X, e * d) % n | ||
* = X % n | ||
* | ||
* In a real cryptosystem, p and q are chosen to be so big that it's hard to factor n. In a toy cryptosystem | ||
* like this one, we can use small primes to make things easier (possible). | ||
* | ||
*/ | ||
|
||
/** | ||
* Let's demonstrate RSA using a random key to get the hang of things. | ||
* This MIGHT fail. If it does, just run it again. | ||
* This code deliberately uses very small numbers. That's what makes it possible for us to break. | ||
*/ | ||
val randomKey = RsaCryptosystem.generateRandomKey | ||
Console.println(s"We randomly generated this key:\n$randomKey") | ||
|
||
val cleartext = "hello, hackers" | ||
Console.println(s"Cleartext: $cleartext") | ||
|
||
val ciphertext = RsaCryptosystem.encrypt(cleartext, randomKey) | ||
Console.println(s"Ciphertext: $ciphertext") | ||
|
||
val decryptedCleartext = RsaCryptosystem.decrypt(ciphertext, randomKey) | ||
Console.println(s"Decrypted back: $decryptedCleartext") | ||
|
||
/** | ||
* You have stolen the following ciphertext: | ||
* 63 12471 17384 19150 3861 17806 15090 5270 | ||
* | ||
* This was encrypted using an RSA key with the following public key: | ||
* n = 19153 | ||
* e = 17 | ||
* | ||
* Break the key and decrypt the ciphertext to get the flag | ||
*/ | ||
|
||
/** | ||
* todo: removeme: | ||
* Suggest removing these three lines or gutting severely; | ||
* another valid solution might not include RsaBreaker at all but just procedural code of same purpose | ||
* | ||
* Flag decrypts to: harmonic | ||
*/ | ||
val encryptedFlag = "63 12471 17384 19150 3861 17806 15090 5270" | ||
val decryptedFlag = RsaBreaker.bruteForceDecrypt(encryptedFlag, RsaPublicKey(n = 19153, e = 17)) | ||
Console.println(s"Decrypted to: $decryptedFlag") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
case class RsaPublicKey(n: Int, e: Int) | ||
case class RsaKey(publicKey: RsaPublicKey, privateKey: Int, primes: (Int, Int), eulerTotient: Int) { | ||
override def toString: String = | ||
s""" | ||
|---------- Public ---------- | ||
| n: ${publicKey.n} | ||
| e: ${publicKey.e} | ||
|---------- Private --------- | ||
| d: $privateKey | ||
| | ||
| p: ${primes._1} | ||
| q: ${primes._2} | ||
| | ||
|phi(n): $eulerTotient | ||
|---------------------------- | ||
""".stripMargin | ||
} |