Browse Source

move example application to github.com/f-secure-foundry/tamago-example

pull/5/head
Andrea Barisani 1 year ago
parent
commit
e8218f2796
  1. 10
      LICENSE
  2. 13
      README.md
  3. 1
      example/.gitignore
  4. 36
      example/Makefile
  5. 41
      example/README.md
  6. 183
      example/btcd.go
  7. 61
      example/dcp.go
  8. 45
      example/ecdsa.go
  9. 186
      example/example.go
  10. 98
      example/file.go
  11. 60
      example/mem.go
  12. 377
      example/usb_ethernet.go
  13. 204
      example/usb_zero.go
  14. 16
      example/web_profiling.go
  15. 159
      example/web_server.go
  16. 13
      go.mod
  17. 83
      go.sum
  18. 8
      usbarmory/README.md

10
LICENSE

@ -8,13 +8,3 @@ Foundation under version 3 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
The license does not apply to the following 3rd party bundled code (see
individual file headers for their copyright and license notices):
example/btcd.go
example/ecdsa.go
The TamaGo logo is adapted from the Go gopher designed by Renee French and
licensed under the Creative Commons 3.0 Attributions license. Go Gopher vector
illustration by Hugo Arganda.

13
README.md

@ -100,9 +100,7 @@ but with the addition of the following flags/variables, also make sure that the
required SoC and board packages are available in your `GOPATH`:
```
# USB armory Mk II example from the root directory of this repository
cd example &&
GO_EXTLINK_ENABLED=0 CGO_ENABLED=0 GOOS=tamago GOARM=7 GOARCH=arm \
GO_EXTLINK_ENABLED=0 CGO_ENABLED=0 GOOS=tamago GOARM=7 GOARCH=arm \
${TAMAGO} build -ldflags "-T 0x80010000 -E _rt0_arm_tamago -R 0x1000"
```
@ -112,11 +110,12 @@ Executing and debugging
See the respective board package README file for execution and debugging
information for each specific target (real or emulated).
An emulated run of the [example application](https://github.com/f-secure-foundry/tamago/tree/master/example)
An emulated run of the [example application](https://github.com/f-secure-foundry/tamago-example)
can be launched as follows:
```
make clean && make qemu
git clone https://github.com/f-secure-foundry/tamago-example
cd tamago-example && make qemu
```
License
@ -134,3 +133,7 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
See accompanying LICENSE file for full details.
The TamaGo logo is adapted from the Go gopher designed by Renee French and
licensed under the Creative Commons 3.0 Attributions license. Go Gopher vector
illustration by Hugo Arganda.

1
example/.gitignore

@ -1 +0,0 @@
example

36
example/Makefile

@ -1,36 +0,0 @@
# https://github.com/f-secure-foundry/tamago
#
# Copyright (c) F-Secure Corporation
# https://foundry.f-secure.com
#
# Use of this source code is governed by the license
# that can be found in the LICENSE file.
APP := example
GOENV := GO_EXTLINK_ENABLED=0 CGO_ENABLED=0 GOOS=tamago GOARM=7 GOARCH=arm
TEXT_START := 0x80010000 # ramStart (defined in imx6/imx6ul/memory.go) + 0x10000
GOFLAGS := -ldflags "-T $(TEXT_START) -E _rt0_arm_tamago -R 0x1000"
QEMU ?= qemu-system-arm -machine mcimx6ul-evk -cpu cortex-a7 -m 512M \
-nographic -monitor none -serial null -serial stdio -net none \
-semihosting -d unimp
.PHONY: clean qemu qemu-gdb
all: $(APP)
$(APP):
@if [ "${TAMAGO}" == "" ] || [ ! -f "${TAMAGO}" ]; then \
echo 'You need to set the TAMAGO variable to a compiled version of https://github.com/f-secure-foundry/tamago-go'; \
exit 1; \
fi
$(GOENV) $(TAMAGO) build $(GOFLAGS) -o ${APP}
qemu: $(APP)
$(QEMU) -kernel $(APP)
qemu-gdb: $(APP)
$(QEMU) -kernel $(APP) -S -s
clean:
rm -f $(APP)

41
example/README.md

@ -1,40 +1 @@
TamaGo example application
==========================
This example Go application is part of the
[TamaGo](https://github.com/f-secure-foundry/tamago) project and targets the NXP
i.MX6ULZ SoC of the USB armory Mk II.
The example application performs a variety of simple test procedures each in
its separate goroutine:
1. Directory and file writt/read from an in-memory filesystem.
2. Timer operation.
3. Sleep operation.
4. Random bytes collection (gathered from SoC TRNG on non-emulated runs).
5. ECDSA signing and verification.
6. Test BTC transaction creation and signing.
7. Key derivation with DCP HSM (only on non-emulated runs).
8. Large memory allocation.
Once all tests are completed, and only on non-emulated hardware, the following
network services are started on Ethernet over USB (ECM protocol, only supported
on Linux hosts).
* UDP echo server on 10.0.0.1:1234
* HTTP server on 10.0.0.1:80
* HTTPS server on 10.0.0.1:443
The HTTP/HTTPS servers expose the following routes:
* `/`: a welcome message
* `/dir`: in-memory filesystem
* `/debug/pprof`: Go runtime profiling data through [pprof](https://golang.org/pkg/net/http/pprof/)
* `/debug/charts`: Go runtime profiling data through [debugchargs](https://github.com/mkevac/debugcharts)
Moved to [https://github.com/f-secure-foundry/tamago-example](https://github.com/f-secure-foundry/tamago-example).

183
example/btcd.go

@ -1,183 +0,0 @@
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE at
// github.com/btcsuite/btcd
package main
import (
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// This example demonstrates creating a script which pays to a bitcoin address.
// It also prints the created script hex and uses the DisasmString function to
// display the disassembled script.
func ExamplePayToAddrScript() {
// Parse the address to send the coins to into a btcutil.Address
// which is useful to ensure the accuracy of the address and determine
// the address type. It is also required for the upcoming call to
// PayToAddrScript.
addressStr := "12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV"
address, err := btcutil.DecodeAddress(addressStr, &chaincfg.MainNetParams)
if err != nil {
fmt.Println(err)
return
}
// Create a public key script that pays to the address.
script, err := txscript.PayToAddrScript(address)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Script Hex: %x\n", script)
disasm, err := txscript.DisasmString(script)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Script Disassembly:", disasm)
// Output:
// Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac
// Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG
}
// This example demonstrates extracting information from a standard public key
// script.
func ExampleExtractPkScriptAddrs() {
// Start with a standard pay-to-pubkey-hash script.
scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac"
script, err := hex.DecodeString(scriptHex)
if err != nil {
fmt.Println(err)
return
}
// Extract and print details from the script.
scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs(
script, &chaincfg.MainNetParams)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Script Class:", scriptClass)
fmt.Println("Addresses:", addresses)
fmt.Println("Required Signatures:", reqSigs)
// Output:
// Script Class: pubkeyhash
// Addresses: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV]
// Required Signatures: 1
}
// This example demonstrates manually creating and signing a redeem transaction.
func ExampleSignTxOutput() {
// Ordinarily the private key would come from whatever storage mechanism
// is being used, but for this example just hard code it.
privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" +
"d4f8720ee63e502ee2869afab7de234b80c")
if err != nil {
fmt.Println(err)
return
}
privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash,
&chaincfg.MainNetParams)
if err != nil {
fmt.Println(err)
return
}
// For this example, create a fake transaction that represents what
// would ordinarily be the real transaction that is being spent. It
// contains a single output that pays to address in the amount of 1 BTC.
originTx := wire.NewMsgTx(wire.TxVersion)
prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0))
txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil)
originTx.AddTxIn(txIn)
pkScript, err := txscript.PayToAddrScript(addr)
if err != nil {
fmt.Println(err)
return
}
txOut := wire.NewTxOut(100000000, pkScript)
originTx.AddTxOut(txOut)
originTxHash := originTx.TxHash()
// Create the transaction to redeem the fake transaction.
redeemTx := wire.NewMsgTx(wire.TxVersion)
// Add the input(s) the redeeming transaction will spend. There is no
// signature script at this point since it hasn't been created or signed
// yet, hence nil is provided for it.
prevOut = wire.NewOutPoint(&originTxHash, 0)
txIn = wire.NewTxIn(prevOut, nil, nil)
redeemTx.AddTxIn(txIn)
// Ordinarily this would contain that actual destination of the funds,
// but for this example don't bother.
txOut = wire.NewTxOut(0, nil)
redeemTx.AddTxOut(txOut)
// Sign the redeeming transaction.
lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) {
// Ordinarily this function would involve looking up the private
// key for the provided address, but since the only thing being
// signed in this example uses the address associated with the
// private key from above, simply return it with the compressed
// flag set since the address is using the associated compressed
// public key.
//
// NOTE: If you want to prove the code is actually signing the
// transaction properly, uncomment the following line which
// intentionally returns an invalid key to sign with, which in
// turn will result in a failure during the script execution
// when verifying the signature.
//
// privKey.D.SetInt64(12345)
//
return privKey, true, nil
}
// Notice that the script database parameter is nil here since it isn't
// used. It must be specified when pay-to-script-hash transactions are
// being signed.
sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams,
redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll,
txscript.KeyClosure(lookupKey), nil, nil)
if err != nil {
fmt.Println(err)
return
}
redeemTx.TxIn[0].SignatureScript = sigScript
// Prove that the transaction has been validly signed by executing the
// script pair.
flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures |
txscript.ScriptStrictMultiSig |
txscript.ScriptDiscourageUpgradableNops
vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0,
flags, nil, nil, -1)
if err != nil {
fmt.Println(err)
return
}
if err := vm.Execute(); err != nil {
fmt.Println(err)
return
}
fmt.Println("Transaction successfully signed")
// Output:
// Transaction successfully signed
}

