Skip to content

Instantly share code, notes, and snippets.

@wader
Created October 3, 2023 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wader/bcbc9302fe1a3040834feddd227bc20f to your computer and use it in GitHub Desktop.
Save wader/bcbc9302fe1a3040834feddd227bc20f to your computer and use it in GitHub Desktop.
// usage:
// cat BK7231T_TuyaConfig_obk8D7EC082.bin | go run tyuaconfig.go > out
package main
import (
"crypto/aes"
"crypto/cipher"
"io"
"log"
"os"
)
// from https://github.com/openshwprojects/BK7231GUIFlashTool/blob/5c6ff0551bb0529f8917653f7adb9f49ccaf43c8/BK7231Flasher/TuyaConfig.cs
// // thanks to kmnh & Kuba bk7231 tools for figuring out this format
// static string KEY_MASTER = "qwertyuiopasdfgh";
// static int SECTOR_SIZE = 4096;
// static uint MAGIC_FIRST_BLOCK = 0x13579753;
// static uint MAGIC_NEXT_BLOCK = 0x98761234;
// static byte[] KEY_PART_1 = Encoding.ASCII.GetBytes("8710_2M");
// static byte[] KEY_PART_2 = Encoding.ASCII.GetBytes("HHRRQbyemofrtytf");
// static byte[] MAGIC_CONFIG_START = new byte[] { 0x46, 0xDC, 0xED, 0x0E, 0x67, 0x2F, 0x3B, 0x70, 0xAE, 0x12, 0x76, 0xA3, 0xF8, 0x71, 0x2E, 0x03 };
// // TODO: check more bins with this offset
// // hex 0x1EE000
// // While flash is 0x200000, so we have at most 0x12000 bytes there...
// // So it's 0x12 sectors (18 sectors)
// byte[] makeSecondaryKey(byte[] innerKey)
// {
// byte[] key = new byte[0x10];
// for (int i = 0; i < 16; i++)
// {
// key[i] = (byte)(KEY_PART_1[i & 3] + KEY_PART_2[i]);
// }
// for (int i = 0; i < 16; i++)
// {
// key[i] = (byte)((key[i] + innerKey[i]) % 256);
// }
// return key;
// }
func makeSecondaryKey(innerKey []byte) []byte {
var key [0x10]byte
for i := 0; i < 16; i++ {
key[i] = (byte)(KEY_PART_1[i&3] + KEY_PART_2[i])
}
for i := 0; i < 16; i++ {
key[i] = (byte)((key[i] + innerKey[i]) /*% 256*/)
}
return key[:]
}
var KEY_MASTER = []byte("qwertyuiopasdfgh")
const SECTOR_SIZE = 4096
var KEY_PART_1 = []byte("8710_2M")
var KEY_PART_2 = []byte("HHRRQbyemofrtytf")
// from https://github.com/andreburgaud/crypt2go/blob/v1.4.0/ecb/ecb.go
type ecb struct {
b cipher.Block
blockSize int
tmp []byte
}
func newECB(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
tmp: make([]byte, b.BlockSize()),
}
}
type ecbDecrypter ecb
// NewECBDecrypter returns a BlockMode which decrypts in electronic codebook (ECB)
// mode, using the given Block.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
panic("crypto/cipher: output smaller than input")
}
if len(src) == 0 {
return
}
for len(src) > 0 {
x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize])
src = src[x.blockSize:]
dst = dst[x.blockSize:]
}
}
func main() {
log.Println("bla")
inputEncrypted, err := io.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
cb, err := aes.NewCipher(KEY_MASTER)
if err != nil {
panic(err)
}
headerSectorPlain := [SECTOR_SIZE]byte{}
cm := NewECBDecrypter(cb)
cm.CryptBlocks(headerSectorPlain[:], inputEncrypted[0:SECTOR_SIZE])
//os.Stdout.Write(headerSectorPlain[:])
const MAGIC_SIZE = 4
const CRC_SIZE = 4
key := headerSectorPlain[MAGIC_SIZE+CRC_SIZE : MAGIC_SIZE+CRC_SIZE+16]
secondaryKey := makeSecondaryKey(key)
secondaryCB, err := aes.NewCipher(secondaryKey)
if err != nil {
panic(err)
}
sectorPlain := [SECTOR_SIZE]byte{}
secondaryCM := NewECBDecrypter(secondaryCB)
for sectorIndex := 1; (sectorIndex+1)*SECTOR_SIZE < len(inputEncrypted); sectorIndex++ {
secondaryCM.CryptBlocks(sectorPlain[:], inputEncrypted[sectorIndex*SECTOR_SIZE:(sectorIndex+1)*SECTOR_SIZE])
os.Stdout.Write(sectorPlain[MAGIC_SIZE+CRC_SIZE:])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment