initial commit

This commit is contained in:
Denys Vitali
2022-11-18 11:05:20 +01:00
commit 2a3be10d7e
8 changed files with 182 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/.idea
/*.sh

View File

@@ -0,0 +1,40 @@
package main
import (
"encoding/base64"
"github.com/alexflint/go-arg"
"github.com/denysvitali/tesla-firmware-decrypt/pkg"
"github.com/sirupsen/logrus"
"os"
)
var args struct {
InputFile string `arg:"positional,required"`
Key string `arg:"-k,required"`
}
var logger = logrus.New()
func main() {
arg.MustParse(&args)
f, err := os.Open(args.InputFile)
if err != nil {
logger.Fatalf("unable to open file: %v", err)
}
keyBytes, err := base64.StdEncoding.DecodeString(args.Key)
if err != nil {
logger.Fatalf("unable to decode base64 key: %v", err)
}
if len(keyBytes) != 32 {
logger.Fatalf("invalid key size: expected 32 but received %d", len(keyBytes))
}
dec := decrypt.Decrypt{
Key: keyBytes,
}
err = dec.Decrypt(f, os.Stdout)
if err != nil {
logger.Fatalf("unable to decrypt: %v", err)
}
}

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module github.com/denysvitali/tesla-firmware-decrypt
go 1.19
require golang.org/x/crypto v0.3.0
require (
github.com/alexflint/go-arg v1.4.3 // indirect
github.com/alexflint/go-scalar v1.1.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
golang.org/x/sys v0.2.0 // indirect
)

19
go.sum Normal file
View File

@@ -0,0 +1,19 @@
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM=
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

38
pkg/decrypt.go Normal file
View File

@@ -0,0 +1,38 @@
package decrypt
import (
"encoding/binary"
"fmt"
"golang.org/x/crypto/salsa20"
"io"
)
type Decrypt struct {
Key []byte
}
func (d *Decrypt) Decrypt(reader io.Reader, writer io.Writer) error {
if len(d.Key) != 32 {
return fmt.Errorf("key must be 32 bytes long")
}
blockNumber := 0
for {
nonceBytes := make([]byte, 24)
binary.LittleEndian.PutUint64(nonceBytes, uint64(blockNumber))
block := make([]byte, 256)
_, err := reader.Read(block)
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("unable to read file: %v", err)
}
outBlock := make([]byte, 256)
salsa20.XORKeyStream(outBlock, block, nonceBytes, (*[32]byte)(d.Key))
blockNumber++
_, err = writer.Write(outBlock)
if err != nil {
return fmt.Errorf("unable to write: %v", err)
}
}
}

41
pkg/decrypt_test.go Normal file
View File

@@ -0,0 +1,41 @@
package decrypt_test
import (
"bytes"
"encoding/base64"
"os"
decrypt "tesla-firmware-decrypt/pkg"
"testing"
)
func TestDecrypt_Decrypt(t *testing.T) {
ciphertext, err := os.Open("./resources/1_0.bin")
if err != nil {
t.Fatalf("unable to open file: %v", err)
}
key := "uPaonw9KIv1tptQ3hsJsrR2Ibz3YhOcBRBaRitSi4Qg="
keyBytes, err := base64.StdEncoding.DecodeString(key)
if err != nil {
t.Fatalf("unable to decode base64: %v", err)
}
d := decrypt.Decrypt{Key: keyBytes}
buffer := bytes.NewBuffer(nil)
err = d.Decrypt(ciphertext, buffer)
if err != nil {
t.Fatalf("unable to decrypt: %v", err)
}
expectedHeader := []byte("hsqs")
header := make([]byte, len(expectedHeader))
_, err = buffer.Read(header)
if err != nil {
t.Fatalf("unable to read into buffer: %v", err)
}
if !bytes.Equal(expectedHeader, header) {
t.Fatalf("decryption failed: got %s but %s was expected", header, expectedHeader)
}
}

29
py/main.py Normal file
View File

@@ -0,0 +1,29 @@
import base64
import tempfile
import click
import salsa20
@click.command()
@click.option("-i", "--input", required=True)
@click.option("-k", "--key", required=True)
def cmd(input: str, key: str):
tmp = tempfile.mktemp()
print(tmp)
with open(tmp, mode="wb") as tmp_out:
with open(input, mode="rb") as f:
counter = 0
key_bytes = base64.b64decode(key)
while True:
in_bytes = f.read(256)
if len(in_bytes) == 0:
return
counter_bytes = counter.to_bytes(24, byteorder='little')
res = salsa20.XSalsa20_xor(in_bytes, counter_bytes, key_bytes)
tmp_out.write(res)
counter += 1
if __name__ == "__main__":
cmd()

1
resources/1_0.bin Normal file
View File

@@ -0,0 +1 @@
ƒf²Ø²ø÷¨t[$šëØs©¶IRHBÑ%ô 8, OÑy÷ò© ]¿i¸ˆ»˜´AÖSú;-4TfS†¢¾¶Vý]´…™/¸úP…ûZ~øezþÀœEÊ»|©gì|<7C> ëB“ÿeñïŽ1zîzÏ£»DfòœD%(`bÀõÔAÐÒ™"XwÒ¬<>±xÔèŽN!\¾Ü™T'FaÊêLÔÄá7J#à¯yÖ—è°…9<E280A6>Î×<C38E>“è