61
example/dcp.go

@ -1,61 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
"crypto/aes"
"fmt"
"strings"
"github.com/f-secure-foundry/tamago/imx6"
)
const testVector = "\x75\xf9\x02\x2d\x5a\x86\x7a\xd4\x30\x44\x0f\xee\xc6\x61\x1f\x0a"
const zeroVector = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
func testKeyDerivation() (err error) {
diversifier := []byte{0xde, 0xad, 0xbe, 0xef}
iv := make([]byte, aes.BlockSize)
key, err := imx6.DCP.DeriveKey(diversifier, iv)
if err != nil {
return
} else {
if strings.Compare(string(key), zeroVector) == 0 {
err = fmt.Errorf("derivedKey all zeros!\n")
return
}
// if the SoC is secure booted we can only print the result
if imx6.DCP.SNVS() {
fmt.Printf("imx6_dcp: derived SNVS key %x\n", key)
return
}
if strings.Compare(string(key), testVector) != 0 {
err = fmt.Errorf("derivedKey:%x != testVector:%x\n", key, testVector)
return
} else {
fmt.Printf("imx6_dcp: derived test key %x\n", key)
}
}
return
}
func TestDCP() {
imx6.DCP.Init()
if err := testKeyDerivation(); err != nil {
fmt.Printf("imx6_dcp: error, %v\n", err)
}
}

