From 0ec38a033fbc302cebf1725bd186fb9e4729b4ec Mon Sep 17 00:00:00 2001 From: sam80180 Date: Sun, 19 May 2024 00:36:17 +0800 Subject: [PATCH] fix: finish RSA public key authentication --- .gitignore | 4 ++ Dockerfile_build | 11 ++++ docker-compose.yml | 11 ++++ go.mod | 25 +++++++- go.sum | 68 +++++++++++++++++++-- src/adb/my_rsa_funcs.c | 27 +++++++++ src/adb/my_rsa_funcs.h | 3 + src/adb/packet.go | 11 ++++ src/adb/pubkey.go | 120 +++++++++++++++++++++++++++++++++++++ src/adb/tcpusb.go | 131 ++++++++++++++++++++++++++++++----------- 10 files changed, 368 insertions(+), 43 deletions(-) create mode 100644 Dockerfile_build create mode 100644 docker-compose.yml create mode 100644 src/adb/my_rsa_funcs.c create mode 100644 src/adb/my_rsa_funcs.h create mode 100644 src/adb/pubkey.go diff --git a/.gitignore b/.gitignore index 5444d11..317da92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .idea/ +.vscode/ dist/ +vendor/ +sonic-android-supply +sonic-android-supply.exe diff --git a/Dockerfile_build b/Dockerfile_build new file mode 100644 index 0000000..d39ce46 --- /dev/null +++ b/Dockerfile_build @@ -0,0 +1,11 @@ +FROM centos:7.6.1810 +WORKDIR /app +ENV CGO_ENABLED=1 GO111MODULE=on GOPATH=/go GOOS=linux GOLANG_VERSION=1.20.14 \ + PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +RUN yum install -y gcc openssl-devel; \ + cd /tmp; curl https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz --output go.tar.gz; tar -C /usr/local -xzf go.tar.gz; rm -f go.tar.gz +#CMD ["/bin/bash"] +CMD ["/bin/bash", "-c", "go mod tidy; go mod vendor; go build"] + +# References: +# https://www.ovhcloud.com/en-gb/community/tutorials/how-to-install-go-centos/ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f2d91d7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3" +services: + build_sas: + build: + context: "." + dockerfile: "./Dockerfile_build" + container_name: "build_sas" + image: "build_sas" + tty: true + volumes: + - ".:/app" diff --git a/go.mod b/go.mod index 49a0907..1fb44f8 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,34 @@ module github.com/SonicCloudOrg/sonic-android-supply -go 1.18 +go 1.20 require ( - github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 + github.com/0xc0d/encoding v0.1.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/pkg/errors v0.8.0 + github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.4.0 + github.com/spf13/viper v1.18.2 ) require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index eb26905..33d0aea 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,76 @@ +github.com/0xc0d/encoding v0.1.0 h1:doSPUQU7EAeDEyk1RKUrodin+7V0Fv2PeQE0izJOUYo= +github.com/0xc0d/encoding v0.1.0/go.mod h1:kmh8I0sB3CBTNeJfu8ryX4IQUuJBMoPmkGMD94Sam/4= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 h1:wqckanyE9qc/XnvnybC6SHOb8Nyd62QXAZOzA8twFig= -github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9/go.mod h1:64ikIrMv84B+raz7akXOqbF7cK3/OQQ/6cClY10oy7A= +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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/adb/my_rsa_funcs.c b/src/adb/my_rsa_funcs.c new file mode 100644 index 0000000..3dac3ed --- /dev/null +++ b/src/adb/my_rsa_funcs.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include +#include +#include "my_rsa_funcs.h" + +char last_error_string[2048] = {0}; + +int rsa_public_verify(unsigned char *m, unsigned char *sigbuf, unsigned char* strPEMPubKey) { + BIO *bio = BIO_new_mem_buf(strPEMPubKey, strlen(strPEMPubKey)); + RSA *public_key = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); + int n = RSA_verify(NID_sha1, m, strlen(m), sigbuf, strlen(sigbuf), public_key); + if (n==-1) { + snprintf(last_error_string, sizeof(last_error_string), "%s", ERR_error_string(ERR_get_error(), NULL)); + } // end if + RSA_free(public_key); + return n; +} // end rsa_public_verify() + +/* +References: +https://www.openssl.org/docs/man3.0/man3/RSA_verify.html +https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/daemon/auth.cpp#177 +*/ diff --git a/src/adb/my_rsa_funcs.h b/src/adb/my_rsa_funcs.h new file mode 100644 index 0000000..c09888c --- /dev/null +++ b/src/adb/my_rsa_funcs.h @@ -0,0 +1,3 @@ +extern char last_error_string[2048]; + +int rsa_public_verify(unsigned char *, unsigned char *, unsigned char *); diff --git a/src/adb/packet.go b/src/adb/packet.go index bced509..43642d8 100644 --- a/src/adb/packet.go +++ b/src/adb/packet.go @@ -2,11 +2,13 @@ package adb import ( "bytes" + "encoding/base64" "encoding/binary" "encoding/hex" "fmt" "io" "os" + "regexp" "strconv" ) @@ -79,3 +81,12 @@ func (pkt Packet) DumpToStdout() { dumper.Write(pkt.Body) dumper.Close() } + +func (pkt *Packet) ExtractADBRSAPublicKeyFromPayload() ([]byte, error) { + REGEX_ADB_PUBKEY := regexp.MustCompile(`(?m)^((?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?)\x00?(\s.*\s*)?$`) // https://github.com/openstf/stf/issues/1030#issuecomment-479487994 + trimmedBody := pkt.BodySkipNull() + if !REGEX_ADB_PUBKEY.Match(trimmedBody) { + return nil, fmt.Errorf("unrecognizable public key format") + } // end if + return base64.StdEncoding.DecodeString(string(REGEX_ADB_PUBKEY.FindSubmatch(trimmedBody)[1])) +} // end ExtractADBRSAPublicKeyFromPayload() diff --git a/src/adb/pubkey.go b/src/adb/pubkey.go new file mode 100644 index 0000000..dd0a24d --- /dev/null +++ b/src/adb/pubkey.go @@ -0,0 +1,120 @@ +package adb + +/* +#cgo CFLAGS: -I/usr/local/opt/openssl/include +#cgo LDFLAGS: -L/usr/include/openssl -Lmy/library/src -lcrypto +#include "my_rsa_funcs.h" +*/ +import "C" + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/binary" + "encoding/pem" + "fmt" + "math/big" + "unsafe" + + "github.com/0xc0d/encoding/bytebuffer" +) + +var ANDROID_PUBKEY_MODULUS_SIZE int = 2048 / 8 +var ANDROID_PUBKEY_ENCODED_SIZE int = 3*4 + 2*ANDROID_PUBKEY_MODULUS_SIZE +var ANDROID_PUBKEY_MODULUS_SIZE_WORDS int = ANDROID_PUBKEY_MODULUS_SIZE / 4 +var RSA_SHA_PKCS1_SIGNATURE_PADDING []byte = []byte{ + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, + 0x04, 0x14, +} + +func swapEndianness(bytes []byte) []byte { + len := len(bytes) + out := make([]byte, len) + for i, _ := range bytes { + out[i] = bytes[len-i-1] + } // end for + return out +} // end swapEndianness() + +func AdbPubKeyDecode(androidPubkey []byte) (*rsa.PublicKey, error) { + key_size := len(androidPubkey) + if key_size < ANDROID_PUBKEY_ENCODED_SIZE { + return nil, fmt.Errorf("invalid key length") + } // end if + keyStruct := bytebuffer.NewWrappedBuffer(androidPubkey, key_size) + keyStruct.SetOrder(binary.LittleEndian) + modulusSize, _ := keyStruct.GetAsInt32() + if int(modulusSize) != ANDROID_PUBKEY_MODULUS_SIZE_WORDS { + return nil, fmt.Errorf("invalid modulus length") + } // end if + modulus := make([]byte, ANDROID_PUBKEY_MODULUS_SIZE) + keyStruct.SetPosition(8) + keyStruct.GetBytes(modulus, 0, ANDROID_PUBKEY_MODULUS_SIZE) + n := new(big.Int) + n.SetBytes(swapEndianness(modulus)) + keyStruct.SetPosition(520) + e, _ := keyStruct.GetAsInt32() + return &rsa.PublicKey{N: n, E: int(e)}, nil +} // end AdbPubKeyDecode() +/* + func RSA_public_decrypt(pubKey *rsa.PublicKey, data []byte) []byte { + // https://stackoverflow.com/a/44853488/12857692 + c := new(big.Int) + m := new(big.Int) + m.SetBytes(data) + e := big.NewInt(int64(pubKey.E)) + c.Exp(m, e, pubKey.N) + out := c.Bytes() + skip := 0 + for i := 2; i < len(out); i++ { + if i+1 >= len(out) { + break + } + if out[i] == 0xff && out[i+1] == 0 { + skip = i + 2 + break + } + } + return out[skip:] + } +*/ +func publicKeyToPEM(pubkey *rsa.PublicKey) ([]byte, error) { + derBytes, _ := x509.MarshalPKIXPublicKey(pubkey) + pemBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: derBytes, + } + pemBytes := pem.EncodeToMemory(pemBlock) + return pemBytes, nil +} // end publicKeyToPEM() + +func PubKeyValidate(publicKey *rsa.PublicKey, digest, signature []byte) (bool, error) { + pem, _ := publicKeyToPEM(publicKey) + nVerify := C.rsa_public_verify((*C.uchar)(unsafe.Pointer(&digest[0])), (*C.uchar)(unsafe.Pointer(&signature[0])), (*C.uchar)(unsafe.Pointer(&pem[0]))) + if nVerify != 0 { + return false, fmt.Errorf("%s", C.GoString(&C.last_error_string[0])) + } // end if + return true, nil +} // end PubKeyValidate() + +/* +References: +https://github.com/MuntashirAkon/libadb-android/blob/master/libadb/src/main/java/io/github/muntashirakon/adb/AndroidPubkey.java +*/ diff --git a/src/adb/tcpusb.go b/src/adb/tcpusb.go index 7654fab..57a8f46 100644 --- a/src/adb/tcpusb.go +++ b/src/adb/tcpusb.go @@ -12,8 +12,11 @@ import ( "sync" "github.com/pkg/errors" + "github.com/spf13/viper" ) +var A_VERSION uint32 = swapUint32(1) + /** * @link https://github.com/codeskyblue/fa */ @@ -21,6 +24,7 @@ import ( // Session created when adb connected type Session struct { // adbSession device *Device + devProps *viper.Viper conn net.Conn signature []byte err error @@ -29,6 +33,7 @@ type Session struct { // adbSession maxPayload uint32 remoteAddress string services map[uint32]*TransportService + authorized bool mu sync.Mutex tmpLocalIdLock sync.Mutex @@ -40,15 +45,18 @@ func NewSession(conn net.Conn, device *Device) *Session { token := make([]byte, TOKEN_LENGTH) rand.Read(token) log.Println("Create challenge", base64.StdEncoding.EncodeToString(token)) - - return &Session{ + devProps := viper.New() + sess := Session{ device: device, + devProps: devProps, conn: conn, token: token, version: 1, remoteAddress: conn.RemoteAddr().String(), services: make(map[uint32]*TransportService), } + sess.refreshPropertyCache() + return &sess } func (s *Session) nextLocalId() uint32 { @@ -103,38 +111,53 @@ func (s *Session) Serve() { log.Println("session closed") } +func (sess *Session) send_connect() { + connProps := make([]string, 0, 3) + for _, propName := range []string{ + "ro.product.name", + "ro.product.model", + "ro.product.device", + } { + strPropVal := "" + if sess.devProps.IsSet(propName) { + strPropVal = sess.devProps.GetString(propName) + } // end if + connProps = append(connProps, fmt.Sprintf("%s=%s", propName, strPropVal)) + } // end for + connProps = append(connProps, "features=shell_v2,cmd") + payload := fmt.Sprintf("device::%s", strings.Join(connProps, ";")) + sess.err = sess.writePacket(_CNXN, A_VERSION, sess.maxPayload, []byte(payload)) + //Packet{_CNXN, A_VERSION, sess.maxPayload, []byte(payload)}.DumpToStdout() +} // end send_connect() + +func (sess *Session) send_auth_request() { + sess.err = sess.writePacket(_AUTH, AUTH_TOKEN, 0, sess.token) +} // end send_auth_request() + func (sess *Session) onConnection(pkt Packet) { + auth_required := false + kSecureAdb := "ro.adb.secure" + if sess.devProps.IsSet(kSecureAdb) { + auth_required = sess.devProps.GetBool(kSecureAdb) + } // end if + if !auth_required { + log.Println("Authentication not required") + sess.send_connect() + sess.authVerified() + return + } // end if sess.version = pkt.Arg0 - log.Printf("Version: %x", pkt.Arg0) maxPayload := pkt.Arg1 - // log.Println("MaxPayload:", maxPayload) if maxPayload > 0xFFFF { // UINT16_MAX maxPayload = 0xFFFF } sess.maxPayload = maxPayload - // log.Println("MaxPayload:", maxPayload) - sess.err = sess.writePacket(_AUTH, AUTH_TOKEN, 0, sess.token) - // pkt.DumpToStdout() + log.Printf("version: %x, maxPayload: %d", sess.version, sess.maxPayload) + sess.send_auth_request() } func (sess *Session) authVerified() { - version := swapUint32(1) - // FIXME(ssx): need device.Properties() - props, _ := sess.device.Properties() - connProps := make([]string, 0, 3) - for _, propName := range []string{ - "ro.product.name", - "ro.product.model", - "ro.product.device", - } { - connProps = append(connProps, fmt.Sprintf("%s=%s", propName, props[propName])) - } - // connProps = append(connProps, "features=cmd") //,stat_v2,shell_v2") - deviceBanner := "device" - payload := fmt.Sprintf("%s::%s", deviceBanner, strings.Join(connProps, ";")) - // id := "device::;;\x00" - sess.err = sess.writePacket(_CNXN, version, sess.maxPayload, []byte(payload)) - Packet{_CNXN, sess.version, sess.maxPayload, []byte(payload)}.DumpToStdout() + sess.authorized = true } func (sess *Session) onAuth(pkt Packet) { @@ -147,30 +170,50 @@ func (sess *Session) onAuth(pkt Packet) { // If no rsa pubkey, then send AUTH to request it // Check signature again and send CNXN if passed log.Printf("Receive signature: %s", base64.StdEncoding.EncodeToString(pkt.Body)) - // sess.err = sess.writePacket(_AUTH, AUTH_TOKEN, 0, sess.token) - sess.authVerified() + sess.send_connect() // workaround to this problem (https://github.com/openstf/stf/issues/973) + sess.send_auth_request() case AUTH_RSAPUBLICKEY: if sess.signature == nil { sess.err = errors.New("Public key sent before signature") return } + if len(pkt.Body) <= 0 { + sess.err = errors.New("empty RSA public key") + return + } // end if log.Printf("Receive public key: %s", pkt.Body) - // TODO(ssx): parse public key from body and verify signature - // pkt.DumpToStdout() - log.Println("receive RSA PublicKey") - // pkt.DumpToStdout() - // send deviceId - // time.Sleep(10 * time.Second) - // sess.err = errors.New("retry") - // adb 1.0.40 will show "failed to authenticate to x.x.x.x:5555" - // but actually connected. - // sess.authVerified() + rawPubKey, errPubKey := pkt.ExtractADBRSAPublicKeyFromPayload() + if errPubKey != nil { + sess.err = errPubKey + return + } // end if + pubKey, errDec := AdbPubKeyDecode(rawPubKey) + if errDec != nil { + sess.err = errDec + return + } // end if + bVerified, errVerify := PubKeyValidate(pubKey, sess.token, sess.signature) + if errVerify != nil { + sess.err = errVerify + return + } // end if + if !bVerified { + sess.err = errors.New("signature mismatch") + return + } // end if + log.Println("Signature verified") + sess.send_connect() + sess.authVerified() default: sess.err = fmt.Errorf("unknown authentication method: %d", pkt.Arg0) } } func (sess *Session) onOpen(pkt Packet) { + if !sess.authorized { + sess.err = errors.New("unauthorized") + return + } // end if remoteId := pkt.Arg0 localId := sess.nextLocalId() if len(pkt.Body) < 2 { @@ -195,6 +238,10 @@ func (sess *Session) onOpen(pkt Packet) { } func (sess *Session) forwardServicePacket(pkt Packet) { + if !sess.authorized { + sess.err = errors.New("unauthorized") + return + } // end if sess.mu.Lock() service, ok := sess.services[pkt.Arg1] // localId sess.mu.Unlock() @@ -205,6 +252,18 @@ func (sess *Session) forwardServicePacket(pkt Packet) { service.handle(pkt) } +func (sess *Session) refreshPropertyCache() { + props, _ := sess.device.Properties() + if props == nil { + return + } // end if + conf := viper.New() + for k, v := range props { + conf.Set(k, string(v)) + } // end for + (*sess).devProps = conf +} // end refreshPropertyCache() + type TransportService struct { sess *Session device *Device