All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sigmastate.crypto.GF2_192.scala Maven / Gradle / Ivy

The newest version!
/*
 By Leonid Reyzin

 This is free and unencumbered software released into the public domain.

 Anyone is free to copy, modify, publish, use, compile, sell, or
 distribute this software, either in source code form or as a compiled
 binary, for any purpose, commercial or non-commercial, and by any
 means.

 In jurisdictions that recognize copyright laws, the author or authors
 of this software dedicate any and all copyright interest in the
 software to the public domain. We make this dedication for the benefit
 of the public at large and to the detriment of our heirs and
 successors. We intend this dedication to be an overt act of
 relinquishment in perpetuity of all present and future rights to this
 software under copyright law.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.

 For more information, please refer to 
 */
package sigmastate.crypto

import debox.cfor

import java.util

class GF2_192 extends AnyRef {
  private[crypto] val word: Array[Long] = new Array[Long](3)

  /**
    * returns a copy of the field element
    *
    * @param that element to copy
    */
  def this(that: GF2_192) {
    this()
    this.word(0) = that.word(0)
    this.word(1) = that.word(1)
    this.word(2) = that.word(2)
  }

  /**
    * returns the field element whose 32 least significant bits are bits of that and rest are 0
    *
    * @param that lower 32 bits
    */
  def this(that: Int) {
    this()
    this.word(0) = that.toLong & 0xFFFFFFFFL
  }

  /**
    * returns the field element whose bits are given by the long array
    *
    * @param that must be length 3
    */
  def this(that: Array[Long]) {
    this()
    require(that.length == 3)
    this.word(0) = that(0)
    this.word(1) = that(1)
    this.word(2) = that(2)
  }

  /**
    * returns the field element whose bits are given by the byte array that[pos]...that[pos+23]
    *
    * @param that must be length at least pos+24
    */
  def this(that: Array[Byte], pos: Int) {
    this()
    assert(that.length >= pos + 24)
    cfor(0)(_ < 8, _ + 1) { i =>
      word(0) |= (that(i + pos).toLong & 0xFF) << (i << 3)
    }
    cfor(0)(_ < 8, _ + 1) { i =>
      word(1) |= (that(i + pos + 8).toLong & 0xFF) << (i << 3)
    }
    cfor(0)(_ < 8, _ + 1) { i =>
      word(2) |= (that(i + pos + 16).toLong & 0xFF) << (i << 3)
    }
  }

    /**
    * returns the field element whose bits are given by the byte array that
    *
    * @param that must be length 24
    */
  def this(that: Array[Byte]) {
    this(that, 0)
  }


  /**
    *
    * @param obj the field element with which to compare
    * @return true if and only if this and that represent the same field element
    */
  override def equals(obj: Any): Boolean = {
    if (this eq obj.asInstanceOf[AnyRef]) return true // equal references
    obj match {
      case that: GF2_192 =>
        this.word(0) == that.word(0) && this.word(1) == that.word(1) && this.word(2) == that.word(2)
      case _ => false
    }
  }

  override def hashCode = java.util.Arrays.hashCode(word)

  /**
    *
    * @return long array of length 3 containing the three words of the field element
    */
  def toLongArray: Array[Long] = {
    val ret = new Array[Long](3)
    ret(0) = word(0)
    ret(1) = word(1)
    ret(2) = word(2)
    ret
  }

  /**
    *
    * @return byte array of length 24 containing the two words of the field element
    */
  def toByteArray: Array[Byte] = {
    val ret = new Array[Byte](24)
    toByteArray(ret, 0)
    ret
  }

  /**
    * @param ret bytes of the field element will go into ret[pos]...ret[pos+23]
    */
  def toByteArray(ret: Array[Byte], pos: Int): Unit = {
    assert(ret.length >= pos + 24)
    cfor (0)(_ < 3, _ + 1) { j =>
      cfor(0)(_ < 8, _ + 1) { i =>
        ret(pos + i + 8 * j) = ((word(j) >> (i << 3)) & 0xFF).toByte
      }
    }
  }