45
example/ecdsa.go

@ -1,45 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Adapted from go/src/crypto/ecdsa/ecdsa_test.go
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"time"
)
func testSignAndVerify(c elliptic.Curve, tag string) {
start := time.Now()
fmt.Printf("ECDSA sign and verify with p%d ... ", c.Params().BitSize)
priv, _ := ecdsa.GenerateKey(c, rand.Reader)
hashed := []byte("testing")
r, s, err := ecdsa.Sign(rand.Reader, priv, hashed)
if err != nil {
fmt.Printf("%s: error signing: %s", tag, err)
return
}
if !ecdsa.Verify(&priv.PublicKey, hashed, r, s) {
fmt.Printf("%s: Verify failed", tag)
}
hashed[0] ^= 0xff
if ecdsa.Verify(&priv.PublicKey, hashed, r, s) {
fmt.Printf("%s: Verify always works!", tag)
}
fmt.Printf("done (%s)\n", time.Since(start))
}
func TestSignAndVerify() {
testSignAndVerify(elliptic.P224(), "p224")
testSignAndVerify(elliptic.P256(), "p256")
}

186
example/example.go

@ -1,186 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
// Basic test example for tamago/arm running on the USB armory Mk II.
package main
import (
"crypto/rand"
"fmt"
"io/ioutil"
"log"
"math"
"math/big"
mathrand "math/rand"
"os"
"time"
"github.com/f-secure-foundry/tamago/imx6"
_ "github.com/f-secure-foundry/tamago/usbarmory/mark-two"
)
const banner = "Hello from tamago/arm!"
const verbose = true
var exit chan bool
func init() {
log.SetFlags(0)
// imx6 package debugging
if verbose {
log.SetOutput(os.Stdout)
} else {
log.SetOutput(ioutil.Discard)
}
model := imx6.Model()
_, family, revMajor, revMinor := imx6.SiliconVersion()
if !imx6.Native {
return
}
if err := imx6.SetARMFreq(900000000); err != nil {
fmt.Printf("WARNING: error setting ARM frequency: %v\n", err)
}
fmt.Printf("imx6_soc: %#s (%#x, %d.%d) @ freq:%d MHz - native:%v\n",
model, family, revMajor, revMinor, imx6.ARMFreq()/1000000, imx6.Native)
}
func main() {
start := time.Now()
exit = make(chan bool)
n := 0
fmt.Println("-- main --------------------------------------------------------------")
fmt.Printf("%s (epoch %d)\n", banner, start.UnixNano())
n += 1
go func() {
fmt.Println("-- fs ----------------------------------------------------------------")
TestFile()
TestDir()
exit <- true
}()
sleep := 100 * time.Millisecond
n += 1
go func() {
fmt.Println("-- timer -------------------------------------------------------------")
t := time.NewTimer(sleep)
fmt.Printf("waking up timer after %v\n", sleep)
start := time.Now()
for now := range t.C {
fmt.Printf("woke up at %d (%v)\n", now.Nanosecond(), now.Sub(start))
break
}
exit <- true
}()
n += 1
go func() {
fmt.Println("-- sleep -------------------------------------------------------------")
fmt.Printf("sleeping %s\n", sleep)
start := time.Now()
time.Sleep(sleep)
fmt.Printf("slept %s (%v)\n", sleep, time.Now().Sub(start))
exit <- true
}()
n += 1
go func() {
fmt.Println("-- rng ---------------------------------------------------------------")
size := 32
for i := 0; i < 10; i++ {
rng := make([]byte, size)
rand.Read(rng)
fmt.Printf("%x\n", rng)
}
count := 1000
start := time.Now()
for i := 0; i < count; i++ {
rng := make([]byte, size)
rand.Read(rng)
}
fmt.Printf("retrieved %d random bytes in %s\n", size*count, time.Since(start))
seed, _ := rand.Int(rand.Reader, big.NewInt(int64(math.MaxInt64)))
mathrand.Seed(seed.Int64())
exit <- true
}()
n += 1
go func() {
fmt.Println("-- ecdsa -------------------------------------------------------------")
TestSignAndVerify()
exit <- true
}()
n += 1
go func() {
fmt.Println("-- btc ---------------------------------------------------------------")
ExamplePayToAddrScript()
ExampleExtractPkScriptAddrs()
ExampleSignTxOutput()
exit <- true
}()
if imx6.Native && imx6.Family == imx6.IMX6ULL {
n += 1
go func() {
fmt.Println("-- i.mx6 dcp ---------------------------------------------------------")
TestDCP()
exit <- true
}()
}
fmt.Printf("launched %d test goroutines\n", n)
for i := 1; i <= n; i++ {
<-exit
}
fmt.Printf("----------------------------------------------------------------------\n")
fmt.Printf("completed %d goroutines (%s)\n", n, time.Since(start))
runs := 9
chunksMax := 50
fillSize := 160 * 1024 * 1024
chunks := mathrand.Intn(chunksMax) + 1
chunkSize := fillSize / chunks
fmt.Printf("-- memory allocation (%d runs) ----------------------------------------\n", runs)
testAlloc(runs, chunks, chunkSize)
if imx6.Native && (imx6.Family == imx6.IMX6UL || imx6.Family == imx6.IMX6ULL) {
fmt.Println("-- i.mx6 usb ---------------------------------------------------------")
StartUSBEthernet()
}
fmt.Printf("Goodbye from tamago/arm (%s)\n", time.Since(start))
}

