-
-
Notifications
You must be signed in to change notification settings - Fork 197
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add device pairing with Trust Pop Up (#27)
* copy over code from go-ios-old * fix buid package * fix pair and pair_save, add close method * pairing works now * fix console output * delete pair_read.go * add formatting to log.Info, add comment to pair function, fix formatting directive
- Loading branch information
1 parent
992c060
commit a805086
Showing
6 changed files
with
424 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package ios | ||
|
||
import ( | ||
"bytes" | ||
|
||
plist "howett.net/plist" | ||
) | ||
|
||
type readBuid struct { | ||
BundleID string | ||
ClientVersionString string | ||
MessageType string | ||
ProgName string | ||
LibUSBMuxVersion uint32 `plist:"kLibUSBMuxVersion"` | ||
} | ||
|
||
type readBuidResponse struct { | ||
BUID string | ||
} | ||
|
||
func newReadBuid() *readBuid { | ||
data := &readBuid{ | ||
BundleID: "go.ios.control", | ||
ClientVersionString: "go-usbmux-0.0.1", | ||
MessageType: "ReadBUID", | ||
ProgName: "go-usbmux", | ||
LibUSBMuxVersion: 3, | ||
} | ||
return data | ||
} | ||
|
||
func readBuidResponsefromBytes(plistBytes []byte) readBuidResponse { | ||
decoder := plist.NewDecoder(bytes.NewReader(plistBytes)) | ||
var data readBuidResponse | ||
_ = decoder.Decode(&data) | ||
return data | ||
} | ||
|
||
//ReadBuid requests the BUID of the host | ||
//It returns the deserialized BUID as a string. | ||
func (muxConn *UsbMuxConnection) ReadBuid() (string, error) { | ||
muxConn.Send(newReadBuid()) | ||
resp, err := muxConn.ReadMessage() | ||
if err != nil { | ||
return "", err | ||
} | ||
buidResponse := readBuidResponsefromBytes(resp.Payload) | ||
return buidResponse.BUID, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package ios | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/sha1" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/asn1" | ||
"encoding/pem" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
"regexp" | ||
"strings" | ||
"time" | ||
) | ||
|
||
//This code could be a little nicer | ||
func createRootCertificate(publicKeyBytes []byte) ([]byte, []byte, []byte, []byte, []byte, error) { | ||
reader := rand.Reader | ||
bitSize := 2048 | ||
|
||
rootKeyPair, err := rsa.GenerateKey(reader, bitSize) | ||
if err != nil { | ||
return nil, nil, nil, nil, nil, err | ||
} | ||
var b big.Int | ||
b.SetInt64(0) | ||
rootCertTemplate := x509.Certificate{ | ||
SerialNumber: &b, | ||
Subject: pkix.Name{}, | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(10, 0, 0), | ||
SignatureAlgorithm: x509.SHA1WithRSA, | ||
BasicConstraintsValid: true, | ||
IsCA: true, | ||
} | ||
|
||
digestString, _ := computeSKIKey(&rootKeyPair.PublicKey) | ||
|
||
rootCertTemplate.ExtraExtensions = []pkix.Extension{ | ||
{ | ||
Id: []int{2, 5, 29, 14}, | ||
Value: []byte(digestString), | ||
}} | ||
|
||
rootCert, err := x509.CreateCertificate(rand.Reader, &rootCertTemplate, &rootCertTemplate, &rootKeyPair.PublicKey, rootKeyPair) | ||
if err != nil { | ||
return nil, nil, nil, nil, nil, err | ||
} | ||
|
||
hostCertTemplate := x509.Certificate{ | ||
SerialNumber: &b, | ||
Subject: pkix.Name{}, | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(10, 0, 0), | ||
SignatureAlgorithm: x509.SHA1WithRSA, | ||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | ||
//ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||
BasicConstraintsValid: true, | ||
IsCA: false, | ||
} | ||
|
||
hostCertTemplate.ExtraExtensions = []pkix.Extension{ | ||
{ | ||
Id: []int{2, 5, 29, 14}, | ||
Value: (digestString), | ||
}} | ||
block, _ := pem.Decode([]byte(publicKeyBytes)) | ||
|
||
if block == nil { | ||
return nil, nil, nil, nil, nil, errors.New("failed to parse PEM block containing the public key") | ||
} | ||
|
||
var devicePublicKey rsa.PublicKey | ||
_, err1 := asn1.Unmarshal(block.Bytes, &devicePublicKey) | ||
if err1 != nil { | ||
return nil, nil, nil, nil, nil, err1 | ||
} | ||
|
||
deviceCertTemplate := x509.Certificate{ | ||
SerialNumber: &b, | ||
Subject: pkix.Name{}, | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().AddDate(10, 0, 0), | ||
SignatureAlgorithm: x509.SHA1WithRSA, | ||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | ||
BasicConstraintsValid: true, | ||
IsCA: false, | ||
} | ||
digestString2, _ := computeSKIKey(&devicePublicKey) | ||
deviceCertTemplate.ExtraExtensions = []pkix.Extension{ | ||
{ | ||
Id: []int{2, 5, 29, 14}, | ||
Value: (digestString2), | ||
}} | ||
|
||
hostCert, err := x509.CreateCertificate(rand.Reader, &hostCertTemplate, &hostCertTemplate, &rootKeyPair.PublicKey, rootKeyPair) | ||
if err != nil { | ||
return nil, nil, nil, nil, nil, err | ||
} | ||
|
||
deviceCert, err := x509.CreateCertificate(rand.Reader, &deviceCertTemplate, &deviceCertTemplate, &devicePublicKey, rootKeyPair) | ||
if err != nil { | ||
return nil, nil, nil, nil, nil, err | ||
} | ||
return certBytesToPEM(rootCert), certBytesToPEM(hostCert), certBytesToPEM(deviceCert), savePEMKey(rootKeyPair), savePEMKey(rootKeyPair), nil | ||
|
||
} | ||
|
||
func computeSKIKey(pub *rsa.PublicKey) ([]byte, error) { | ||
|
||
encodedPub, err := x509.MarshalPKIXPublicKey(pub) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var subPKI subjectPublicKeyInfo | ||
_, err = asn1.Unmarshal(encodedPub, &subPKI) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes) | ||
|
||
digestString := toHexString(pubHash[:]) | ||
|
||
return []byte(digestString), nil | ||
} | ||
|
||
func toHexString(bytes []byte) string { | ||
digestString := fmt.Sprintf("%x", bytes) | ||
if len(digestString)%2 == 1 { | ||
digestString = "0" + digestString | ||
} | ||
re := regexp.MustCompile("..") | ||
digestString = strings.TrimRight(re.ReplaceAllString(digestString, "$0:"), ":") | ||
digestString = strings.ToUpper(digestString) | ||
return digestString | ||
} | ||
|
||
type subjectPublicKeyInfo struct { | ||
Algorithm pkix.AlgorithmIdentifier | ||
SubjectPublicKey asn1.BitString | ||
} | ||
|
||
func certBytesToPEM(certBytes []byte) []byte { | ||
pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) | ||
return pemCert | ||
} | ||
|
||
func savePEMKey(key *rsa.PrivateKey) []byte { | ||
var privateKey = &pem.Block{ | ||
Type: "PRIVATE KEY", | ||
Bytes: x509.MarshalPKCS1PrivateKey(key), | ||
} | ||
return pem.EncodeToMemory(privateKey) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package ios | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/google/uuid" | ||
plist "howett.net/plist" | ||
) | ||
|
||
//Pair tries to pair with a device. The first time usually | ||
//fails because the user has to accept a trust pop up on the iOS device. | ||
// What you have to do to pair is: | ||
// 1. run the Pair() function | ||
// 2. accept the trust pop up on the device | ||
// 3. run the Pair() function a second time | ||
func Pair(device DeviceEntry) error { | ||
usbmuxConn, err := NewUsbMuxConnectionSimple() | ||
if err != nil { | ||
return err | ||
} | ||
defer usbmuxConn.Close() | ||
buid, err := usbmuxConn.ReadBuid() | ||
if err != nil { | ||
return err | ||
} | ||
lockdown, err := usbmuxConn.ConnectLockdown(device.DeviceID) | ||
if err != nil { | ||
return err | ||
} | ||
publicKey, err := lockdown.GetValue("DevicePublicKey") | ||
if err != nil { | ||
return err | ||
} | ||
wifiMac, err := lockdown.GetValue("WiFiAddress") | ||
if err != nil { | ||
return err | ||
} | ||
rootCert, hostCert, deviceCert, rootPrivateKey, hostPrivateKey, err := createRootCertificate(publicKey.([]byte)) | ||
if err != nil { | ||
return fmt.Errorf("Failed creating pair record with error: %v", err) | ||
} | ||
|
||
pairRecordData := newFullPairRecordData(buid, hostCert, rootCert, deviceCert) | ||
request := newLockDownPairRequest(pairRecordData) | ||
|
||
err = lockdown.Send(request) | ||
if err != nil { | ||
return err | ||
} | ||
resp, err := lockdown.ReadMessage() | ||
if err != nil { | ||
return err | ||
} | ||
response := getLockdownPairResponsefromBytes(resp) | ||
if isPairingDialogOpen(response) { | ||
return fmt.Errorf("Please accept the PairingDialog on the device and run pairing again!") | ||
} | ||
if response.Error != "" { | ||
return fmt.Errorf("Lockdown error: %s", response.Error) | ||
} | ||
usbmuxConn, err = NewUsbMuxConnectionSimple() | ||
defer usbmuxConn.Close() | ||
if err != nil { | ||
return err | ||
} | ||
success, err := usbmuxConn.savePair(device.Properties.SerialNumber, deviceCert, hostPrivateKey, hostCert, rootPrivateKey, rootCert, response.EscrowBag, wifiMac.(string), pairRecordData.HostID, buid) | ||
if !success || err != nil { | ||
return errors.New("Saving the PairRecord to usbmux failed") | ||
} | ||
return nil | ||
} | ||
|
||
type FullPairRecordData struct { | ||
DeviceCertificate []byte | ||
HostCertificate []byte | ||
RootCertificate []byte | ||
SystemBUID string | ||
HostID string | ||
} | ||
|
||
type PairingOptions struct { | ||
ExtendedPairingErrors bool | ||
} | ||
|
||
type LockDownPairRequest struct { | ||
Label string | ||
PairRecord FullPairRecordData | ||
Request string | ||
ProtocolVersion string | ||
PairingOptions PairingOptions | ||
} | ||
|
||
type LockdownPairResponse struct { | ||
Error string | ||
Request string | ||
EscrowBag []byte | ||
} | ||
|
||
func getLockdownPairResponsefromBytes(plistBytes []byte) *LockdownPairResponse { | ||
decoder := plist.NewDecoder(bytes.NewReader(plistBytes)) | ||
var data LockdownPairResponse | ||
_ = decoder.Decode(&data) | ||
return &data | ||
} | ||
|
||
func isPairingDialogOpen(resp *LockdownPairResponse) bool { | ||
return resp.Error == "PairingDialogResponsePending" | ||
} | ||
|
||
func newLockDownPairRequest(pairRecord FullPairRecordData) LockDownPairRequest { | ||
var req LockDownPairRequest | ||
req.Label = "go-ios" | ||
req.PairingOptions = PairingOptions{true} | ||
req.Request = "Pair" | ||
req.ProtocolVersion = "2" | ||
req.PairRecord = (pairRecord) | ||
return req | ||
} | ||
|
||
func newFullPairRecordData(systemBuid string, hostCert []byte, rootCert []byte, deviceCert []byte) FullPairRecordData { | ||
var data FullPairRecordData | ||
data.SystemBUID = systemBuid | ||
data.HostID = strings.ToUpper(uuid.New().String()) | ||
data.RootCertificate = rootCert | ||
data.HostCertificate = hostCert | ||
data.DeviceCertificate = deviceCert | ||
|
||
return data | ||
} |
Oops, something went wrong.