  /**
    *
    * @return true if this == 0, false otherwise
    */
  def isZero: Boolean = word(0) == 0L && word(1) == 0L && word(2) == 0L

  /**
    *
    * @return true if this == 1, false otherwise
    */
  def isOne: Boolean = word(0) == 1L && word(1) == 0L && word(2) == 0L

  //The tables above were generated by the code below. The code is no longer needed.
  /* *******************************************************************************************


      static long [][] powTable0 = new long [7] [];
      static long [][] powTable1 = new long [7] [];
      static long [][] powTable2 = new long [7] [];

      public static void genPowTable() {
          GF2_192 z = new GF2_192();
          int i;

          powTable0[0] = new long [192];
          powTable1[0] = new long [192];
          powTable2[0] = new long [192];
          i = 0;
          for (; i<64; i++)  {
              z.word[0] = 1L<= 0, _ - 1) { i =>
      w3 = w2 >>> 63
      w2 = (w2 << 1) | (w1 >>> 63)
      w1 = (w1 << 1) | (w0 >>> 63)
      w0 <<= 1
      val t: Long = (b >>> i) & 1
      w2 ^= a.word(2) * t
      w1 ^= a.word(1) * t
      w0 ^= (a.word(0) * t) ^ (irredPentanomial * w3) // mod reduce
    }
    res.word(0) = w0
    res.word(1) = w1
    res.word(2) = w2
  }

  /**
    * Computes a plus b and puts the result into res.
    *
    * @param res output; must be not null; may be equal to a and/or b
    * @param a   multiplicand; may be equal to res, in which case will get overwritten
    * @param b   multiplier; may be equal to res, in which case will get overwritten
    */
  def add(res: GF2_192, a: GF2_192, b: GF2_192): Unit = {
    res.word(0) = a.word(0) ^ b.word(0)
    res.word(1) = a.word(1) ^ b.word(1)
    res.word(2) = a.word(2) ^ b.word(2)
  }

  /**
    * Computes a times b and puts the result into res.
    * Uses table lookups, which may not preserve
    * the secrecy of the inputs in case of side-channel attacks.
    *
    * @param res output; must be not null; may be equal to a and/or b
    * @param a   multiplicand; may be equal to res, in which case will get overwritten
    * @param b   multiplier; may be equal to res, in which case will get overwritten
    */
  def mul(res: GF2_192,
          a: GF2_192,
          b: GF2_192): Unit = {
    // Implements a sort of times-x-and-add algorithm, except instead of multiplying by x
    // we multiply by x^4 and then add one of possible 16 precomputed values
    // contains a*0, a*1, a*x, a*(x+1), a*x^2, a*(x^2+1), a*(x^2+x), a*(x^2+x+1)
    // a*x^3, a*(x^3+1), a*(x^3+x), a*(x^3+x+1), a*(x^3+x^2), a*(x^3+x^2+1), a*(x^3+x^2+x), a*(x^3+x^2+x+1), all mod reduced
    // First word of each is in a0 muls, second word of each is in a1muls, third word of each is in a2muls
    val a0muls = new Array[Long](16)
    val a1muls = new Array[Long](16)
    val a2muls = new Array[Long](16)

    // a0muls[0], a1muls[0] and a2muls[0] are already correctly initialized to 0
    a0muls(1) = a.word(0)
    a1muls(1) = a.word(1)
    a2muls(1) = a.word(2)
    // a*x, a*x^2, a*x^3
    var i = 2
    while (i <= 8) {
      // multiply a*x^{log_2 i/2} by x to get a*x^{log_2 i}
      val prev = i / 2
      a0muls(i) = a0muls(prev) << 1
      a1muls(i) = (a1muls(prev) << 1) | (a0muls(prev) >>> 63)
      a2muls(i) = (a2muls(prev) << 1) | (a1muls(prev) >>> 63)
      // mod reduce
      a0muls(i) ^= irredMuls((a2muls(prev) >>> 63).toInt)
      i *= 2
    }

    // a*(x+1)
    a0muls(3) = a0muls(1) ^ a0muls(2)
    a1muls(3) = a1muls(1) ^ a1muls(2)
    a2muls(3) = a2muls(1) ^ a2muls(2)

    // a*(x^2+1), a*(x^2+x), a*(x^2+x+1)
    cfor(1)(_ < 4, _ + 1) { i =>
      a0muls(4 | i) = a0muls(4) ^ a0muls(i)
      a1muls(4 | i) = a1muls(4) ^ a1muls(i)
      a2muls(4 | i) = a2muls(4) ^ a2muls(i)
    }

    // a*(x^3+1), a*(x^3+x), a*(x^3+x+1), a*(x^3+x^2), a*(x^3+x^2+1), a*(x^3+x^2+x), a*(x^3+x^2+x+1)
    cfor(1)(_ < 8, _ + 1) { i =>
      a0muls(8 | i) = a0muls(8) ^ a0muls(i)
      a1muls(8 | i) = a1muls(8) ^ a1muls(i)
      a2muls(8 | i) = a2muls(8) ^ a2muls(i)
    }
    var w0: Long = 0
    var w1: Long = 0
    var w2: Long = 0
    cfor(2)(_ >= 0, _ - 1) { j =>
      val multiplier = b.word(j)
      var i = 60
      while (i >= 0) {
        // Multiply by x^4
        val modReduceIndex = (w2 >>> 60).toInt
        w2 = (w2 << 4) | (w1 >>> 60)
        w1 = (w1 << 4) | (w0 >>> 60)
        // MOD REDUCE ACCORDING TO modReduceIndex by XORing the right value
        w0 = (w0 << 4) ^ irredMuls(modReduceIndex)
        //w0 = (w0<<4)^(irredPentanomial*(modReduceIndex&8))^(irredPentanomial*(modReduceIndex&4))^(irredPentanomial*(modReduceIndex&2))^(irredPentanomial*(modReduceIndex&1));
        // Add the correct multiple of a
        val index = ((multiplier >>> i) & 15).toInt
        w0 ^= a0muls(index)
        w1 ^= a1muls(index)
        w2 ^= a2muls(index)
        i -= 4
      }
    }
    res.word(0) = w0
    res.word(1) = w1
    res.word(2) = w2
  }

  def invert(res: GF2_192,
             z: GF2_192): Unit = {
    // Computes z^{2^192-2} = z^{exponent written in binary as 191 ones followed by a single zero}
    // (by Fermat's little theorem, this is the correct inverse)
    // contains z raised to the power whose binary representation is 2^k ones
    val zTo2ToK1s = new GF2_192(z)

    // Square res to get its exponent to be 10 in binary
    mul(res, z, z)
    // contains z raised to the power whose binary representation is 2^k ones followed by 2^k zeros
    val zTo2ToK1s2ToK0s = new GF2_192(res)
    // Loop invariant
    // res contains z raised to the power whose binary representation is 2^{k+1}-1 ones followed by a single zero
    // zTo2ToK1s contains z raised to the power whose binary representation is 2^k ones
    // zTo2ToK1s2ToK0s contains z raised to the power whose binary representation is 2^k ones followed by 2^k zeros
    var k = 0
    while (k < 6) {
      k += 1
      // Fill in the zeros in the exponent of zTo2ToK1s2ToK0s with ones
      mul(zTo2ToK1s, zTo2ToK1s2ToK0s, zTo2ToK1s)
      // zTo2ToK1s2ToK0s = power2To2ToK with 2^k zeros appended to the exponent
      power2To2ToK(zTo2ToK1s2ToK0s, zTo2ToK1s, k)
      // prepend 2^k ones to res
      mul(res, res, zTo2ToK1s2ToK0s)
    }
    // Prepened another 64 ones to res
    power2To2ToK(zTo2ToK1s2ToK0s, zTo2ToK1s2ToK0s, k)
    mul(res, res, zTo2ToK1s2ToK0s)
  }

