package main

import (
	"bytes"
	"crypto/aes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os/user"
	"path/filepath"
)

/*
0 maybe device ID, not needed to work
1
2 lic name string
3 lic Type "0"
4 lic time "0"
5
*/

var keyFile string
var deviceId string

func initCmdline() {
	flag.StringVar(&keyFile, "k", "Key.data", "key file")
	flag.StringVar(&deviceId, "id", "DHO8XX", "device Id")
	flag.Parse()
}

func Expand(path string) string {
	if len(path) == 0 || path[0] != '~' {
		return path
	}
	usr, err := user.Current()
	if err != nil {
		return path
	}
	return filepath.Join(usr.HomeDir, path[1:])
}

const delta = 0x9E3779B9

func mx(sum uint32, y uint32, z uint32, p uint32, e uint32, k []uint32) uint32 {
	return ((z>>5 ^ y<<2) + (y>>3 ^ z<<4)) ^ ((sum ^ y) + (k[p&3^e] ^ z))
}

func fixk(k []uint32) []uint32 {
	if len(k) < 4 {
		key := make([]uint32, 4)
		copy(key, k)
		return key
	}
	return k
}

func XXTeaEncrypt(v []uint32, k []uint32) []uint32 {
	length := uint32(len(v))
	n := length - 1
	k = fixk(k)
	var y, z, sum, e, p, q uint32
	z = v[n]
	sum = 0
	for q = 6 + 52/length; q > 0; q-- {
		sum += delta
		e = sum >> 2 & 3
		for p = 0; p < n; p++ {
			y = v[p+1]
			v[p] += mx(sum, y, z, p, e, k)
			z = v[p]
		}
		y = v[0]
		v[n] += mx(sum, y, z, p, e, k)
		z = v[n]
	}
	return v
}

func XXTeaDecrypt(v []uint32, k []uint32) []uint32 {
	length := uint32(len(v))
	n := length - 1
	k = fixk(k)
	var y, z, sum, e, p, q uint32
	y = v[0]
	q = 6 + 52/length
	for sum = q * delta; sum != 0; sum -= delta {
		e = sum >> 2 & 3
		for p = n; p > 0; p-- {
			z = v[p-1]
			v[p] -= mx(sum, y, z, p, e, k)
			y = v[p]
		}
		z = v[n]
		v[0] -= mx(sum, y, z, p, e, k)
		y = v[0]
	}
	return v
}

func HexString(data []byte) string {
	var buf bytes.Buffer
	for i := 0; i < len(data)-1; i++ {
		buf.WriteString(fmt.Sprintf("%02X ", data[i]))
	}
	buf.WriteString(fmt.Sprintf("%02X", data[len(data)-1]))
	return buf.String()
}

func HexStringS(data []byte, sep string) string {
	var buf bytes.Buffer
	for i := 0; i < len(data)-1; i++ {
		buf.WriteString(fmt.Sprintf("%02X%s", data[i], sep))
	}
	buf.WriteString(fmt.Sprintf("%02X", data[len(data)-1]))
	return buf.String()
}

func HexStringLsb(data []byte, sep string) string {
	var buf bytes.Buffer
	for i := 0; i < len(data)-1; i++ {
		buf.WriteString(fmt.Sprintf("%02x%s", (data[i]<<4)|(data[i]>>4), sep))
	}
	t := data[len(data)-1]
	buf.WriteString(fmt.Sprintf("%02x", (t<<4)|(t>>4)))
	return buf.String()
}

func decodeDefaultXXTEA(data []byte) []byte {
	k := []uint32{0x03920001, 0x08410841, 0x18C32104, 0x318639C7}
	N := len(data)
	u32 := make([]uint32, N>>2)
	for i, _ := range u32 {
		u32[i] = binary.LittleEndian.Uint32(data[i*4:])
	}
	u32d := XXTeaDecrypt(u32, k)
	res := make([]uint8, N)
	for i, v := range u32d {
		binary.LittleEndian.PutUint32(res[i*4:], v)
	}
	return res
}

func EncryptLicenseString(key []uint8, str string) (string, error) {
	block, err := aes.NewCipher(key)
	if nil != err {
		return "", err
	}
	x := make([]uint8, 48)
	copy(x, []uint8(str))
	block.Encrypt(x, x)
	block.Encrypt(x[0x10:], x[0x10:])
	block.Encrypt(x[0x20:], x[0x20:])
	return HexStringLsb(x, ""), nil
}

func MakeLicense(key []uint8, deviceId, feature string) string {
	s := fmt.Sprintf("%s#0#%s#4#0#0", deviceId, feature)
	res, err := EncryptLicenseString(key, s)
	if nil != err {
		return ""
	}
	return fmt.Sprintf("DHO800-%s@%s", feature, res)
}

func LoadKeys() ([]uint8, error) {
	data, err := ioutil.ReadFile(Expand(keyFile))
	if nil != err {
		return nil, err
	}
	dd := decodeDefaultXXTEA(data)
	i := bytes.Index(dd, []uint8(";"))
	if -1 == i {
		return nil, errors.New("key format error")
	}
	fmt.Printf("Key: %v\n", string(dd))
	return dd[i+1:], nil

}

/*
"BW2T4"		Bandwith upgrade options
"BW2T8"
"BW4T8"
"BW7T10"
"BW7T15"
"BW7T20"
"BW10T20"
"BW15T25"
"AERO"		MIL-STD-1553 serial bus trigger and analysis (MIL-STD-1553)
"ARINC"		ARINC 429 Aero space protocol
"AUDIO"		Audio serial bus trigger and analysis (I2S, only available for the MSO5XX4 model or the model installed with the MSO5000-4CH option)
"AUTO"		Auto serial bus trigger and analysis (CAN and LIN)
"AWG"		Dual-channel 25 MHz arbitrary waveform generator
"BND"       	Bundle of COMP, EMBD, AUTO, FLEX, AUDIO, AERO, AWG, PWR
"BODE"		Bode plot
"COMP"		PC serial bus trigger and analysis (RS232/UART)
"COUNT"		??? Frequency counter
"DG"		??? Signal generator
"EMBD"		Embedded serial bus trigger and analysis (I2C and SPI)
"EYE"		"Eye" analisys
"FLEX"		FlexRay serial bus trigger and analysis (FlexRay)
"JITTER"	Jitter analisys
"MSO"		??? Mixed Signal Oscilloscope
"PWR"		Built-in Power Analysis
"RLU"		Memory Depth Upgrade Option 
"RTSA"		Real-Time Spectrum Analyzer
"UPA"		Ultra Power Analyzer
"CM_ENET"
"CM_HDMI"
"CM_MIPI"
"CM_USB"
*/
var options []string = []string{"BW2T4", "BW2T8", "BW4T8", "BW7T10", "BW7T15", "BW7T20", "BW10T20", "BW15T25", "AERO", "ARINC", "AUDIO", "AUTO", "AWG", "BND", "BODE", "COMP", "COUNT", "DG", "EMBD", "EYE", "FLEX", "JITTER", "MSO", "PWR", "RLU", "RTSA", "UPA", "CM_USB", "CM_ENET", "CM_MIPI", "CM_HDMI"}

func main() {
	initCmdline()
	k, err := LoadKeys()
	if nil != err {
		fmt.Printf("error %v\n", err)
		return
	}
	fmt.Printf("Generating options for %s\n", deviceId)
	for _, o := range options {
		lic := MakeLicense(k[0:32], deviceId, o)
		fmt.Printf(":SYSTem:OPTion:INSTall %v\n", lic)
	}

}
