172 lines
5.0 KiB
Go
172 lines
5.0 KiB
Go
package mijia
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/base64"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
testSsecurity = "MDEyMzQ1Njc4OWFiY2RlZg=="
|
|
testNonce = "AAECAwQFBgcICQoL"
|
|
testSignedNonce = "16/CeTzC9IqVVbiZ01Hy/Qd8rtVo5ybLo+ph/Vvh52k="
|
|
)
|
|
|
|
func TestGenerateNonce(t *testing.T) {
|
|
before := time.Now().Unix() / 60
|
|
nonce, err := generateNonce()
|
|
if err != nil {
|
|
t.Fatalf("generateNonce() error = %v", err)
|
|
}
|
|
after := time.Now().Unix() / 60
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(nonce)
|
|
if err != nil {
|
|
t.Fatalf("generated nonce is not base64: %v", err)
|
|
}
|
|
if len(decoded) <= 8 {
|
|
t.Fatalf("decoded nonce length = %d, want more than 8", len(decoded))
|
|
}
|
|
|
|
minute := decodeBigEndian(decoded[8:])
|
|
if minute < before || minute > after {
|
|
t.Fatalf("nonce minute = %d, want between %d and %d", minute, before, after)
|
|
}
|
|
if len(decoded[8:]) > 1 && decoded[8] == 0 {
|
|
t.Fatal("nonce minute is not minimally encoded")
|
|
}
|
|
}
|
|
|
|
func TestSignedNonce(t *testing.T) {
|
|
got, err := signedNonce(testSsecurity, testNonce)
|
|
if err != nil {
|
|
t.Fatalf("signedNonce() error = %v", err)
|
|
}
|
|
if got != testSignedNonce {
|
|
t.Fatalf("signedNonce() = %q, want %q", got, testSignedNonce)
|
|
}
|
|
}
|
|
|
|
func TestRC4Drop1024(t *testing.T) {
|
|
ciphertext, err := encryptRC4(testSignedNonce, "hello world")
|
|
if err != nil {
|
|
t.Fatalf("encryptRC4() error = %v", err)
|
|
}
|
|
if ciphertext != "9ve6riTrkW1oJUE=" {
|
|
t.Fatalf("encryptRC4() = %q, want fixed ciphertext", ciphertext)
|
|
}
|
|
|
|
plaintext, err := decryptRC4(testSignedNonce, ciphertext)
|
|
if err != nil {
|
|
t.Fatalf("decryptRC4() error = %v", err)
|
|
}
|
|
if string(plaintext) != "hello world" {
|
|
t.Fatalf("decryptRC4() = %q, want %q", plaintext, "hello world")
|
|
}
|
|
}
|
|
|
|
func TestGenerateEncryptedParams(t *testing.T) {
|
|
params, err := generateEncryptedParams("/miotspec/prop/get", "post", testSignedNonce, testNonce, "{\"k\":\"v\"}", testSsecurity)
|
|
if err != nil {
|
|
t.Fatalf("generateEncryptedParams() error = %v", err)
|
|
}
|
|
|
|
want := encryptedParams{
|
|
Data: "5bC94HHpkCBn",
|
|
RC4Hash: "19WFsROvqTVVE0wemsEqz8GI7Ib8ZRiiLJS5hQ==",
|
|
Signature: "uRPqk6IHkFCAg+Skfo+5l/KbTpM=",
|
|
Ssecurity: testSsecurity,
|
|
Nonce: testNonce,
|
|
}
|
|
if params != want {
|
|
t.Fatalf("generateEncryptedParams() = %#v, want %#v", params, want)
|
|
}
|
|
|
|
firstSignature := encryptedSignature("/miotspec/prop/get", "post", []orderedParam{{Key: "data", Value: "{\"k\":\"v\"}"}}, testSignedNonce)
|
|
if firstSignature != "IGSsXdO7OZiyN58pywgUYELur6g=" {
|
|
t.Fatalf("first signature = %q, want fixed signature", firstSignature)
|
|
}
|
|
}
|
|
|
|
func TestDecryptPayload(t *testing.T) {
|
|
plaintext, err := decryptPayload(testSsecurity, testNonce, "9ve6riTrkW1oJUE=")
|
|
if err != nil {
|
|
t.Fatalf("decryptPayload() plain error = %v", err)
|
|
}
|
|
if plaintext != "hello world" {
|
|
t.Fatalf("decryptPayload() plain = %q, want %q", plaintext, "hello world")
|
|
}
|
|
|
|
var compressed bytes.Buffer
|
|
writer := gzip.NewWriter(&compressed)
|
|
if _, err := writer.Write([]byte("gzip response")); err != nil {
|
|
t.Fatalf("gzip write: %v", err)
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("gzip close: %v", err)
|
|
}
|
|
ciphertext, err := encryptRC4Bytes(testSignedNonce, compressed.Bytes())
|
|
if err != nil {
|
|
t.Fatalf("encrypt compressed response: %v", err)
|
|
}
|
|
|
|
decrypted, err := decryptPayload(testSsecurity, testNonce, ciphertext)
|
|
if err != nil {
|
|
t.Fatalf("decryptPayload() gzip error = %v", err)
|
|
}
|
|
if decrypted != "gzip response" {
|
|
t.Fatalf("decryptPayload() gzip = %q, want %q", decrypted, "gzip response")
|
|
}
|
|
}
|
|
|
|
func TestDecryptPayloadInvalidBase64(t *testing.T) {
|
|
_, err := decryptPayload(testSsecurity, testNonce, "not-base64")
|
|
if err == nil || !strings.Contains(err.Error(), "decode RC4 payload") {
|
|
t.Fatalf("decryptPayload() error = %v, want invalid base64 error", err)
|
|
}
|
|
}
|
|
|
|
func TestDecryptPayloadDamagedGzip(t *testing.T) {
|
|
ciphertext, err := encryptRC4Bytes(testSignedNonce, []byte{0x1f, 0x8b, 0xff})
|
|
if err != nil {
|
|
t.Fatalf("encrypt damaged gzip response: %v", err)
|
|
}
|
|
|
|
_, err = decryptPayload(testSsecurity, testNonce, ciphertext)
|
|
if err == nil || !strings.Contains(err.Error(), "open gzip response") {
|
|
t.Fatalf("decryptPayload() error = %v, want damaged gzip error", err)
|
|
}
|
|
}
|
|
|
|
func TestDecryptPayloadDecompressedLimit(t *testing.T) {
|
|
var compressed bytes.Buffer
|
|
writer := gzip.NewWriter(&compressed)
|
|
oversized := bytes.Repeat([]byte{0xff}, maxDecompressedResponseBytes+1)
|
|
if _, err := writer.Write(oversized); err != nil {
|
|
t.Fatalf("gzip write: %v", err)
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("gzip close: %v", err)
|
|
}
|
|
ciphertext, err := encryptRC4Bytes(testSignedNonce, compressed.Bytes())
|
|
if err != nil {
|
|
t.Fatalf("encrypt oversized gzip response: %v", err)
|
|
}
|
|
|
|
_, err = decryptPayload(testSsecurity, testNonce, ciphertext)
|
|
if err == nil || !strings.Contains(err.Error(), "decompressed response exceeds") {
|
|
t.Fatalf("decryptPayload() error = %v, want decompressed limit error", err)
|
|
}
|
|
}
|
|
|
|
func decodeBigEndian(value []byte) int64 {
|
|
var result int64
|
|
for _, current := range value {
|
|
result = result<<8 | int64(current)
|
|
}
|
|
return result
|
|
}
|