initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/.idea
|
||||
/*.sh
|
||||
40
cmd/tesla-firmware-decrypt/main.go
Normal file
40
cmd/tesla-firmware-decrypt/main.go
Normal 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
12
go.mod
Normal 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
19
go.sum
Normal 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
38
pkg/decrypt.go
Normal 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
41
pkg/decrypt_test.go
Normal 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
29
py/main.py
Normal 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
1
resources/1_0.bin
Normal file
@@ -0,0 +1 @@
|
||||
ƒf²Ø²ø÷¨t[$šëØs©¶IRHBÑ%ô8,OÑy÷ò©]¿‹i¸lòˆ»˜´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>“è
|
||||
Reference in New Issue
Block a user