github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/cryptonight/cryptonightv7.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package cryptonight
    18  
    19  //import "fmt"
    20  import "unsafe"
    21  
    22  import "encoding/binary"
    23  import "github.com/aead/skein"
    24  import "github.com/dchest/blake256"
    25  
    26  // see this commit https://github.com/monero-project/monero/commit/e136bc6b8a480426f7565b721ca2ccf75547af62#diff-7000dc02c792439471da62856f839d62
    27  func cryptonightv7(input []byte, variant int) []byte {
    28  
    29  	var dummy [256]byte
    30  	var S [25]uint64
    31  
    32  	var key1 [64]uint32
    33  	var key2 [64]uint32
    34  
    35  	var a [2]uint64
    36  	var b [2]uint64
    37  	var c [2]uint64
    38  
    39  	nonce64 := VARIANT1_INIT64(variant, input) // init variant 1
    40  
    41  	a_uint32 := (*(*[4]uint32)(unsafe.Pointer(&a[0])))[:len(a)*2]
    42  	//b_uint32 := (*(*[4]uint32)(unsafe.Pointer(&b[0])))[:len(b)*2]
    43  	c_uint32 := (*(*[4]uint32)(unsafe.Pointer(&c[0])))[:len(c)*2]
    44  
    45  	// same array is accessed as 3 different ways, buts it bettter than copying
    46  	var ScratchPad = make([]uint64, 1<<18, 1<<18)
    47  	ScratchPad_uint32 := (*(*[MAX_ARRAY_LIMIT]uint32)(unsafe.Pointer(&ScratchPad[0])))[:len(ScratchPad)*2]
    48  	// ScratchPad_byte := (*(*[MAX_ARRAY_LIMIT]byte)(unsafe.Pointer(&ScratchPad[0])))[:len(ScratchPad)*8]
    49  
    50  	copy(dummy[:], input)
    51  
    52  	for i := 0; i < 16; i++ {
    53  		S[i] = binary.LittleEndian.Uint64(dummy[i<<3:])
    54  	}
    55  	S[16] = 0x8000000000000000
    56  
    57  	keccakf(&S)
    58  
    59  	// lets convert everything back to bytes
    60  	for i := 0; i < 25; i++ {
    61  		binary.LittleEndian.PutUint64(dummy[i<<3:], S[i])
    62  	}
    63  
    64  	// extract keys
    65  	/*for i := 0 ; i <8;i++{
    66  	    key1[i] = binary.LittleEndian.Uint32(dummy[i<<2:])
    67  	    key2[i] = binary.LittleEndian.Uint32(dummy[32+(i<<2):])
    68  	}*/
    69  
    70  	expandKeyGo(dummy[0:32], key1[:], nil)
    71  	expandKeyGo(dummy[32:64], key2[:], nil)
    72  
    73  	/* for i :=0; i< 60;i++{
    74  	         fmt.Printf("%2d %X\n", i, key2[i])
    75  	}*/
    76  
    77  	// var text [128]byte
    78  	var text_uint32 [32]uint32
    79  	//copy(text[:],dummy[64:64+128]) // copy 128 bytes
    80  
    81  	for i := 0; i < 32; i++ {
    82  		text_uint32[i] = binary.LittleEndian.Uint32(dummy[64+(i<<2):])
    83  	}
    84  
    85  	tweak_12 := binary.LittleEndian.Uint64(dummy[192:]) ^ nonce64 // v7 tweak
    86  
    87  	/* for i :=0; i< 32;i++{
    88  	         fmt.Printf("%2d %X  i  %08X %08X\n", i, text_uint32[i] , key1[i],key1[i<<1])
    89  	}*/
    90  
    91  	for i := 0; i < 0x4000; i++ {
    92  		for j := 0; j < 8; j++ {
    93  			CNAESTransform((text_uint32[(j << 2):]), key1[:])
    94  		}
    95  
    96  		//memcpy(CNCtx.Scratchpad + (i << 4), text, 128);
    97  		copy(ScratchPad_uint32[i*32:], text_uint32[:])
    98  	}
    99  
   100  	a[0] = S[0] ^ S[4]
   101  	a[1] = S[1] ^ S[5]
   102  	b[0] = S[2] ^ S[6]
   103  	b[1] = S[3] ^ S[7]
   104  
   105  	var variant1_scratch [16]byte
   106  	// the big slow round
   107  	for i := 0; i < 0x80000; i++ {
   108  		// {
   109  		c[0] = ScratchPad[((a[0]&0x1FFFF0)>>3)+0]
   110  		c[1] = ScratchPad[((a[0]&0x1FFFF0)>>3)+1]
   111  
   112  		CNAESRnd(c_uint32, a_uint32)
   113  
   114  		b[0] ^= c[0]
   115  		b[1] ^= c[1]
   116  
   117  		// TODO optimize it
   118  		// 1 patch comes here VARIANT1_1
   119  		if variant > 0 {
   120  			/// binary.LittleEndian.PutUint64(variant1_scratch[:],b[0])
   121  			binary.LittleEndian.PutUint64(variant1_scratch[8:], b[1])
   122  			/*    // unoptimisez original version
   123  			       * tmp := variant1_scratch[11]
   124  			      tmp1 := (tmp>>4)&1
   125  			      tmp2 := (tmp>>5)&1
   126  			      tmp3 := tmp1^tmp2;
   127  			      tmp0 := tmp3
   128  			      if (tmp & 1) == 1 {
   129  			          tmp0 = tmp3
   130  			      }else{
   131  			          tmp0 = ((((tmp2<<1)|tmp1) + 1)&3)
   132  			      }
   133  
   134  			      if (tmp & 1)  == 1{
   135  			          variant1_scratch[11] = ((tmp & 0xef) | (tmp0<<4))
   136  			      }else{
   137  			          variant1_scratch[11] = ((tmp & 0xcf) | (tmp0<<4))
   138  			      }
   139  			*/
   140  			tmp := variant1_scratch[11]
   141  			table := uint32(0x75310)
   142  
   143  			index := (((tmp >> 3) & 6) | (tmp & 1)) << 1
   144  			variant1_scratch[11] = tmp ^ (byte(table>>index) & 0x30)
   145  
   146  			//ScratchPad[((a[0]&0x1FFFF0)>>3)+0] = binary.LittleEndian.Uint64(variant1_scratch[:])
   147  			ScratchPad[((a[0]&0x1FFFF0)>>3)+0] = b[0]
   148  			ScratchPad[((a[0]&0x1FFFF0)>>3)+1] = binary.LittleEndian.Uint64(variant1_scratch[8:])
   149  
   150  		} else {
   151  
   152  			ScratchPad[((a[0]&0x1FFFF0)>>3)+0] = b[0]
   153  			ScratchPad[((a[0]&0x1FFFF0)>>3)+1] = b[1]
   154  		}
   155  
   156  		b[0] = ScratchPad[((c[0]&0x1FFFF0)>>3)+0]
   157  		b[1] = ScratchPad[((c[0]&0x1FFFF0)>>3)+1]
   158  
   159  		// time to do 64 bit * 64 bit multiply
   160  		var lower64, upper64 uint64
   161  		{
   162  
   163  			x := c[0]
   164  			y := b[0]
   165  
   166  			a := x >> 32
   167  			b := x & 0xffffffff
   168  			c := y >> 32
   169  			d := y & 0xffffffff
   170  
   171  			ac := a * c
   172  			bc := b * c
   173  			ad := a * d
   174  			bd := b * d
   175  
   176  			mid34 := (bd >> 32) + (bc & 0xffffffff) + (ad & 0xffffffff)
   177  
   178  			upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32)
   179  			lower64 = (mid34 << 32) | (bd & 0xffffffff)
   180  			_ = lower64
   181  			_ = upper64
   182  
   183  		}
   184  
   185  		a[1] += lower64
   186  		a[0] += upper64
   187  
   188  		ScratchPad[((c[0]&0x1FFFF0)>>3)+0] = a[0]
   189  		ScratchPad[((c[0]&0x1FFFF0)>>3)+1] = a[1]
   190  
   191  		// v2 patch comes here  VARIANT1_2
   192  		if variant > 0 {
   193  			// 3rd uint32 ( based on pointer) needs to be xored with nonce
   194  			//ScratchPad[((c[0]&0x1FFFF0)>>3)+1] = (a[1] ^ nonce64)&0xffffffff| (a[1]>>32)<<32 // use last 4 bytes
   195  			//_ = nonce64
   196  			ScratchPad[((c[0]&0x1FFFF0)>>3)+1] = a[1] ^ tweak_12
   197  		}
   198  
   199  		a[0] ^= b[0]
   200  		a[1] ^= b[1]
   201  
   202  		b[0] = c[0]
   203  		b[1] = c[1]
   204  
   205  	}
   206  
   207  	// fmt.Printf(" a %X %X\n", a[0],a[1]);
   208  	// fmt.Printf(" b %X %X\n", b[0],b[1]);
   209  
   210  	for i := 0; i < 32; i++ {
   211  		text_uint32[i] = binary.LittleEndian.Uint32(dummy[64+(i<<2):])
   212  	}
   213  
   214  	for i := 0; i < 0x4000; i++ {
   215  
   216  		for j := 0; j < 32; j++ {
   217  			text_uint32[j] ^= ScratchPad_uint32[(i*32)+j]
   218  
   219  		}
   220  		for j := 0; j < 8; j++ {
   221  			CNAESTransform((text_uint32[(j << 2):]), key2[:])
   222  		}
   223  
   224  	}
   225  
   226  	/*for i :=0; i< 32;i++{
   227  	         fmt.Printf("%2d %X\n", i, text_uint32[i])
   228  	}*/
   229  
   230  	for i := 0; i < 32; i++ {
   231  		binary.LittleEndian.PutUint32(dummy[64+(i<<2):], text_uint32[i])
   232  	}
   233  
   234  	for i := 8; i < 25; i++ {
   235  		S[i] = binary.LittleEndian.Uint64(dummy[i<<3:])
   236  	}
   237  
   238  	keccakf(&S) // do the keccak round
   239  	/* for i :=0; i< 25;i++{
   240  	         fmt.Printf("S %02d %X\n", i, S[i])
   241  	}*/
   242  
   243  	// lets convert everything back to bytes
   244  	for i := 0; i < 25; i++ {
   245  		binary.LittleEndian.PutUint64(dummy[i<<3:], S[i])
   246  	}
   247  
   248  	var resulthash []byte
   249  
   250  	switch S[0] & 3 {
   251  
   252  	case 0:
   253  		// fmt.Printf("blake\n")
   254  
   255  		// blake
   256  		blakehash := blake256.New()
   257  		blakehash.Write(dummy[:200])
   258  		resulthash = blakehash.Sum(nil) //matching
   259  
   260  	case 1: // groestl
   261  		// fmt.Printf("groestl not implemented\n")
   262  		var output [32]byte
   263  		var input = dummy[:200]
   264  		crypto_hash(output[:], input, uint64(len(input)))
   265  		resulthash = output[:]
   266  	case 2: // jh
   267  		//fmt.Printf("jh not implemented\n")
   268  
   269  		myjhash := NewJhash256()
   270  		myjhash.Write(dummy[:200])
   271  		resulthash = myjhash.Sum(nil)
   272  
   273  	case 3: // skein
   274  		//  fmt.Printf("skein\n")
   275  		skeinhash := skein.New256(nil)
   276  		skeinhash.Write(dummy[:200])
   277  		resulthash = skeinhash.Sum(nil) //matchin
   278  
   279  	}
   280  
   281  	//fmt.Printf("result hash %x\n", resulthash)
   282  
   283  	return resulthash
   284  
   285  }
   286  
   287  func SlowHashv7(msg []byte) []byte {
   288  
   289  	hash := cryptonightv7(append(msg, byte(0x01)), 1)
   290  	// hash := cryptonight(msg)
   291  	return hash
   292  
   293  }
   294  
   295  func VARIANT1_INIT64(variant int, data []byte) (nonce uint64) {
   296  	if variant > 0 && len(data) < 43 {
   297  		panic("Cryptonight variants need at least 43 bytes of data")
   298  	}
   299  
   300  	if variant > 0 {
   301  		nonce = binary.LittleEndian.Uint64(data[35:])
   302  	}
   303  
   304  	return
   305  }