98
example/file.go

@ -1,98 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func TestFile() {
var err error
defer func() {
if err != nil {
fmt.Printf("TestFile error: %v\n", err)
}
}()
dirPath := "/dir"
fileName := "tamago.txt"
path := filepath.Join(dirPath, fileName)
fmt.Printf("writing %d bytes to %s\n", len(banner), path)
err = os.MkdirAll(dirPath, 0700)
if err != nil {
return
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
_, err = file.WriteString(banner)
if err != nil {
panic(err)
}
file.Close()
read, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
if strings.Compare(banner, string(read)) != 0 {
fmt.Println("TestFile: comparison fail")
} else {
fmt.Printf("read %s (%d bytes)\n", path, len(read))
}
}
func TestDir() {
dirPath := "/dir"
fmt.Printf("listing directory %s\n", dirPath)
f, err := os.Open(dirPath)
if err != nil {
panic(err)
}
d, err := f.Stat()
if err != nil {
panic(err)
}
if !d.IsDir() {
panic("expected directory")
}
files, err := f.Readdir(-1)
if err != nil {
panic(err)
}
for _, i := range files {
fmt.Printf("%s/%s (%d bytes)\n", dirPath, i.Name(), i.Size())
}
}

60
example/mem.go

@ -1,60 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
"fmt"
"runtime"
)
func testAlloc(runs int, chunks int, chunkSize int) {
var memstats runtime.MemStats
// Instead of forcing runtime.GC() as shown in the loop, gcpercent can
// be tuned to a value sufficiently low to prevent the next GC target
// being set beyond the end of available RAM. A lower than default
// (100) value (such as 80 for this example) triggers GC more
// frequently and avoids forced GC runs.
//
// This is not something unique to `GOOS=tamago` but more evident as,
// when running on bare metal, there is no swap or OS virtual memory.
//
//gcpercent := 80
//fmt.Printf("setting garbage collection target: %d\n", gcpercent)
//debug.SetGCPercent(gcpercent)
for run := 1; run <= runs; run++ {
fmt.Printf("allocating %d * %d MB chunks (%d/%d) ", chunks, chunkSize/(1024*1024), run, runs)
mem := make([][]byte, chunks)
for i := 0; i <= chunks-1; i++ {
fmt.Printf(".")
mem[i] = make([]byte, chunkSize)
}
fmt.Printf("\n")
// When getting close to the end of available RAM, the next GC
// target might be set beyond it. Therfore in this specific
// test it is best to force a GC run.
//
// This is not something unique to `GOOS=tamago` but more
// evident as when running bare metal we have no swap or OS
// virtual memory.
runtime.GC()
}
runtime.ReadMemStats(&memstats)
totalAllocated := uint64(runs) * uint64(chunks) * uint64(chunkSize)
fmt.Printf("%d MB allocated (Mallocs: %d Frees: %d HeapSys: %d NumGC:%d)\n",
totalAllocated/(1024*1024), memstats.Mallocs, memstats.Frees, memstats.HeapSys, memstats.NumGC)
}

377
example/usb_ethernet.go

