Files
go2rtc/pkg/tuya/crypto.go
T
seydx 998c85d6f5 - support adding cameras via interface
- support qr code auth
- support resolution change
- support h265
- refactor code
2025-07-10 16:09:18 +02:00

135 lines
2.7 KiB
Go

package tuya
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"math/rand"
)
// https://github.com/tuya/tuya-device-sharing-sdk/blob/main/tuya_sharing/customerapi.py
func AesGCMEncrypt(rawData string, secret string) (string, error) {
nonce := []byte(RandomNonce(12))
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return "", err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
ciphertext := aesgcm.Seal(nil, nonce, []byte(rawData), nil)
nonceB64 := base64.StdEncoding.EncodeToString(nonce)
ciphertextB64 := base64.StdEncoding.EncodeToString(ciphertext)
return nonceB64 + ciphertextB64, nil
}
func AesGCMDecrypt(cipherData string, secret string) (string, error) {
if len(cipherData) <= 16 {
return "", fmt.Errorf("invalid ciphertext length")
}
nonceB64 := cipherData[:16]
ciphertextB64 := cipherData[16:]
nonce, err := base64.StdEncoding.DecodeString(nonceB64)
if err != nil {
return "", err
}
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextB64)
if err != nil {
return "", err
}
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return "", err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
func SecretGenerating(rid, sid, hashKey string) string {
message := hashKey
mod := 16
if sid != "" {
sidLength := len(sid)
length := sidLength
if length > mod {
length = mod
}
ecode := ""
for i := 0; i < length; i++ {
idx := int(sid[i]) % mod
ecode += string(sid[idx])
}
message += "_"
message += ecode
}
h := hmac.New(sha256.New, []byte(rid))
h.Write([]byte(message))
byteTemp := h.Sum(nil)
secret := hex.EncodeToString(byteTemp)
return secret[:16]
}
func RestfulSign(hashKey, queryEncdata, bodyEncdata string, data map[string]string) string {
headers := []string{"X-appKey", "X-requestId", "X-sid", "X-time", "X-token"}
headerSignStr := ""
for _, item := range headers {
val, exists := data[item]
if exists && val != "" {
headerSignStr += item + "=" + val + "||"
}
}
signStr := ""
if len(headerSignStr) > 2 {
signStr = headerSignStr[:len(headerSignStr)-2]
}
if queryEncdata != "" {
signStr += queryEncdata
}
if bodyEncdata != "" {
signStr += bodyEncdata
}
h := hmac.New(sha256.New, []byte(hashKey))
h.Write([]byte(signStr))
return hex.EncodeToString(h.Sum(nil))
}
func RandomNonce(length int) string {
const charset = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"
result := make([]byte, length)
for i := range result {
result[i] = charset[rand.Intn(len(charset))]
}
return string(result)
}