summaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/ByteUtils.scala109
-rw-r--r--src/utils/ConversionUtils.scala38
-rw-r--r--src/utils/external/Base64.scala69
3 files changed, 216 insertions, 0 deletions
diff --git a/src/utils/ByteUtils.scala b/src/utils/ByteUtils.scala
new file mode 100644
index 0000000..344f6f7
--- /dev/null
+++ b/src/utils/ByteUtils.scala
@@ -0,0 +1,109 @@
+package ixee.cryptopals.utils
+
+object ByteUtils {
+ trait SizedNumeric[T] {
+ def manifest: Manifest[T]
+ def byteSize: Int
+ def bitSize: Int
+ def fromLong(l: Long): T
+ def toLong(l: T): Long
+ def buildFrom[U : SizedNumeric](from: U): T = fromLong(from.toLong)
+ def truncate[U : SizedNumeric](from: U): T = buildFrom(from)
+ override def toString = s"SizedNumeric[${manifest}]"
+ }
+
+ trait BitOps[T] {
+ def @>>>(t: T, x: Int): T
+ def @<<(t: T, x: Int): T
+ def @|(a: T, b: T): T
+ def @&(a: T, b: T): T
+ def @+(a: T, b: T): T
+ }
+
+ implicit val byteOps: BitOps[Byte] = new BitOps[Byte] {
+ def @>>>(t: Byte, x: Int): Byte = (t >>> x).toByte
+ def @<<(t: Byte, x: Int): Byte = (t << x).toByte
+ def @|(a: Byte, b: Byte): Byte = (a | b).toByte
+ def @&(a: Byte, b: Byte): Byte = (a & b).toByte
+ def @+(a: Byte, b: Byte): Byte = (a + b).toByte
+ }
+
+ implicit val intOps: BitOps[Int] = new BitOps[Int] {
+ def @>>>(t: Int, x: Int): Int = t >>> x
+ def @<<(t: Int, x: Int): Int = (t << x)
+ def @|(a: Int, b: Int): Int = a | b
+ def @&(a: Int, b: Int): Int = a & b
+ def @+(a: Int, b: Int): Int = a + b
+ }
+
+ implicit class WithBitOpts[T : BitOps](x: T) {
+ def @>>>(b: Int): T = implicitly[BitOps[T]].@>>>(x, b)
+ def @<<(b: Int): T = implicitly[BitOps[T]].@<<(x, b)
+ def @|(b: T): T = implicitly[BitOps[T]].@|(x, b)
+ def @&(b: T): T = implicitly[BitOps[T]].@&(x, b)
+ def @+(b: T): T = implicitly[BitOps[T]].@+(x, b)
+ }
+
+ implicit class SizedWithByteInfo[T : SizedNumeric](x: T) {
+ def byteSize = implicitly[SizedNumeric[T]].byteSize
+ def bitSize = implicitly[SizedNumeric[T]].bitSize
+ def toLong = implicitly[SizedNumeric[T]].toLong(x)
+ def liftedTo[U : SizedNumeric]: U = {
+ val uSize = implicitly[SizedNumeric[U]]
+ val tSize = implicitly[SizedNumeric[T]]
+ if (uSize.byteSize < tSize.byteSize) {
+ throw new RuntimeException(s"Target size ($uSize) is smaller than the source size ($tSize)")
+ }
+
+ uSize.buildFrom(x)
+ }
+ def truncatedTo[U : SizedNumeric]: U = {
+ implicitly[SizedNumeric[U]].truncate(x)
+ }
+ }
+
+ implicit class RichArrayOfBytes(b: Array[Byte]) {
+ def to[T : SizedNumeric : BitOps]: T = {
+ val numeric = implicitly[SizedNumeric[T]]
+ if(b.length < numeric.byteSize) {
+ throw new RuntimeException("Byte input is not long enough")
+ }
+
+ var out: Long = b(0)
+ for(i <- 1 until numeric.byteSize) {
+ out << 8
+ out = b(i) | out
+ }
+ numeric.fromLong(out)
+ }
+ }
+ def sizedNumeric[T : Manifest](bytes: Int)(from: Long => T)(to: T => Long) = new SizedNumeric[T] {
+ def manifest = implicitly[Manifest[T]]
+ def byteSize = bytes
+ def bitSize = bytes * 8
+ def toLong(t: T) = to(t)
+ def fromLong(l: Long) = from(l)
+ }
+
+ implicit val intSized = sizedNumeric(4) { x: Long => x.toInt } { _.toLong }
+ implicit val shortized = sizedNumeric(2) { x: Long => x.toShort } { _.toLong }
+ implicit val byteSized = sizedNumeric(1) { x: Long => x.toByte } { _.toLong }
+
+ def toArrayBuf[T : SizedNumeric : BitOps](x: T): Array[Byte] = {
+ val buf = new Array[Byte](x.byteSize)
+ for(i <- 0 until x.byteSize) {
+ val shiftAmount: Int = (x.byteSize - i - 1) << 3
+ buf(i) = (x @>>> shiftAmount).truncatedTo[Byte]
+ }
+ buf
+ }
+
+ def toBinaryString[T : SizedNumeric](x: T)(implicit a: BitOps[T]): String = {
+ val buf = new StringBuilder(x.bitSize)
+ for(i <- 0 until x.bitSize) {
+ val shiftAmount: Int = x.bitSize - i - 1
+ buf.append((x @>>> shiftAmount) @& 0x01.liftedTo[T])
+ }
+ buf.toString()
+ }
+}
diff --git a/src/utils/ConversionUtils.scala b/src/utils/ConversionUtils.scala
new file mode 100644
index 0000000..26f2d24
--- /dev/null
+++ b/src/utils/ConversionUtils.scala
@@ -0,0 +1,38 @@
+package ixee.cryptopals.utils
+
+import ByteUtils.WithBitOpts
+
+object ConversionUtils {
+ def hexStr2Bytes(s: String): Iterator[Byte] =
+ padToByte(s).grouped(2).map(byteStr2Byte)
+
+ def byteStr2Byte(str: String): Byte =
+ charSeq2Byte(str.toSeq)
+
+ def charSeq2Byte(seq: Seq[Char]): Byte =
+ seq.map(octet2nibble).reduce(joinNibbles)
+
+ def joinNibbles(a: Byte, b: Byte): Byte =
+ ((a @<< 4) @+ b)
+
+ def padToByte(s: String): String =
+ if (s.length % 2 != 0) "0" + s else s
+
+ def octet2nibble(c: Char): Byte = {
+ (c.toLower match {
+ case c if c >= 'a' && c <= 'f' =>
+ (c - 'a') + 10.toByte
+ case c if c >= '0' && c <= '9' =>
+ c - '0'
+ case _ =>
+ throw new IllegalArgumentException(s"Invalid hexadecimal character: $c")
+ }).toByte
+ }
+
+ def hexStr2Base64String(s: String): String = {
+ import io.github.marklister.base64.Base64._
+
+ hexStr2Bytes(s).toArray.toBase64
+ }
+}
+
diff --git a/src/utils/external/Base64.scala b/src/utils/external/Base64.scala
new file mode 100644
index 0000000..692abb8
--- /dev/null
+++ b/src/utils/external/Base64.scala
@@ -0,0 +1,69 @@
+package io.github.marklister.base64
+
+/**
+ * Base64 encoder
+ * @author Mark Lister
+ * (c) Mark Lister 2014
+ *
+ * I, the copyright holder of this work, release this work into the public domain.
+ * This applies worldwide. In some countries this may not be legally possible;
+ * if so: I grant anyone the right to use this work for any purpose, without any
+ * conditions, unless such conditions are required by law.
+ *
+ * The repo for this Base64 encoder lives at https://github.com/marklister/base64
+ * Please send your issues, suggestions and pull requests there.
+ *
+ */
+
+object Base64 {
+
+ class B64Scheme(val encodeTable: IndexedSeq[Char]) {
+ lazy val decodeTable = collection.immutable.TreeMap(encodeTable.zipWithIndex: _*)
+ }
+
+ lazy val base64 = new B64Scheme(('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9') ++ Seq('+', '/'))
+ lazy val base64Url = new B64Scheme(base64.encodeTable.dropRight(2) ++ Seq('-', '_'))
+
+ implicit class Encoder(b: Array[Byte]) {
+ private[this] val zero = Array(0, 0).map(_.toByte)
+ lazy val pad = (3 - b.length % 3) % 3
+
+ def toBase64(implicit scheme: B64Scheme = base64): String = {
+ def sixBits(x: Array[Byte]): Seq[Int] = {
+ val a = (x(0) & 0xfc) >> 2
+ val b = ((x(0) & 0x3) << 4) + ((x(1) & 0xf0) >> 4)
+ val c = ((x(1) & 0xf) << 2) + ((x(2) & 0xc0) >> 6)
+ val d = (x(2)) & 0x3f
+ Seq(a, b, c, d)
+ }
+ ((b ++ zero.take(pad)).grouped(3)
+ .flatMap(sixBits(_))
+ .map(x => scheme.encodeTable(x))
+ .toSeq
+ .dropRight(pad) :+ "=" * pad)
+ .mkString
+ }
+ }
+
+ implicit class Decoder(s: String) {
+ lazy val cleanS = s.reverse.dropWhile(_ == '=').reverse
+ lazy val pad = s.length - cleanS.length
+
+ def toByteArray(implicit scheme: B64Scheme = base64): Array[Byte] = {
+ def threeBytes(s: Seq[Char]): Array[Byte] = {
+ val r = s.map(scheme.decodeTable(_)).foldLeft(0)((a,b)=>(a << 6) +b)
+ java.nio.ByteBuffer.allocate(8).putLong(r).array().takeRight(3)
+ }
+ if (pad > 2 || s.length % 4 != 0) throw new java.lang.IllegalArgumentException("Invalid Base64 String:" + s)
+ if (!cleanS.forall(scheme.encodeTable.contains(_))) throw new java.lang.IllegalArgumentException("Invalid Base64 String:" + s)
+
+ (cleanS + "A" * pad)
+ .grouped(4)
+ .map(threeBytes(_))
+ .flatten
+ .toArray
+ .dropRight(pad)
+ }
+ }
+
+}