// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package des

import (
	
	
)

func cryptBlock( []uint64, ,  []byte,  bool) {
	 := binary.BigEndian.Uint64()
	 = permuteInitialBlock()
	,  := uint32(>>32), uint32()

	 = ( << 1) | ( >> 31)
	 = ( << 1) | ( >> 31)

	if  {
		for  := 0;  < 8; ++ {
			,  = feistel(, , [15-2*], [15-(2*+1)])
		}
	} else {
		for  := 0;  < 8; ++ {
			,  = feistel(, , [2*], [2*+1])
		}
	}

	 = ( << 31) | ( >> 1)
	 = ( << 31) | ( >> 1)

	// switch left & right and perform final permutation
	 := (uint64() << 32) | uint64()
	binary.BigEndian.PutUint64(, permuteFinalBlock())
}

// Encrypt one block from src into dst, using the subkeys.
func encryptBlock( []uint64, ,  []byte) {
	cryptBlock(, , , false)
}

// Decrypt one block from src into dst, using the subkeys.
func decryptBlock( []uint64, ,  []byte) {
	cryptBlock(, , , true)
}

// DES Feistel function. feistelBox must be initialized via
// feistelBoxOnce.Do(initFeistelBox) first.
func feistel(,  uint32, ,  uint64) (,  uint32) {
	var  uint32

	 =  ^ uint32(>>32)
	 ^= feistelBox[7][&0x3f] ^
		feistelBox[5][(>>8)&0x3f] ^
		feistelBox[3][(>>16)&0x3f] ^
		feistelBox[1][(>>24)&0x3f]

	 = (( << 28) | ( >> 4)) ^ uint32()
	 ^= feistelBox[6][()&0x3f] ^
		feistelBox[4][(>>8)&0x3f] ^
		feistelBox[2][(>>16)&0x3f] ^
		feistelBox[0][(>>24)&0x3f]

	 =  ^ uint32(>>32)
	 ^= feistelBox[7][&0x3f] ^
		feistelBox[5][(>>8)&0x3f] ^
		feistelBox[3][(>>16)&0x3f] ^
		feistelBox[1][(>>24)&0x3f]

	 = (( << 28) | ( >> 4)) ^ uint32()
	 ^= feistelBox[6][()&0x3f] ^
		feistelBox[4][(>>8)&0x3f] ^
		feistelBox[2][(>>16)&0x3f] ^
		feistelBox[0][(>>24)&0x3f]

	return , 
}

// feistelBox[s][16*i+j] contains the output of permutationFunction
// for sBoxes[s][i][j] << 4*(7-s)
var feistelBox [8][64]uint32

var feistelBoxOnce sync.Once

// general purpose function to perform DES block permutations
func permuteBlock( uint64,  []uint8) ( uint64) {
	for ,  := range  {
		 := ( >> ) & 1
		 |=  << uint((len()-1)-)
	}
	return
}

func initFeistelBox() {
	for  := range sBoxes {
		for  := 0;  < 4; ++ {
			for  := 0;  < 16; ++ {
				 := uint64(sBoxes[][][]) << (4 * (7 - uint()))
				 = permuteBlock(, permutationFunction[:])

				// Row is determined by the 1st and 6th bit.
				// Column is the middle four bits.
				 := uint8((( & 2) << 4) | &1)
				 := uint8( << 1)
				 :=  | 

				// The rotation was performed in the feistel rounds, being factored out and now mixed into the feistelBox.
				 = ( << 1) | ( >> 31)

				feistelBox[][] = uint32()
			}
		}
	}
}

// permuteInitialBlock is equivalent to the permutation defined
// by initialPermutation.
func permuteInitialBlock( uint64) uint64 {
	// block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes)
	 :=  >> 48
	 :=  << 48
	 ^=  ^  ^ <<48 ^ >>48

	// block = b1 b0 b5 b4 b3 b2 b7 b6
	 =  >> 32 & 0xff00ff
	 = ( & 0xff00ff00)
	 ^= <<32 ^  ^ <<8 ^ <<24 // exchange b0 b4 with b3 b7

	// block is now b1 b3 b5 b7 b0 b2 b4 b6, the permutation:
	//                  ...  8
	//                  ... 24
	//                  ... 40
	//                  ... 56
	//  7  6  5  4  3  2  1  0
	// 23 22 21 20 19 18 17 16
	//                  ... 32
	//                  ... 48

	// exchange 4,5,6,7 with 32,33,34,35 etc.
	 =  & 0x0f0f00000f0f0000
	 =  & 0x0000f0f00000f0f0
	 ^=  ^  ^ >>12 ^ <<12

	// block is the permutation:
	//
	//   [+8]         [+40]
	//
	//  7  6  5  4
	// 23 22 21 20
	//  3  2  1  0
	// 19 18 17 16    [+32]

	// exchange 0,1,4,5 with 18,19,22,23
	 =  & 0x3300330033003300
	 =  & 0x00cc00cc00cc00cc
	 ^=  ^  ^ >>6 ^ <<6

	// block is the permutation:
	// 15 14
	// 13 12
	// 11 10
	//  9  8
	//  7  6
	//  5  4
	//  3  2
	//  1  0 [+16] [+32] [+64]

	// exchange 0,2,4,6 with 9,11,13,15:
	 =  & 0xaaaaaaaa55555555
	 ^=  ^ >>33 ^ <<33

	// block is the permutation:
	// 6 14 22 30 38 46 54 62
	// 4 12 20 28 36 44 52 60
	// 2 10 18 26 34 42 50 58
	// 0  8 16 24 32 40 48 56
	// 7 15 23 31 39 47 55 63
	// 5 13 21 29 37 45 53 61
	// 3 11 19 27 35 43 51 59
	// 1  9 17 25 33 41 49 57
	return 
}

// permuteInitialBlock is equivalent to the permutation defined
// by finalPermutation.
func permuteFinalBlock( uint64) uint64 {
	// Perform the same bit exchanges as permuteInitialBlock
	// but in reverse order.
	 :=  & 0xaaaaaaaa55555555
	 ^=  ^ >>33 ^ <<33

	 =  & 0x3300330033003300
	 :=  & 0x00cc00cc00cc00cc
	 ^=  ^  ^ >>6 ^ <<6

	 =  & 0x0f0f00000f0f0000
	 =  & 0x0000f0f00000f0f0
	 ^=  ^  ^ >>12 ^ <<12

	 =  >> 32 & 0xff00ff
	 = ( & 0xff00ff00)
	 ^= <<32 ^  ^ <<8 ^ <<24

	 =  >> 48
	 =  << 48
	 ^=  ^  ^ <<48 ^ >>48
	return 
}

// creates 16 28-bit blocks rotated according
// to the rotation schedule
func ksRotate( uint32) ( []uint32) {
	 = make([]uint32, 16)
	 := 
	for  := 0;  < 16; ++ {
		// 28-bit circular left shift
		 := ( << (4 + ksRotations[])) >> 4
		 := ( << 4) >> (32 - ksRotations[])
		[] =  | 
		 = []
	}
	return
}

// creates 16 56-bit subkeys from the original key
func ( *desCipher) ( []byte) {
	feistelBoxOnce.Do(initFeistelBox)

	// apply PC1 permutation to key
	 := binary.BigEndian.Uint64()
	 := permuteBlock(, permutedChoice1[:])

	// rotate halves of permuted key according to the rotation schedule
	 := ksRotate(uint32( >> 28))
	 := ksRotate(uint32(<<4) >> 4)

	// generate subkeys
	for  := 0;  < 16; ++ {
		// combine halves to form 56-bit input to PC2
		 := uint64([])<<28 | uint64([])
		// apply PC2 permutation to 7 byte input
		.subkeys[] = unpack(permuteBlock(, permutedChoice2[:]))
	}
}

// Expand 48-bit input to 64-bit, with each 6-bit block padded by extra two bits at the top.
// By doing so, we can have the input blocks (four bits each), and the key blocks (six bits each) well-aligned without
// extra shifts/rotations for alignments.
func unpack( uint64) uint64 {
	var  uint64

	 = ((>>(6*1))&0xff)<<(8*0) |
		((>>(6*3))&0xff)<<(8*1) |
		((>>(6*5))&0xff)<<(8*2) |
		((>>(6*7))&0xff)<<(8*3) |
		((>>(6*0))&0xff)<<(8*4) |
		((>>(6*2))&0xff)<<(8*5) |
		((>>(6*4))&0xff)<<(8*6) |
		((>>(6*6))&0xff)<<(8*7)

	return 
}