diff --git a/README.md b/README.md new file mode 100644 index 0000000..66fba38 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Rigol HDO-series license generator + +* Obtain the IP address of the scope +* Connect to the scope using the Android Debug Bridge `adb connect :55555` +* Download the unique key from the scope `adb pull /rigol/data/Key.data` +* Generate the license key using the rgtool program on the Key file `go run ./rgtool.go` +* Insert generated codes via SCPI interface on the website on the scope (`http://`) + +When not wanting to install adb or go, docker could also be used instead. +```sh +docker run --rm -it -v $(pwd):/workdir -w /workdir alpine \ + /bin/sh -c 'apk add android-tools && adb connect :55555 && adb pull /rigol/data/Key.data' +``` +```sh +docker run --rm -it -v $(pwd):/workdir -w /workdir golang go run ./rgtool.go +``` diff --git a/rgtool.go b/rgtool.go new file mode 100644 index 0000000..cb7a530 --- /dev/null +++ b/rgtool.go @@ -0,0 +1,227 @@ +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", "HDO4XXXXXX", "devide 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("HDO4000-%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 + +} + +// BND +// MSO +// COMP +// EMBD +// AUTO +// FLEX +// AUDIO +// AERO +// ARINC +// DG +// JITTER +// EYE +// RTSA +// CM_USB +// CM_ENET +// CM_IPI +// CM_HDMI +// PWR +// UPA +// RLU +// BODE +// BW7T10 +// BW7T20 +// BW100T20 +// BW2T4 +// BW2T8 +// BW4T8 +// COUNT +// UNKNOWN + +var options []string = []string{"EMBD", "AUTO", "BW2T8", "COMP", "AERO", "FLEX", "AUDIO", "RLU", "UPA"} + +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(":SYST:OPT:INST %v\n", lic) + } + +}