  /**
    * Squares z and puts the result into res. Same as power2To2ToK(res, z, 0).
    * About same efficiency as mul(res, z, z) (more efficient implementations are possible,
    * but not provided here because of risk of side-channel attacks)
    *
    * @param res output; must be not null; may be equal to z
    * @param z   input to be squared; may be equal to res, in which case will get overwritten
    */
  def sqr(res: GF2_192,
          z: GF2_192): Unit = {
    // It is possible to precompute the square of every byte value in every byte position
    // (only bytes a few byte positions are needed -- squares of bytes in other positions
    // can be figured out by shifting squares of bytes in positions 0 and 16). This will result in a slightly
    // bigger table and several times more efficient squaring. However, because table lookups will
    // be input-dependent, this gives a higher risk of side-channel attacks that reveal z.
    // Hence, it's not implemented.
    //
    power2To2ToK(res, z, 0)
  }

  /**
    * Raises z to the power 2^{2^k} and puts the result into res. Same sqr(z, z) 2^k times.
    * Takes only about as much time as mul(res, z, z) (even more efficient implementations are possible,
    * but not provided here because of risk of side-channel attacks)
    * @param res output; must be not null; may be equal to z
    * @param z   input to be squared; may be equal to res, in which case will get overwritten
    */
  def power2To2ToK(res: GF2_192, z: GF2_192, k: Int): Unit = {
    if (k >= 7) {
      // By Fermat's little theorem, z^{2^{2^k}} = z^{2^{2^k} mod (2^{192}-1)}
      // If k>=7, then 2^{2^k} mod (2^{192}-1) = 2^64 when k is even and 2^128 when k is odd (proof below),
      // so that's what we compute.
      // Note that we have no precomputed table for k=7 (i.e., 2^128), because we don't expect
      // this to be needed -- only up to k=6 is used in inversion.
      // Here's the proof: let m = 64. 2^{2^k} mod (2^{192}-1) = 2^{mn} mod (2^{3m}-1) for n = 2^{k-6}.
      // Let d = n div 3 and r = n mod 3.
      // Then 2^{mn} = (2^{3m}-1) (2^{m(n-3}}+2^{m(n-6)}+...+2^{m-nd})+2^{nr}
      // So the remainder is 2^{nr}. r is 2 when k is odd and 1 when k is even.
      power2To2ToK(res, z, 6)
      if (k % 2 == 1) {
        power2To2ToK(res, res, 6)
      }
    } else {
      // powTable0[k][i] contains the result of raising x^i to the power 2^k for i = 0...63
      // powTable0[k][i-64] contains the result of raising x^i to the power 2^k for i = 64...127
      // powTable0[k][i-128] contains the result of raising x^i to the power 2^k for i = 128...191
      // Because raising to the power 2^k is linear over any field of characteristic 2,
      // we just need to XOR the values in these tables at indices i where z is 1.
      // This selection is done via multiplication by 0 or 1, to avoid having an input-dependent path
      // through the code, thus reducing the chance of side-channel attacks.
      //
      // Note that more efficient tables can be precomputed -- for example, the result of raising
      // every one of 16 possible 4-bit nibbles at every one of 32 possible nibble positions.
      // But indexing into these tables will be input-dependent, which may make side-channel attacks easier.
      var t0: Long = 0
      var t1: Long = 0
      var t2: Long = 0
      var maxIndex = 0
      var i = 0
      cfor(0)(_ < z.word.length, _ + 1) { iW =>
        var w = z.word(iW)
        maxIndex += 64
        while (i < maxIndex) {
          val multiplier: Long = w & 1
          // No "if w&1 == 0" here, to avoid a data-dependent path through the code,
          // thus reducing the chance of side channel attacks
          t0 ^= powTable0(k)(i) * multiplier
          t1 ^= powTable1(k)(i) * multiplier
          t2 ^= powTable2(k)(i) * multiplier
          w >>>= 1
          i += 1
        }
      }
      res.word(0) = t0
      res.word(1) = t1
      res.word(2) = t2
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy