// Copyright 2010 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 blowfish

// getNextWord returns the next big-endian uint32 value from the byte slice
// at the given position in a circular manner, updating the position.
func getNextWord( []byte,  *int) uint32 {
	var  uint32
	 := *
	for  := 0;  < 4; ++ {
		 = <<8 | uint32([])
		++
		if  >= len() {
			 = 0
		}
	}
	* = 
	return 
}

// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
// pi and substitution tables for calls to Encrypt. This is used, primarily,
// by the bcrypt package to reuse the Blowfish key schedule during its
// set up. It's unlikely that you need to use this directly.
func ( []byte,  *Cipher) {
	 := 0
	for  := 0;  < 18; ++ {
		// Using inlined getNextWord for performance.
		var  uint32
		for  := 0;  < 4; ++ {
			 = <<8 | uint32([])
			++
			if  >= len() {
				 = 0
			}
		}
		.p[] ^= 
	}

	var ,  uint32
	for  := 0;  < 18;  += 2 {
		,  = encryptBlock(, , )
		.p[], .p[+1] = , 
	}

	for  := 0;  < 256;  += 2 {
		,  = encryptBlock(, , )
		.s0[], .s0[+1] = , 
	}
	for  := 0;  < 256;  += 2 {
		,  = encryptBlock(, , )
		.s1[], .s1[+1] = , 
	}
	for  := 0;  < 256;  += 2 {
		,  = encryptBlock(, , )
		.s2[], .s2[+1] = , 
	}
	for  := 0;  < 256;  += 2 {
		,  = encryptBlock(, , )
		.s3[], .s3[+1] = , 
	}
}

// This is similar to ExpandKey, but folds the salt during the key
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
// and specializing it here is useful.
func expandKeyWithSalt( []byte,  []byte,  *Cipher) {
	 := 0
	for  := 0;  < 18; ++ {
		.p[] ^= getNextWord(, &)
	}

	 = 0
	var ,  uint32
	for  := 0;  < 18;  += 2 {
		 ^= getNextWord(, &)
		 ^= getNextWord(, &)
		,  = encryptBlock(, , )
		.p[], .p[+1] = , 
	}

	for  := 0;  < 256;  += 2 {
		 ^= getNextWord(, &)
		 ^= getNextWord(, &)
		,  = encryptBlock(, , )
		.s0[], .s0[+1] = , 
	}

	for  := 0;  < 256;  += 2 {
		 ^= getNextWord(, &)
		 ^= getNextWord(, &)
		,  = encryptBlock(, , )
		.s1[], .s1[+1] = , 
	}

	for  := 0;  < 256;  += 2 {
		 ^= getNextWord(, &)
		 ^= getNextWord(, &)
		,  = encryptBlock(, , )
		.s2[], .s2[+1] = , 
	}

	for  := 0;  < 256;  += 2 {
		 ^= getNextWord(, &)
		 ^= getNextWord(, &)
		,  = encryptBlock(, , )
		.s3[], .s3[+1] = , 
	}
}

func encryptBlock(,  uint32,  *Cipher) (uint32, uint32) {
	,  := , 
	 ^= .p[0]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[1]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[2]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[3]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[4]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[5]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[6]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[7]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[8]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[9]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[10]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[11]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[12]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[13]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[14]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[15]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[16]
	 ^= .p[17]
	return , 
}

func decryptBlock(,  uint32,  *Cipher) (uint32, uint32) {
	,  := , 
	 ^= .p[17]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[16]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[15]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[14]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[13]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[12]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[11]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[10]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[9]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[8]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[7]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[6]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[5]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[4]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[3]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[2]
	 ^= ((.s0[byte(>>24)] + .s1[byte(>>16)]) ^ .s2[byte(>>8)]) + .s3[byte()] ^ .p[1]
	 ^= .p[0]
	return , 
}