@ -1,377 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
"encoding/binary"
"log"
"net"
"runtime"
"strings"
"github.com/f-secure-foundry/tamago/imx6/usb"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"gvisor.dev/gvisor/pkg/waiter"
)
const maxPacketSize = 512
const hostMAC = "1a:55:89:a2:69:42"
const deviceMAC = "1a:55:89:a2:69:41"
const IP = "10.0.0.1"
const MTU = 1500
// set to true to enable packet sniffing
const sniff = false
// populated by setupStack()
var hostMACBytes []byte
var deviceMACBytes []byte
var link *channel.Endpoint
// Ethernet frame buffers
var rx []byte
func configureEthernetDevice(device *usb.Device) {
// Supported Language Code Zero: English
device.SetLanguageCodes([]uint16{0x0409})
// device descriptor
device.Descriptor = &usb.DeviceDescriptor{}
device.Descriptor.SetDefaults()
device.Descriptor.DeviceClass = 0x2
device.Descriptor.VendorId = 0x0525
device.Descriptor.ProductId = 0xa4a2
device.Descriptor.Device = 0x0001
device.Descriptor.NumConfigurations = 1
iManufacturer, _ := device.AddString(`TamaGo`)
device.Descriptor.Manufacturer = iManufacturer
iProduct, _ := device.AddString(`RNDIS/Ethernet Gadget`)
device.Descriptor.Product = iProduct
iSerial, _ := device.AddString(`0.1`)
device.Descriptor.SerialNumber = iSerial
// device qualifier
device.Qualifier = &usb.DeviceQualifierDescriptor{}
device.Qualifier.SetDefaults()
device.Qualifier.DeviceClass = 2
device.Qualifier.NumConfigurations = 2
}
func configureECM(device *usb.Device) {
// source and sink configuration
conf := &usb.ConfigurationDescriptor{}
conf.SetDefaults()
conf.TotalLength = 71
conf.NumInterfaces = 1
conf.ConfigurationValue = 1
device.Configurations = append(device.Configurations, conf)
// CDC communication interface
iface := &usb.InterfaceDescriptor{}
iface.SetDefaults()
iface.NumEndpoints = 1
iface.InterfaceClass = 2
iface.InterfaceSubClass = 6
iInterface, _ := device.AddString(`CDC Ethernet Control Model (ECM)`)
iface.Interface = iInterface
header := &usb.CDCHeaderDescriptor{}
header.SetDefaults()
iface.ClassDescriptors = append(iface.ClassDescriptors, header.Bytes())
union := &usb.CDCUnionDescriptor{}
union.SetDefaults()
iface.ClassDescriptors = append(iface.ClassDescriptors, union.Bytes())
ethernet := &usb.CDCEthernetDescriptor{}
ethernet.SetDefaults()
iMacAddress, _ := device.AddString(strings.ReplaceAll(hostMAC, ":", ""))
ethernet.MacAddress = iMacAddress
iface.ClassDescriptors = append(iface.ClassDescriptors, ethernet.Bytes())
conf.Interfaces = append(conf.Interfaces, iface)
ep2IN := &usb.EndpointDescriptor{}
ep2IN.SetDefaults()
ep2IN.EndpointAddress = 0x82
ep2IN.Attributes = 3
ep2IN.MaxPacketSize = 16
ep2IN.Interval = 9
ep2IN.Function = ECMControl
iface.Endpoints = append(iface.Endpoints, ep2IN)
// CDC data interface
iface = &usb.InterfaceDescriptor{}
iface.SetDefaults()
iface.AlternateSetting = 1
iface.NumEndpoints = 2
iface.InterfaceClass = 10
iInterface, _ = device.AddString(`CDC Data`)
iface.Interface = iInterface
conf.Interfaces = append(conf.Interfaces, iface)
ep1IN := &usb.EndpointDescriptor{}
ep1IN.SetDefaults()
ep1IN.EndpointAddress = 0x81
ep1IN.Attributes = 2
ep1IN.MaxPacketSize = maxPacketSize
ep1IN.Function = ECMTx
iface.Endpoints = append(iface.Endpoints, ep1IN)
ep1OUT := &usb.EndpointDescriptor{}
ep1OUT.SetDefaults()
ep1OUT.EndpointAddress = 0x01
ep1OUT.Attributes = 2
ep1OUT.MaxPacketSize = maxPacketSize
ep1OUT.Function = ECMRx
iface.Endpoints = append(iface.Endpoints, ep1OUT)
}
func configureNetworkStack(addr tcpip.Address, nic tcpip.NICID, sniff bool) (s *stack.Stack) {
var err error
hostMACBytes, err = net.ParseMAC(hostMAC)
if err != nil {
log.Fatal(err)
}
deviceMACBytes, err = net.ParseMAC(deviceMAC)
if err != nil {
log.Fatal(err)
}
s = stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{
ipv4.NewProtocol(),
arp.NewProtocol()},
TransportProtocols: []stack.TransportProtocol{
tcp.NewProtocol(),
udp.NewProtocol(),
icmp.NewProtocol4()},
})
linkAddr, err := tcpip.ParseMACAddress(deviceMAC)
if err != nil {
log.Fatal(err)
}
link = channel.New(256, MTU, linkAddr)
linkEP := stack.LinkEndpoint(link)
if sniff {
linkEP = sniffer.New(linkEP)
}
if err := s.CreateNIC(nic, linkEP); err != nil {
log.Fatal(err)
}
if err := s.AddAddress(nic, arp.ProtocolNumber, arp.ProtocolAddress); err != nil {
log.Fatal(err)
}
if err := s.AddAddress(nic, ipv4.ProtocolNumber, addr); err != nil {
log.Fatal(err)
}
subnet, err := tcpip.NewSubnet("\x00\x00\x00\x00", "\x00\x00\x00\x00")
if err != nil {
log.Fatal(err)
}
s.SetRouteTable([]tcpip.Route{{
Destination: subnet,
NIC: nic,
}})
return
}
func startICMPEndpoint(s *stack.Stack, addr tcpip.Address, port uint16, nic tcpip.NICID) {
var wq waiter.Queue
fullAddr := tcpip.FullAddress{Addr: addr, Port: port, NIC: nic}
ep, err := s.NewEndpoint(icmp.ProtocolNumber4, ipv4.ProtocolNumber, &wq)
if err != nil {
log.Fatalf("endpoint error (icmp): %v\n", err)
}
if err := ep.Bind(fullAddr); err != nil {
log.Fatal("bind error (icmp endpoint): ", err)
}
}
func startUDPListener(s *stack.Stack, addr tcpip.Address, port uint16, nic tcpip.NICID) (conn *gonet.PacketConn) {
var err error
fullAddr := tcpip.FullAddress{Addr: addr, Port: port, NIC: nic}
conn, err = gonet.DialUDP(s, &fullAddr, nil, ipv4.ProtocolNumber)
if err != nil {
log.Fatal("listener error: ", err)
}
return
}
func startEchoServer(s *stack.Stack, addr tcpip.Address, port uint16, nic tcpip.NICID) {
c := startUDPListener(s, addr, port, nic)
for {
runtime.Gosched()
buf := make([]byte, 1024)
n, addr, err := c.ReadFrom(buf)
if err != nil {
log.Printf("udp recv error, %v\n", err)
continue
}
_, err = c.WriteTo(buf[0:n], addr)
if err != nil {
log.Printf("udp send error, %v\n", err)
}
}
}
// ECMControl implements the endpoint 2 IN function.
func ECMControl(_ []byte, lastErr error) (in []byte, err error) {
// ignore for now
return
}
// ECMTx implements the endpoint 1 IN function, used to transmit Ethernet
// packet from device to host.
func ECMTx(_ []byte, lastErr error) (in []byte, err error) {
select {
case info := <-link.C:
hdr := info.Pkt.Header.View()
payload := info.Pkt.Data.ToView()
proto := make([]byte, 2)
binary.BigEndian.PutUint16(proto, uint16(info.Proto))
// Ethernet frame header
in = append(in, hostMACBytes...)
in = append(in, deviceMACBytes...)
in = append(in, proto...)
// packet header
in = append(in, hdr...)
// payload
in = append(in, payload...)
default:
}
return
}
// ECMRx implements the endpoint 1 OUT function, used to receive ethernet
// packet from host to device.
func ECMRx(out []byte, lastErr error) (_ []byte, err error) {
if len(rx) == 0 && len(out) < 14 {
return
}
rx = append(rx, out...)
// more data expected or zero length packet
if len(out) == 512 {
return
}
hdr := buffer.NewViewFromBytes(rx[0:14])
proto := tcpip.NetworkProtocolNumber(binary.BigEndian.Uint16(rx[12:14]))
payload := buffer.NewViewFromBytes(rx[14:])
pkt := tcpip.PacketBuffer{
LinkHeader: hdr,
Data: payload.ToVectorisedView(),
}
link.InjectInbound(proto, pkt)
rx = []byte{}
return
}
// StartUSBEthernet starts an emulated Ethernet over USB device (ECM protocol,
// only supported on Linux hosts) with a test UDP echo service on port 1234.
func StartUSBEthernet() {
addr := tcpip.Address(net.ParseIP(IP)).To4()
s := configureNetworkStack(addr, 1, sniff)
// handle pings
startICMPEndpoint(s, addr, 0, 1)
go func() {
// UDP echo server
startEchoServer(s, addr, 1234, 1)
}()
// create index.html in in-memory filesystem
setupStaticWebAssets()
go func() {
// HTTP web server (see web_server.go)
startWebServer(s, addr, 80, 1, false)
}()
go func() {
// HTTPS web server (see web_server.go)
startWebServer(s, addr, 443, 1, true)
}()
device := &usb.Device{}
configureEthernetDevice(device)
configureECM(device)
usb.USB1.Init()
usb.USB1.DeviceMode()
usb.USB1.Reset()
// never returns
usb.USB1.Start(device)
}

204
example/usb_zero.go

@ -1,204 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
"fmt"
"github.com/f-secure-foundry/tamago/imx6/usb"
)
func configureZeroDevice(device *usb.Device) {
// Supported Language Code Zero: English
device.SetLanguageCodes([]uint16{0x0409})
// device descriptor
device.Descriptor = &usb.DeviceDescriptor{}
device.Descriptor.SetDefaults()
device.Descriptor.DeviceClass = 0xff
device.Descriptor.VendorId = 0x0525
device.Descriptor.ProductId = 0xa4a0
device.Descriptor.Device = 0x0001
device.Descriptor.NumConfigurations = 2
iManufacturer, _ := device.AddString(`TamaGo`)
device.Descriptor.Manufacturer = iManufacturer
iProduct, _ := device.AddString(`Gadget Zero`)
device.Descriptor.Product = iProduct
iSerial, _ := device.AddString(`0.1`)
device.Descriptor.SerialNumber = iSerial
// device qualifier
device.Qualifier = &usb.DeviceQualifierDescriptor{}
device.Qualifier.SetDefaults()
device.Qualifier.DeviceClass = 0xff
device.Qualifier.NumConfigurations = 2
}
func configureSourceSink(device *usb.Device) {
// source and sink configuration
conf := &usb.ConfigurationDescriptor{}
conf.SetDefaults()
conf.TotalLength = 32
conf.NumInterfaces = 1
conf.ConfigurationValue = 3
iConfiguration, _ := device.AddString(`source and sink data`)
conf.Configuration = iConfiguration
device.Configurations = append(device.Configurations, conf)
// source and sink interface
iface := &usb.InterfaceDescriptor{}
iface.SetDefaults()
iface.NumEndpoints = 2
iface.InterfaceClass = 0xff
iface.Interface = 0
conf.Interfaces = append(conf.Interfaces, iface)
// source EP1 IN endpoint (bulk)
ep1IN := &usb.EndpointDescriptor{}
ep1IN.SetDefaults()
ep1IN.EndpointAddress = 0x81
ep1IN.Attributes = 2
ep1IN.MaxPacketSize = 512
ep1IN.Function = source
iface.Endpoints = append(iface.Endpoints, ep1IN)
// sink EP1 OUT endpoint (bulk)
ep1OUT := &usb.EndpointDescriptor{}
ep1OUT.SetDefaults()
ep1OUT.EndpointAddress = 0x01
ep1OUT.Attributes = 2
ep1OUT.MaxPacketSize = 512
ep1OUT.Function = sink
iface.Endpoints = append(iface.Endpoints, ep1OUT)
}
// Linux tools/usb/testusb.c does not seem to test loopback functionality at
// all, for now we leave endpoint functions undefined.
func configureLoopback(device *usb.Device) {
// loopback configuration
conf := &usb.ConfigurationDescriptor{}
conf.SetDefaults()
conf.TotalLength = 0x0020
conf.NumInterfaces = 1
conf.ConfigurationValue = 2
iConfiguration, _ := device.AddString(`loop input to output`)
conf.Configuration = iConfiguration
device.Configurations = append(device.Configurations, conf)
// loopback interface
iface := &usb.InterfaceDescriptor{}
iface.SetDefaults()
iface.NumEndpoints = 2
iface.InterfaceClass = 0xff
iInterface, _ := device.AddString(`loop input to output`)
iface.Interface = iInterface
conf.Interfaces = append(conf.Interfaces, iface)
// loopback EP1 IN endpoint (bulk)
ep1IN := &usb.EndpointDescriptor{}
ep1IN.SetDefaults()
ep1IN.EndpointAddress = 0x81
ep1IN.Attributes = 2
ep1IN.MaxPacketSize = 512
iface.Endpoints = append(iface.Endpoints, ep1IN)
// loopback EP1 OUT endpoint (bulk)
ep1OUT := &usb.EndpointDescriptor{}
ep1OUT.SetDefaults()
ep1OUT.EndpointAddress = 0x01
ep1OUT.Attributes = 2
ep1OUT.MaxPacketSize = 512
iface.Endpoints = append(iface.Endpoints, ep1OUT)
}
// source implements the IN endpoint data source, to be used `modprobe usbtest
// pattern=1 mod_pattern=1`.
func source(_ []byte, lastErr error) (in []byte, err error) {
in = make([]byte, 512*10)
for i := 0; i < len(in); i++ {
in[i] = byte((i % 512) % 63)
}
return
}
// sink implemente the OUT endpoint data sink, to be used `modprobe usbtest
// pattern=1 mod_pattern=1`.
func sink(out []byte, lastErr error) (_ []byte, err error) {
// skip zero length packets
if len(out) == 0 {
return
}
for i := 0; i < len(out); i++ {
if out[i] != byte((i%512)%63) {
return nil, fmt.Errorf("imx6_usb: EP1.0 function error, buffer mismatch (out[%d] == %x)", i, out[i])
}
}
return
}
// StartUSBGadgetZero starts an emulated Linux Gadget Zero device
// (bulk/interrupt endpoints only).
//
// https://github.com/torvalds/linux/blob/master/drivers/usb/gadget/legacy/zero.c
//
// To be tested on host side with `modprobe usbtest pattern=1 mod_pattern=1`.
//
// Example of tests (using Linux tools/usb/testusb.c) expected to pass:
//
// test 0, 0.000007 secs
// test 1, 0.000475 secs
// test 2, 0.000079 secs
// test 3, 0.001594 secs
// test 4, 0.000129 secs
// test 5, 0.011356 secs
// test 6, 0.007847 secs
// test 7, 0.014690 secs
// test 8, 0.007832 secs
// test 10, 0.020543 secs
// test 11, 0.025884 secs
// test 12, 0.029996 secs
// test 17, 0.000058 secs
// test 18, 0.000079 secs
// test 19, 0.001588 secs
// test 20, 0.000092 secs
// test 24, 0.019632 secs
func StartUSBGadgetZero() {
device := &usb.Device{}
configureZeroDevice(device)
configureSourceSink(device)
configureLoopback(device)
usb.USB1.Init()
usb.USB1.DeviceMode()
usb.USB1.Reset()
// never returns
usb.USB1.Start(device)
}

