Task

Find, through brute force, the five-letter passwords corresponding with the following SHA-256 hashes:

1. 1115dd800feaacefdf481f1f9070374a2a81e27880f187396db67958b207cbad
2. 3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b
3. 74e1bb62f8dabb8125a58852b63bdf6eaef667cb56ac7f7cdba6d7305c50a22f

Your program should naively iterate through all possible passwords consisting only of five lower-case ASCII English letters. It should use concurrent or parallel processing, if your language supports that feature. You may calculate SHA-256 hashes by calling a library or through a custom implementation. Print each matching password, along with its SHA-256 hash.

Related task: SHA-256

import java.security.MessageDigest
 
import scala.collection.parallel.immutable.ParVector
 
object EncryptionCracker {
  def main(args: Array[String]): Unit = {
    val hash1 = "1115dd800feaacefdf481f1f9070374a2a81e27880f187396db67958b207cbad"
    val hash2 = "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b"
    val hash3 = "74e1bb62f8dabb8125a58852b63bdf6eaef667cb56ac7f7cdba6d7305c50a22f"
 
    val charSet = ('a' to 'z').toVector
    val num = 5
 
    for(tmp <- List(hash1, hash2, hash3)){
      println(tmp)
      crack(tmp, charSet, num) match{
        case Some(s) => println(s"String: $s\n")
        case None => println("Failed\n")
      }
    }
  }
 
  def crack(hash: String, charSet: Vector[Char], num: Int): Option[String] = {
    val perms = charSet
      .flatMap(c => Vector.fill(num)(c)).combinations(num)  //Generate distinct sets of letters
      .to(ParVector)                                        //Convert to ParVector
      .flatMap(_.permutations.map(_.mkString))              //Finish generating candidates
 
    perms.find(str => getHash(str).equalsIgnoreCase(hash))  //Search for a matching string
  }
 
  def getHash(str: String): String = {
    val digester = MessageDigest.getInstance("SHA-256")
    digester.digest(str.getBytes("UTF-8")).map("%02x".format(_)).mkString
  }
}