// Copyright (c) 2012 Damian Gryski <damian@gryski.com>
// Licensed under the GPLv3 or, at your option, any later version.

// Package skip32 implements the Skip32 blockcipher
/*

SKIP32 is a 32-bit block cipher based on SKIPJACK, written by Greg Rose of QUALCOMM Australia.

It is useful for obfuscating small integers (like sequential database ids)
that are exposed to prevent an analysis of growth rates as in:
https://en.wikipedia.org/wiki/German_tank_problem

This library deliberately exposes only the integer obfuscation routines and
hides the low-level encryption.

Using this routine to hide IDs is not sufficient security by itself.  Please
ensure your application does secondary validation and do not rely on the
secrecy of the generated IDs.

*/
package skip32

import (
	"errors"
	"strconv"
)

var ftable = [...]byte{
	0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
	0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
	0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
	0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
	0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
	0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
	0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
	0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
	0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
	0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
	0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
	0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
	0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
	0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
	0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
	0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46,
}

func g(key []byte, k int, w uint16) uint16 {

	g1 := byte((w >> 8) & 0xff)
	g2 := byte(w & 0xff)

	g3 := ftable[g2^key[(4*k+0)%10]] ^ g1
	g4 := ftable[g3^key[(4*k+1)%10]] ^ g2
	g5 := ftable[g4^key[(4*k+2)%10]] ^ g3
	g6 := ftable[g5^key[(4*k+3)%10]] ^ g4

	return (uint16(g5) << 8) + uint16(g6)
}

func crypt32(key []byte, buf []byte, encrypt bool) {
	var kstep, k int

	if encrypt {
		kstep = 1
		k = 0
	} else {
		kstep = -1
		k = 23
	}

	/* pack into words */
	wl := (uint16(buf[0]) << 8) + uint16(buf[1])
	wr := (uint16(buf[2]) << 8) + uint16(buf[3])

	/* 24 feistel rounds, doubled up */
	for i := 0; i < 24/2; i++ {
		wr ^= g(key, k, wl) ^ uint16(k)
		k += kstep
		wl ^= g(key, k, wr) ^ uint16(k)
		k += kstep
	}

	/* implicitly swap halves while unpacking */
	buf[0] = byte(wr >> 8)
	buf[1] = byte(wr & 0xFF)
	buf[2] = byte(wl >> 8)
	buf[3] = byte(wl & 0xFF)
}

// Skip32 is a 32-bit integer obfuscator
type Skip32 struct {
	key []byte
}

// New returns a new Skip32 obfuscator.  key must be 10 bytes (80 bits).
func New(key []byte) (*Skip32, error) {

	if l := len(key); l != 10 {
		return nil, errors.New("Invalid key size: " + strconv.Itoa(l))
	}

	s := new(Skip32)

	s.key = make([]byte, 10)

	copy(s.key, key)

	return s, nil

}

// Obfus obfuscates a uint32
func (s *Skip32) Obfus(id uint32) uint32 {

	var buf [4]byte

	buf[0] = byte((id >> 24) & 0xff)
	buf[1] = byte((id >> 16) & 0xff)
	buf[2] = byte((id >> 8) & 0xff)
	buf[3] = byte((id >> 0) & 0xff)

	crypt32(s.key, buf[:], true /* encrypt */)

	j := (uint32(buf[0]) << 24) | (uint32(buf[1]) << 16) | (uint32(buf[2]) << 8) | uint32(buf[3])

	return j
}

// Unobfus unobfuscates an uint32
func (s *Skip32) Unobfus(id uint32) uint32 {

	var buf [4]byte

	buf[0] = byte((id >> 24) & 0xff)
	buf[1] = byte((id >> 16) & 0xff)
	buf[2] = byte((id >> 8) & 0xff)
	buf[3] = byte((id >> 0) & 0xff)

	crypt32(s.key, buf[:], false /* decrypt */)

	j := (uint32(buf[0]) << 24) | (uint32(buf[1]) << 16) | (uint32(buf[2]) << 8) | uint32(buf[3])

	return j

}

// Obfus64 obfuscates a uint64
func (s *Skip32) Obfus64(id uint64) uint64 {

	// as two 32-bit integers
	ida := uint32(id >> 32)
	idb := uint32(id & 0x00000000fffffffff)

	// use skip32/cbc
	ca := s.Obfus(ida)
	idb ^= ca
	cb := s.Obfus(idb)

	return uint64(ca)<<32 | uint64(cb)
}

// UnObfus64 unobfuscates a uint64
func (s *Skip32) UnObfus64(id uint64) uint64 {

	// as two 32-bit integers
	ca := uint32(id >> 32)
	cb := uint32(id & 0x00000000ffffffff)

	// undo the skip32/cbc
	ida := s.Unobfus(ca)
	idb := s.Unobfus(cb)
	idb ^= ca

	return uint64(ida)<<32 | uint64(idb)
}