16
example/web_profiling.go

@ -1,16 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
_ "github.com/mkevac/debugcharts"
_ "net/http/pprof"
)

159
example/web_server.go

@ -1,159 +0,0 @@
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
//
// +build tamago,arm
package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"html"
"log"
"math/big"
"net"
"net/http"
"os"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
func generateTLSCerts(address net.IP) ([]byte, []byte, error) {
TLSCert := new(bytes.Buffer)
TLSKey := new(bytes.Buffer)
serial, _ := rand.Int(rand.Reader, big.NewInt(1<<63-1))
log.Printf("generating TLS keypair IP: %s, Serial: %X", IP, serial)
validFrom, _ := time.Parse(time.RFC3339, "1981-01-07T00:00:00Z")
validUntil, _ := time.Parse(time.RFC3339, "2022-01-07T00:00:00Z")
certTemplate := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
Organization: []string{"F-Secure Foundry"},
OrganizationalUnit: []string{"TamaGo test certificates"},
CommonName: IP,
},
IPAddresses: []net.IP{address},
SignatureAlgorithm: x509.ECDSAWithSHA256,
PublicKeyAlgorithm: x509.ECDSA,
NotBefore: validFrom,
NotAfter: validUntil,
SubjectKeyId: []byte{1, 2, 3, 4, 5},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
caTemplate := certTemplate
caTemplate.SerialNumber = serial
caTemplate.SubjectKeyId = []byte{1, 2, 3, 4, 6}
caTemplate.BasicConstraintsValid = true
caTemplate.IsCA = true
caTemplate.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
caTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
pub := &priv.PublicKey
cert, err := x509.CreateCertificate(rand.Reader, &certTemplate, &caTemplate, pub, priv)
if err != nil {
return nil, nil, err
}
pem.Encode(TLSCert, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
ecb, _ := x509.MarshalECPrivateKey(priv)
pem.Encode(TLSKey, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ecb})
h := sha256.New()
h.Write(cert)
log.Printf("SHA-256 fingerprint: % X", h.Sum(nil))
return TLSCert.Bytes(), TLSKey.Bytes(), nil
}
func setupStaticWebAssets() {
file, err := os.OpenFile("/index.html", os.O_WRONLY|os.O_CREATE|os.O_EXCL|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("<html><body>")
file.WriteString(fmt.Sprintf("<p>%s</p><ul>", html.EscapeString(banner)))
file.WriteString(fmt.Sprintf(`<li><a href="%s">%s</a></li>`, "/dir", "/dir"))
file.WriteString(fmt.Sprintf(`<li><a href="%s">%s</a></li>`, "/debug/charts", "/debug/charts"))
file.WriteString(fmt.Sprintf(`<li><a href="%s">%s</a></li>`, "/debug/pprof", "/debug/pprof"))
file.WriteString("</ul></body></html>")
staticHandler := http.FileServer(http.Dir("/"))
http.Handle("/", http.StripPrefix("/", staticHandler))
}
func startWebServer(s *stack.Stack, addr tcpip.Address, port uint16, nic tcpip.NICID, https bool) {
var err error
fullAddr := tcpip.FullAddress{Addr: addr, Port: port, NIC: nic}
listener, err := gonet.NewListener(s, fullAddr, ipv4.ProtocolNumber)
if err != nil {
log.Fatal("listener error: ", err)
}
srv := &http.Server{
Addr: addr.String() + ":" + string(port),
}
if https {
TLSCert, TLSKey, err := generateTLSCerts(net.ParseIP(addr.String()))
if err != nil {
log.Fatal("TLS cert|key error: ", err)
}
log.Printf("generated TLS certificate:\n%s", TLSCert)
log.Printf("generated TLS key:\n%s", TLSKey)
certificate, err := tls.X509KeyPair(TLSCert, TLSKey)
if err != nil {
log.Fatal("X509KeyPair error: ", err)
}
srv.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{certificate},
}
}
log.Printf("starting web server at %s:%d", addr.String(), port)
if https {
err = srv.ServeTLS(listener, "", "")
} else {
err = srv.Serve(listener)
}
log.Fatal("server returned unexpectedly ", err)
return
}

13
go.mod

@ -1,16 +1,3 @@
module github.com/f-secure-foundry/tamago
go 1.13
require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422
github.com/golang/protobuf v1.3.2 // indirect
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 // indirect
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
gvisor.dev/gvisor v0.0.0-20191224014503-95108940a01c
)
replace gvisor.dev/gvisor v0.0.0-20191224014503-95108940a01c => github.com/f-secure-foundry/gvisor v0.0.0-20191224100818-98827aa91607

83
go.sum

@ -1,83 +0,0 @@
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 h1:EqnrgSSg0SFWRlEZLExgjtuUR/IPnuQ6qw6nwRda4Uk=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cenkalti/backoff v0.0.0-20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/f-secure-foundry/gvisor v0.0.0-20191224100818-98827aa91607 h1:qEivZMI07i5BH8QIe41AhkxczCmBOIXih624UpnT8Vw=
github.com/f-secure-foundry/gvisor v0.0.0-20191224100818-98827aa91607/go.mod h1:rPM8OvTBwX0eD8lW4bytttlKgsWOsHwWu0U9fj3y/BU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/subcommands v0.0.0-20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615 h1:/mD+ABZyXD39BzJI2XyRJlqdZG11gXFo0SSynL+OFeU=
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/runtime-spec v0.1.2-0.20171211145439-b2d941ef6a78/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shirou/gopsutil v2.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4=
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/vishvananda/netlink v1.0.1-0.20190318003149-adb577d4a45e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=