<commit_before>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package account

import (
	"bytes"

	"golang.org/x/crypto/ed25519"
	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/crypto/sha3"

	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/util"
)

// PrivateKey - base type for PrivateKey
type PrivateKey struct {
	PrivateKeyInterface
}

// PrivateKeyInterface - interface type for private key methods
type PrivateKeyInterface interface {
	Account() *Account
	KeyType() int
	PrivateKeyBytes() []byte
	Bytes() []byte
	String() string
	IsTesting() bool
	MarshalText() ([]byte, error)
}

// ED25519PrivateKey - structure for ed25519 keys
type ED25519PrivateKey struct {
	Test       bool
	PrivateKey []byte
}

// NothingPrivateKey - just for debugging
type NothingPrivateKey struct {
	Test       bool
	PrivateKey []byte
}

// seed parameters
var (
	seedHeader   = []byte{0x5a, 0xfe}
	seedHeaderV1 = append(seedHeader, []byte{0x01}...)
	seedHeaderV2 = append(seedHeader, []byte{0x02}...)
)

// for seed v1 only
var (
	seedNonce = [24]byte{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	}
	authSeedIndex = [16]byte{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe7,
	}
)

const (
	seedHeaderLength   = 3
	seedPrefixLength   = 1
	secretKeyV1Length  = 32
	secretKeyV2Length  = 17
	seedChecksumLength = 4

	base58EncodedSeedV1Length = 40
	base58EncodedSeedV2Length = 24
)

// PrivateKeyFromBase58Seed - this converts a Base58 encoded seed string and returns a private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBase58Seed(seedBase58Encoded string) (*PrivateKey, error) {

	// verify length
	seed := util.FromBase58(seedBase58Encoded)
	seedLength := len(seed)
	if base58EncodedSeedV1Length != seedLength && base58EncodedSeedV2Length != seedLength {
		return nil, fault.ErrInvalidSeedLength
	}

	// verify checksum
	digest := sha3.Sum256(seed[:seedLength-checksumLength])
	checksumStart := seedLength - seedChecksumLength
	expectedChecksum := digest[:seedChecksumLength]
	actualChecksum := seed[checksumStart:]
	if !bytes.Equal(expectedChecksum, actualChecksum) {
		return nil, fault.ErrChecksumMismatch
	}

	header := seed[:seedHeaderLength]
	var encryptedSk []byte // encrypted seed for generate key pair
	var isTest bool        // denote the network is test net

	switch {
	case bytes.Equal(seedHeaderV1, header):
		// copy the secret key from seed
		var sk [secretKeyV1Length]byte
		secretStart := seedHeaderLength + seedPrefixLength
		copy(sk[:], seed[secretStart:])

		prefix := seed[seedHeaderLength:secretStart]
		// first byte of prefix is test/live indication
		isTest = prefix[0] == 0x01

		encryptedSk = secretbox.Seal([]byte{}, authSeedIndex[:], &seedNonce, &sk)

	case bytes.Equal(seedHeaderV2, header):
		sk := seed[seedHeaderLength:checksumStart]

		// verify valid secret key
		if secretKeyV2Length != len(sk) || 0 != sk[16]&0x0f {
			return nil, fault.ErrInvalidSeedLength
		}

		// parse network
		mode := sk[0]&0x80 | sk[1]&0x40 | sk[2]&0x20 | sk[3]&0x10
		isTest = mode == sk[15]&0xf0^0xf0

		// add the seed 4 times to hash value
		hash := sha3.NewShake256()
		for i := 0; i < 4; i++ {
			n, err := hash.Write(sk)
			if err != nil {
				return nil, err
			}
			if secretKeyV2Length != n {
				return nil, fault.InvalidError("secret key is not written successfully")
			}
		}

		const encryptedLength = 32
		encryptedSk = make([]byte, encryptedLength)
		n, err := hash.Read(encryptedSk)
		if nil != err {
			return nil, err
		}
		if encryptedLength != n {
			return nil, fault.InvalidError("encrypted secret is not read successfully")
		}

	default:
		return nil, fault.ErrInvalidSeedHeader
	}

	// generate key pair from encrypted secret key
	_, priv, err := ed25519.GenerateKey(bytes.NewBuffer(encryptedSk))
	if nil != err {
		return nil, err
	}

	privateKey := &PrivateKey{
		PrivateKeyInterface: &ED25519PrivateKey{
			Test:       isTest,
			PrivateKey: priv,
		},
	}
	return privateKey, nil
}

// PrivateKeyFromBase58 - this converts a Base58 encoded string and returns an private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBase58(privateKeyBase58Encoded string) (*PrivateKey, error) {
	// Decode the privateKey
	privateKeyDecoded := util.FromBase58(privateKeyBase58Encoded)
	if 0 == len(privateKeyDecoded) {
		return nil, fault.ErrCannotDecodePrivateKey
	}

	// Parse the key variant
	keyVariant, keyVariantLength := util.FromVarint64(privateKeyDecoded)

	// Check key type
	if 0 == keyVariantLength || keyVariant&publicKeyCode == publicKeyCode {
		return nil, fault.ErrNotPrivateKey
	}

	// compute algorithm
	keyAlgorithm := keyVariant >> algorithmShift
	if keyAlgorithm < 0 || keyAlgorithm >= algorithmLimit {
		return nil, fault.ErrInvalidKeyType
	}

	// network selection
	isTest := 0 != keyVariant&testKeyCode

	// Compute key length
	keyLength := len(privateKeyDecoded) - keyVariantLength - checksumLength
	if keyLength <= 0 {
		return nil, fault.ErrInvalidKeyLength
	}

	// Checksum
	checksumStart := len(privateKeyDecoded) - checksumLength
	checksum := sha3.Sum256(privateKeyDecoded[:checksumStart])
	if !bytes.Equal(checksum[:checksumLength], privateKeyDecoded[checksumStart:]) {
		return nil, fault.ErrChecksumMismatch
	}

	// return a pointer to the specific private key type
	switch keyAlgorithm {
	case ED25519:
		if keyLength != ed25519.PrivateKeySize {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyDecoded[keyVariantLength:checksumStart]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &ED25519PrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	case Nothing:
		if 2 != keyLength {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyDecoded[keyVariantLength:checksumStart]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &NothingPrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	default:
		return nil, fault.ErrInvalidKeyType
	}
}

// PrivateKeyFromBytes - this converts a byte encoded buffer and returns an private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBytes(privateKeyBytes []byte) (*PrivateKey, error) {

	// Parse the key variant
	keyVariant, keyVariantLength := util.FromVarint64(privateKeyBytes)

	// Check key type
	if 0 == keyVariantLength || keyVariant&publicKeyCode == publicKeyCode {
		return nil, fault.ErrNotPrivateKey
	}

	// compute algorithm
	keyAlgorithm := keyVariant >> algorithmShift
	if keyAlgorithm < 0 || keyAlgorithm >= algorithmLimit {
		return nil, fault.ErrInvalidKeyType
	}

	// network selection
	isTest := 0 != keyVariant&testKeyCode

	// Compute key length
	keyLength := len(privateKeyBytes) - keyVariantLength
	if keyLength <= 0 {
		return nil, fault.ErrInvalidKeyLength
	}

	// return a pointer to the specific private key type
	switch keyAlgorithm {
	case ED25519:
		if keyLength != ed25519.PrivateKeySize {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyBytes[keyVariantLength:]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &ED25519PrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	case Nothing:
		if 2 != keyLength {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyBytes[keyVariantLength:]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &NothingPrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	default:
		return nil, fault.ErrInvalidKeyType
	}
}

// UnmarshalText - convert string to private key structure
func (privateKey *PrivateKey) UnmarshalText(s []byte) error {
	a, err := PrivateKeyFromBase58(string(s))
	if nil != err {
		return err
	}
	privateKey.PrivateKeyInterface = a.PrivateKeyInterface
	return nil
}

// ED25519
// -------

// IsTesting - return whether the private key is in test mode or not
func (privateKey *ED25519PrivateKey) IsTesting() bool {
	return privateKey.Test
}

// KeyType - key type code (see enumeration in account.go)
func (privateKey *ED25519PrivateKey) KeyType() int {
	return ED25519
}

// Account - return the corresponding account
func (privateKey *ED25519PrivateKey) Account() *Account {
	return &Account{
		AccountInterface: &ED25519Account{
			Test:      privateKey.Test,
			PublicKey: privateKey.PrivateKey[ed25519.PrivateKeySize-ed25519.PublicKeySize:],
		},
	}
}

// PrivateKeyBytes - fetch the private key as byte slice
func (privateKey *ED25519PrivateKey) PrivateKeyBytes() []byte {
	return privateKey.PrivateKey[:]
}

// Bytes - byte slice for encoded key
func (privateKey *ED25519PrivateKey) Bytes() []byte {
	keyVariant := byte(ED25519 << algorithmShift)
	if privateKey.Test {
		keyVariant |= testKeyCode
	}
	return append([]byte{keyVariant}, privateKey.PrivateKey[:]...)
}

// String - base58 encoding of encoded key
func (privateKey *ED25519PrivateKey) String() string {
	buffer := privateKey.Bytes()
	checksum := sha3.Sum256(buffer)
	buffer = append(buffer, checksum[:checksumLength]...)
	return util.ToBase58(buffer)
}

// MarshalText - convert an privateKey to its Base58 JSON form
func (privateKey ED25519PrivateKey) MarshalText() ([]byte, error) {
	return []byte(privateKey.String()), nil
}

// Nothing
// -------

// IsTesting - return whether the private key is in test mode or not
func (privateKey *NothingPrivateKey) IsTesting() bool {
	return privateKey.Test
}

// KeyType - key type code (see enumeration in account.go)
func (privateKey *NothingPrivateKey) KeyType() int {
	return Nothing
}

// Account - return the corresponding account
func (privateKey *NothingPrivateKey) Account() *Account {
	return nil
}

// PrivateKeyBytes - fetch the private key as byte slice
func (privateKey *NothingPrivateKey) PrivateKeyBytes() []byte {
	return privateKey.PrivateKey[:]
}

// Bytes - byte slice for encoded key
func (privateKey *NothingPrivateKey) Bytes() []byte {
	keyVariant := byte(Nothing << algorithmShift)
	if privateKey.Test {
		keyVariant |= testKeyCode
	}
	return append([]byte{keyVariant}, privateKey.PrivateKey[:]...)
}

// String - base58 encoding of encoded key
func (privateKey *NothingPrivateKey) String() string {
	buffer := privateKey.Bytes()
	checksum := sha3.Sum256(buffer)
	buffer = append(buffer, checksum[:checksumLength]...)
	return util.ToBase58(buffer)
}

// MarshalText - convert an privateKey to its Base58 JSON form
func (privateKey NothingPrivateKey) MarshalText() ([]byte, error) {
	return []byte(privateKey.String()), nil
}
<commit_msg>[account] - Fixes no value of type uint64 is less than 0 (SA4003)<commit_after>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package account

import (
	"bytes"

	"golang.org/x/crypto/ed25519"
	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/crypto/sha3"

	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/util"
)

// PrivateKey - base type for PrivateKey
type PrivateKey struct {
	PrivateKeyInterface
}

// PrivateKeyInterface - interface type for private key methods
type PrivateKeyInterface interface {
	Account() *Account
	KeyType() int
	PrivateKeyBytes() []byte
	Bytes() []byte
	String() string
	IsTesting() bool
	MarshalText() ([]byte, error)
}

// ED25519PrivateKey - structure for ed25519 keys
type ED25519PrivateKey struct {
	Test       bool
	PrivateKey []byte
}

// NothingPrivateKey - just for debugging
type NothingPrivateKey struct {
	Test       bool
	PrivateKey []byte
}

// seed parameters
var (
	seedHeader   = []byte{0x5a, 0xfe}
	seedHeaderV1 = append(seedHeader, []byte{0x01}...)
	seedHeaderV2 = append(seedHeader, []byte{0x02}...)
)

// for seed v1 only
var (
	seedNonce = [24]byte{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	}
	authSeedIndex = [16]byte{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe7,
	}
)

const (
	seedHeaderLength   = 3
	seedPrefixLength   = 1
	secretKeyV1Length  = 32
	secretKeyV2Length  = 17
	seedChecksumLength = 4

	base58EncodedSeedV1Length = 40
	base58EncodedSeedV2Length = 24
)

// PrivateKeyFromBase58Seed - this converts a Base58 encoded seed string and returns a private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBase58Seed(seedBase58Encoded string) (*PrivateKey, error) {

	// verify length
	seed := util.FromBase58(seedBase58Encoded)
	seedLength := len(seed)
	if base58EncodedSeedV1Length != seedLength && base58EncodedSeedV2Length != seedLength {
		return nil, fault.ErrInvalidSeedLength
	}

	// verify checksum
	digest := sha3.Sum256(seed[:seedLength-checksumLength])
	checksumStart := seedLength - seedChecksumLength
	expectedChecksum := digest[:seedChecksumLength]
	actualChecksum := seed[checksumStart:]
	if !bytes.Equal(expectedChecksum, actualChecksum) {
		return nil, fault.ErrChecksumMismatch
	}

	header := seed[:seedHeaderLength]
	var encryptedSk []byte // encrypted seed for generate key pair
	var isTest bool        // denote the network is test net

	switch {
	case bytes.Equal(seedHeaderV1, header):
		// copy the secret key from seed
		var sk [secretKeyV1Length]byte
		secretStart := seedHeaderLength + seedPrefixLength
		copy(sk[:], seed[secretStart:])

		prefix := seed[seedHeaderLength:secretStart]
		// first byte of prefix is test/live indication
		isTest = prefix[0] == 0x01

		encryptedSk = secretbox.Seal([]byte{}, authSeedIndex[:], &seedNonce, &sk)

	case bytes.Equal(seedHeaderV2, header):
		sk := seed[seedHeaderLength:checksumStart]

		// verify valid secret key
		if secretKeyV2Length != len(sk) || 0 != sk[16]&0x0f {
			return nil, fault.ErrInvalidSeedLength
		}

		// parse network
		mode := sk[0]&0x80 | sk[1]&0x40 | sk[2]&0x20 | sk[3]&0x10
		isTest = mode == sk[15]&0xf0^0xf0

		// add the seed 4 times to hash value
		hash := sha3.NewShake256()
		for i := 0; i < 4; i++ {
			n, err := hash.Write(sk)
			if err != nil {
				return nil, err
			}
			if secretKeyV2Length != n {
				return nil, fault.InvalidError("secret key is not written successfully")
			}
		}

		const encryptedLength = 32
		encryptedSk = make([]byte, encryptedLength)
		n, err := hash.Read(encryptedSk)
		if nil != err {
			return nil, err
		}
		if encryptedLength != n {
			return nil, fault.InvalidError("encrypted secret is not read successfully")
		}

	default:
		return nil, fault.ErrInvalidSeedHeader
	}

	// generate key pair from encrypted secret key
	_, priv, err := ed25519.GenerateKey(bytes.NewBuffer(encryptedSk))
	if nil != err {
		return nil, err
	}

	privateKey := &PrivateKey{
		PrivateKeyInterface: &ED25519PrivateKey{
			Test:       isTest,
			PrivateKey: priv,
		},
	}
	return privateKey, nil
}

// PrivateKeyFromBase58 - this converts a Base58 encoded string and returns an private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBase58(privateKeyBase58Encoded string) (*PrivateKey, error) {
	// Decode the privateKey
	privateKeyDecoded := util.FromBase58(privateKeyBase58Encoded)
	if 0 == len(privateKeyDecoded) {
		return nil, fault.ErrCannotDecodePrivateKey
	}

	// Parse the key variant
	keyVariant, keyVariantLength := util.FromVarint64(privateKeyDecoded)

	// Check key type
	if 0 == keyVariantLength || keyVariant&publicKeyCode == publicKeyCode {
		return nil, fault.ErrNotPrivateKey
	}

	// compute algorithm
	keyAlgorithm := keyVariant >> algorithmShift
	if keyAlgorithm >= algorithmLimit {
		return nil, fault.ErrInvalidKeyType
	}

	// network selection
	isTest := 0 != keyVariant&testKeyCode

	// Compute key length
	keyLength := len(privateKeyDecoded) - keyVariantLength - checksumLength
	if keyLength <= 0 {
		return nil, fault.ErrInvalidKeyLength
	}

	// Checksum
	checksumStart := len(privateKeyDecoded) - checksumLength
	checksum := sha3.Sum256(privateKeyDecoded[:checksumStart])
	if !bytes.Equal(checksum[:checksumLength], privateKeyDecoded[checksumStart:]) {
		return nil, fault.ErrChecksumMismatch
	}

	// return a pointer to the specific private key type
	switch keyAlgorithm {
	case ED25519:
		if keyLength != ed25519.PrivateKeySize {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyDecoded[keyVariantLength:checksumStart]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &ED25519PrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	case Nothing:
		if 2 != keyLength {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyDecoded[keyVariantLength:checksumStart]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &NothingPrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	default:
		return nil, fault.ErrInvalidKeyType
	}
}

// PrivateKeyFromBytes - this converts a byte encoded buffer and returns an private key
//
// one of the specific private key types are returned using the base "PrivateKeyInterface"
// interface type to allow individual methods to be called.
func PrivateKeyFromBytes(privateKeyBytes []byte) (*PrivateKey, error) {

	// Parse the key variant
	keyVariant, keyVariantLength := util.FromVarint64(privateKeyBytes)

	// Check key type
	if 0 == keyVariantLength || keyVariant&publicKeyCode == publicKeyCode {
		return nil, fault.ErrNotPrivateKey
	}

	// compute algorithm
	keyAlgorithm := keyVariant >> algorithmShift
	if keyAlgorithm < 0 || keyAlgorithm >= algorithmLimit {
		return nil, fault.ErrInvalidKeyType
	}

	// network selection
	isTest := 0 != keyVariant&testKeyCode

	// Compute key length
	keyLength := len(privateKeyBytes) - keyVariantLength
	if keyLength <= 0 {
		return nil, fault.ErrInvalidKeyLength
	}

	// return a pointer to the specific private key type
	switch keyAlgorithm {
	case ED25519:
		if keyLength != ed25519.PrivateKeySize {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyBytes[keyVariantLength:]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &ED25519PrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	case Nothing:
		if 2 != keyLength {
			return nil, fault.ErrInvalidKeyLength
		}
		priv := privateKeyBytes[keyVariantLength:]
		privateKey := &PrivateKey{
			PrivateKeyInterface: &NothingPrivateKey{
				Test:       isTest,
				PrivateKey: priv,
			},
		}
		return privateKey, nil
	default:
		return nil, fault.ErrInvalidKeyType
	}
}

// UnmarshalText - convert string to private key structure
func (privateKey *PrivateKey) UnmarshalText(s []byte) error {
	a, err := PrivateKeyFromBase58(string(s))
	if nil != err {
		return err
	}
	privateKey.PrivateKeyInterface = a.PrivateKeyInterface
	return nil
}

// ED25519
// -------

// IsTesting - return whether the private key is in test mode or not
func (privateKey *ED25519PrivateKey) IsTesting() bool {
	return privateKey.Test
}

// KeyType - key type code (see enumeration in account.go)
func (privateKey *ED25519PrivateKey) KeyType() int {
	return ED25519
}

// Account - return the corresponding account
func (privateKey *ED25519PrivateKey) Account() *Account {
	return &Account{
		AccountInterface: &ED25519Account{
			Test:      privateKey.Test,
			PublicKey: privateKey.PrivateKey[ed25519.PrivateKeySize-ed25519.PublicKeySize:],
		},
	}
}

// PrivateKeyBytes - fetch the private key as byte slice
func (privateKey *ED25519PrivateKey) PrivateKeyBytes() []byte {
	return privateKey.PrivateKey[:]
}

// Bytes - byte slice for encoded key
func (privateKey *ED25519PrivateKey) Bytes() []byte {
	keyVariant := byte(ED25519 << algorithmShift)
	if privateKey.Test {
		keyVariant |= testKeyCode
	}
	return append([]byte{keyVariant}, privateKey.PrivateKey[:]...)
}

// String - base58 encoding of encoded key
func (privateKey *ED25519PrivateKey) String() string {
	buffer := privateKey.Bytes()
	checksum := sha3.Sum256(buffer)
	buffer = append(buffer, checksum[:checksumLength]...)
	return util.ToBase58(buffer)
}

// MarshalText - convert an privateKey to its Base58 JSON form
func (privateKey ED25519PrivateKey) MarshalText() ([]byte, error) {
	return []byte(privateKey.String()), nil
}

// Nothing
// -------

// IsTesting - return whether the private key is in test mode or not
func (privateKey *NothingPrivateKey) IsTesting() bool {
	return privateKey.Test
}

// KeyType - key type code (see enumeration in account.go)
func (privateKey *NothingPrivateKey) KeyType() int {
	return Nothing
}

// Account - return the corresponding account
func (privateKey *NothingPrivateKey) Account() *Account {
	return nil
}

// PrivateKeyBytes - fetch the private key as byte slice
func (privateKey *NothingPrivateKey) PrivateKeyBytes() []byte {
	return privateKey.PrivateKey[:]
}

// Bytes - byte slice for encoded key
func (privateKey *NothingPrivateKey) Bytes() []byte {
	keyVariant := byte(Nothing << algorithmShift)
	if privateKey.Test {
		keyVariant |= testKeyCode
	}
	return append([]byte{keyVariant}, privateKey.PrivateKey[:]...)
}

// String - base58 encoding of encoded key
func (privateKey *NothingPrivateKey) String() string {
	buffer := privateKey.Bytes()
	checksum := sha3.Sum256(buffer)
	buffer = append(buffer, checksum[:checksumLength]...)
	return util.ToBase58(buffer)
}

// MarshalText - convert an privateKey to its Base58 JSON form
func (privateKey NothingPrivateKey) MarshalText() ([]byte, error) {
	return []byte(privateKey.String()), nil
}
<|endoftext|>


<commit_before>// +build functional lcow

package functional

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/Microsoft/hcsshim/functional/utilities"
	"github.com/Microsoft/hcsshim/internal/hcs"
	"github.com/Microsoft/hcsshim/internal/hcsoci"
	"github.com/Microsoft/hcsshim/internal/lcow"
	"github.com/Microsoft/hcsshim/internal/uvm"
	"github.com/Microsoft/hcsshim/osversion"
)

// TestLCOWUVMNoSCSINoVPMemInitrd starts an LCOW utility VM without a SCSI controller and
// no VPMem device. Uses initrd.
func TestLCOWUVMNoSCSINoVPMemInitrd(t *testing.T) {
	opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
	opts.SCSIControllerCount = 0
	opts.VPMemDeviceCount = 0

	testLCOWUVMNoSCSISingleVPMem(t, opts, `Command line: initrd=/initrd.img`)
}

// TestLCOWUVMNoSCSISingleVPMemVHD starts an LCOW utility VM without a SCSI controller and
// only a single VPMem device. Uses VPMEM VHD
func TestLCOWUVMNoSCSISingleVPMemVHD(t *testing.T) {
	opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
	opts.SCSIControllerCount = 0
	opts.VPMemDeviceCount = 1
	opts.PreferredRootFSType = uvm.PreferredRootFSTypeVHD

	testLCOWUVMNoSCSISingleVPMem(t, opts, `Command line: root=/dev/pmem0 init=/init`)
}

func testLCOWUVMNoSCSISingleVPMem(t *testing.T, opts *uvm.OptionsLCOW, expected string) {
	testutilities.RequiresBuild(t, osversion.RS5)
	lcowUVM := testutilities.CreateLCOWUVMFromOpts(t, opts)
	defer lcowUVM.Close()
	out, err := exec.Command(`hcsdiag`, `exec`, `-uvm`, lcowUVM.ID(), `dmesg`).Output() // TODO: Move the CreateProcess.
	if err != nil {
		t.Fatal(string(err.(*exec.ExitError).Stderr))
	}
	if !strings.Contains(string(out), expected) {
		t.Fatalf("Expected dmesg output to have %q: %s", expected, string(out))
	}
}

// TestLCOWTimeUVMStartVHD starts/terminates a utility VM booting from VPMem-
// attached root filesystem a number of times.
func TestLCOWTimeUVMStartVHD(t *testing.T) {
	testutilities.RequiresBuild(t, osversion.RS5)

	testLCOWTimeUVMStart(t, false, uvm.PreferredRootFSTypeVHD)
}

// TestLCOWUVMStart_KernelDirect_VHD starts/terminates a utility VM booting from
// VPMem- attached root filesystem a number of times starting from the Linux
// Kernel directly and skipping EFI.
func TestLCOWUVMStart_KernelDirect_VHD(t *testing.T) {
	testutilities.RequiresBuild(t, 18286)

	testLCOWTimeUVMStart(t, true, uvm.PreferredRootFSTypeVHD)
}

// TestLCOWTimeUVMStartInitRD starts/terminates a utility VM booting from initrd-
// attached root file system a number of times.
func TestLCOWTimeUVMStartInitRD(t *testing.T) {
	testutilities.RequiresBuild(t, osversion.RS5)

	testLCOWTimeUVMStart(t, false, uvm.PreferredRootFSTypeInitRd)
}

// TestLCOWUVMStart_KernelDirect_InitRd starts/terminates a utility VM booting
// from initrd- attached root file system a number of times starting from the
// Linux Kernel directly and skipping EFI.
func TestLCOWUVMStart_KernelDirect_InitRd(t *testing.T) {
	testutilities.RequiresBuild(t, 18286)

	testLCOWTimeUVMStart(t, true, uvm.PreferredRootFSTypeInitRd)
}

func testLCOWTimeUVMStart(t *testing.T, kernelDirect bool, rfsType uvm.PreferredRootFSType) {
	for i := 0; i < 3; i++ {
		opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
		opts.KernelDirect = kernelDirect
		opts.VPMemDeviceCount = 32
		opts.PreferredRootFSType = rfsType

		lcowUVM := testutilities.CreateLCOWUVMFromOpts(t, opts)
		lcowUVM.Close()
	}
}

func TestLCOWSimplePodScenario(t *testing.T) {
	t.Skip("Doesn't work quite yet")
	testutilities.RequiresBuild(t, osversion.RS5)
	alpineLayers := testutilities.LayerFolders(t, "alpine")

	cacheDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(cacheDir)
	cacheFile := filepath.Join(cacheDir, "cache.vhdx")

	// This is what gets mounted into /tmp/scratch
	uvmScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(uvmScratchDir)
	uvmScratchFile := filepath.Join(uvmScratchDir, "uvmscratch.vhdx")

	// Scratch for the first container
	c1ScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(c1ScratchDir)
	c1ScratchFile := filepath.Join(c1ScratchDir, "sandbox.vhdx")

	// Scratch for the second container
	c2ScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(c2ScratchDir)
	c2ScratchFile := filepath.Join(c2ScratchDir, "sandbox.vhdx")

	lcowUVM := testutilities.CreateLCOWUVM(t, "uvm")
	defer lcowUVM.Close()

	// Populate the cache and generate the scratch file for /tmp/scratch
	if err := lcow.CreateScratch(lcowUVM, uvmScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	if _, _, err := lcowUVM.AddSCSI(uvmScratchFile, `/tmp/scratch`, false); err != nil {
		t.Fatal(err)
	}

	// Now create the first containers sandbox, populate a spec
	if err := lcow.CreateScratch(lcowUVM, c1ScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	c1Spec := testutilities.GetDefaultLinuxSpec(t)
	c1Folders := append(alpineLayers, c1ScratchDir)
	c1Spec.Windows.LayerFolders = c1Folders
	c1Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "one"}
	c1Opts := &hcsoci.CreateOptions{
		Spec:          c1Spec,
		HostingSystem: lcowUVM,
	}

	// Now create the second containers sandbox, populate a spec
	if err := lcow.CreateScratch(lcowUVM, c2ScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	c2Spec := testutilities.GetDefaultLinuxSpec(t)
	c2Folders := append(alpineLayers, c2ScratchDir)
	c2Spec.Windows.LayerFolders = c2Folders
	c2Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "two"}
	c2Opts := &hcsoci.CreateOptions{
		Spec:          c2Spec,
		HostingSystem: lcowUVM,
	}

	// Create the two containers
	c1hcsSystem, c1Resources, err := CreateContainerTestWrapper(c1Opts)
	if err != nil {
		t.Fatal(err)
	}
	c2hcsSystem, c2Resources, err := CreateContainerTestWrapper(c2Opts)
	if err != nil {
		t.Fatal(err)
	}

	// Start them. In the UVM, they'll be in the created state from runc's perspective after this.eg
	/// # runc list
	//ID                                     PID         STATUS      BUNDLE         CREATED                        OWNER
	//3a724c2b-f389-5c71-0555-ebc6f5379b30   138         running     /run/gcs/c/1   2018-06-04T21:23:39.1253911Z   root
	//7a8229a0-eb60-b515-55e7-d2dd63ffae75   158         created     /run/gcs/c/2   2018-06-04T21:23:39.4249048Z   root
	if err := c1hcsSystem.Start(); err != nil {
		t.Fatal(err)
	}
	defer hcsoci.ReleaseResources(c1Resources, lcowUVM, true)

	if err := c2hcsSystem.Start(); err != nil {
		t.Fatal(err)
	}
	defer hcsoci.ReleaseResources(c2Resources, lcowUVM, true)

	// Start the init process in each container and grab it's stdout comparing to expected
	runInitProcess(t, c1hcsSystem, "hello lcow container one")
	runInitProcess(t, c2hcsSystem, "hello lcow container two")

}

// Helper to run the init process in an LCOW container; verify it exits with exit
// code 0; verify stderr is empty; check output is as expected.
func runInitProcess(t *testing.T, s *hcs.System, expected string) {
	var outB, errB bytes.Buffer
	p, bc, err := lcow.CreateProcess(&lcow.ProcessOptions{
		HCSSystem:   s,
		Stdout:      &outB,
		Stderr:      &errB,
		CopyTimeout: 30 * time.Second,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer p.Close()
	if bc.Err != 0 {
		t.Fatalf("got %d bytes on stderr: %s", bc.Err, errB.String())
	}
	if strings.TrimSpace(outB.String()) != expected {
		t.Fatalf("got %q (%d) expecting %q", outB.String(), bc.Out, expected)
	}
}
<commit_msg>Make lcow tests explicitly set RootFS<commit_after>// +build functional lcow

package functional

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/Microsoft/hcsshim/functional/utilities"
	"github.com/Microsoft/hcsshim/internal/hcs"
	"github.com/Microsoft/hcsshim/internal/hcsoci"
	"github.com/Microsoft/hcsshim/internal/lcow"
	"github.com/Microsoft/hcsshim/internal/uvm"
	"github.com/Microsoft/hcsshim/osversion"
)

// TestLCOWUVMNoSCSINoVPMemInitrd starts an LCOW utility VM without a SCSI controller and
// no VPMem device. Uses initrd.
func TestLCOWUVMNoSCSINoVPMemInitrd(t *testing.T) {
	opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
	opts.SCSIControllerCount = 0
	opts.VPMemDeviceCount = 0
	opts.PreferredRootFSType = uvm.PreferredRootFSTypeInitRd
	opts.RootFSFile = "initrd.img"

	testLCOWUVMNoSCSISingleVPMem(t, opts, `Command line: initrd=/initrd.img`)
}

// TestLCOWUVMNoSCSISingleVPMemVHD starts an LCOW utility VM without a SCSI controller and
// only a single VPMem device. Uses VPMEM VHD
func TestLCOWUVMNoSCSISingleVPMemVHD(t *testing.T) {
	opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
	opts.SCSIControllerCount = 0
	opts.VPMemDeviceCount = 1
	opts.PreferredRootFSType = uvm.PreferredRootFSTypeVHD
	opts.RootFSFile = "rootfs.vhd"

	testLCOWUVMNoSCSISingleVPMem(t, opts, `Command line: root=/dev/pmem0 init=/init`)
}

func testLCOWUVMNoSCSISingleVPMem(t *testing.T, opts *uvm.OptionsLCOW, expected string) {
	testutilities.RequiresBuild(t, osversion.RS5)
	lcowUVM := testutilities.CreateLCOWUVMFromOpts(t, opts)
	defer lcowUVM.Close()
	out, err := exec.Command(`hcsdiag`, `exec`, `-uvm`, lcowUVM.ID(), `dmesg`).Output() // TODO: Move the CreateProcess.
	if err != nil {
		t.Fatal(string(err.(*exec.ExitError).Stderr))
	}
	if !strings.Contains(string(out), expected) {
		t.Fatalf("Expected dmesg output to have %q: %s", expected, string(out))
	}
}

// TestLCOWTimeUVMStartVHD starts/terminates a utility VM booting from VPMem-
// attached root filesystem a number of times.
func TestLCOWTimeUVMStartVHD(t *testing.T) {
	testutilities.RequiresBuild(t, osversion.RS5)

	testLCOWTimeUVMStart(t, false, uvm.PreferredRootFSTypeVHD)
}

// TestLCOWUVMStart_KernelDirect_VHD starts/terminates a utility VM booting from
// VPMem- attached root filesystem a number of times starting from the Linux
// Kernel directly and skipping EFI.
func TestLCOWUVMStart_KernelDirect_VHD(t *testing.T) {
	testutilities.RequiresBuild(t, 18286)

	testLCOWTimeUVMStart(t, true, uvm.PreferredRootFSTypeVHD)
}

// TestLCOWTimeUVMStartInitRD starts/terminates a utility VM booting from initrd-
// attached root file system a number of times.
func TestLCOWTimeUVMStartInitRD(t *testing.T) {
	testutilities.RequiresBuild(t, osversion.RS5)

	testLCOWTimeUVMStart(t, false, uvm.PreferredRootFSTypeInitRd)
}

// TestLCOWUVMStart_KernelDirect_InitRd starts/terminates a utility VM booting
// from initrd- attached root file system a number of times starting from the
// Linux Kernel directly and skipping EFI.
func TestLCOWUVMStart_KernelDirect_InitRd(t *testing.T) {
	testutilities.RequiresBuild(t, 18286)

	testLCOWTimeUVMStart(t, true, uvm.PreferredRootFSTypeInitRd)
}

func testLCOWTimeUVMStart(t *testing.T, kernelDirect bool, rfsType uvm.PreferredRootFSType) {
	for i := 0; i < 3; i++ {
		opts := uvm.NewDefaultOptionsLCOW(t.Name(), "")
		opts.KernelDirect = kernelDirect
		opts.VPMemDeviceCount = 32
		opts.PreferredRootFSType = rfsType
		switch opts.PreferredRootFSType {
		case uvm.PreferredRootFSTypeInitRd:
			opts.RootFSFile = "initrd.img"
		case uvm.PreferredRootFSTypeVHD:
			opts.RootFSFile = "rootfs.vhd"
		}

		lcowUVM := testutilities.CreateLCOWUVMFromOpts(t, opts)
		lcowUVM.Close()
	}
}

func TestLCOWSimplePodScenario(t *testing.T) {
	t.Skip("Doesn't work quite yet")
	testutilities.RequiresBuild(t, osversion.RS5)
	alpineLayers := testutilities.LayerFolders(t, "alpine")

	cacheDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(cacheDir)
	cacheFile := filepath.Join(cacheDir, "cache.vhdx")

	// This is what gets mounted into /tmp/scratch
	uvmScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(uvmScratchDir)
	uvmScratchFile := filepath.Join(uvmScratchDir, "uvmscratch.vhdx")

	// Scratch for the first container
	c1ScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(c1ScratchDir)
	c1ScratchFile := filepath.Join(c1ScratchDir, "sandbox.vhdx")

	// Scratch for the second container
	c2ScratchDir := testutilities.CreateTempDir(t)
	defer os.RemoveAll(c2ScratchDir)
	c2ScratchFile := filepath.Join(c2ScratchDir, "sandbox.vhdx")

	lcowUVM := testutilities.CreateLCOWUVM(t, "uvm")
	defer lcowUVM.Close()

	// Populate the cache and generate the scratch file for /tmp/scratch
	if err := lcow.CreateScratch(lcowUVM, uvmScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	if _, _, err := lcowUVM.AddSCSI(uvmScratchFile, `/tmp/scratch`, false); err != nil {
		t.Fatal(err)
	}

	// Now create the first containers sandbox, populate a spec
	if err := lcow.CreateScratch(lcowUVM, c1ScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	c1Spec := testutilities.GetDefaultLinuxSpec(t)
	c1Folders := append(alpineLayers, c1ScratchDir)
	c1Spec.Windows.LayerFolders = c1Folders
	c1Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "one"}
	c1Opts := &hcsoci.CreateOptions{
		Spec:          c1Spec,
		HostingSystem: lcowUVM,
	}

	// Now create the second containers sandbox, populate a spec
	if err := lcow.CreateScratch(lcowUVM, c2ScratchFile, lcow.DefaultScratchSizeGB, cacheFile, ""); err != nil {
		t.Fatal(err)
	}
	c2Spec := testutilities.GetDefaultLinuxSpec(t)
	c2Folders := append(alpineLayers, c2ScratchDir)
	c2Spec.Windows.LayerFolders = c2Folders
	c2Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "two"}
	c2Opts := &hcsoci.CreateOptions{
		Spec:          c2Spec,
		HostingSystem: lcowUVM,
	}

	// Create the two containers
	c1hcsSystem, c1Resources, err := CreateContainerTestWrapper(c1Opts)
	if err != nil {
		t.Fatal(err)
	}
	c2hcsSystem, c2Resources, err := CreateContainerTestWrapper(c2Opts)
	if err != nil {
		t.Fatal(err)
	}

	// Start them. In the UVM, they'll be in the created state from runc's perspective after this.eg
	/// # runc list
	//ID                                     PID         STATUS      BUNDLE         CREATED                        OWNER
	//3a724c2b-f389-5c71-0555-ebc6f5379b30   138         running     /run/gcs/c/1   2018-06-04T21:23:39.1253911Z   root
	//7a8229a0-eb60-b515-55e7-d2dd63ffae75   158         created     /run/gcs/c/2   2018-06-04T21:23:39.4249048Z   root
	if err := c1hcsSystem.Start(); err != nil {
		t.Fatal(err)
	}
	defer hcsoci.ReleaseResources(c1Resources, lcowUVM, true)

	if err := c2hcsSystem.Start(); err != nil {
		t.Fatal(err)
	}
	defer hcsoci.ReleaseResources(c2Resources, lcowUVM, true)

	// Start the init process in each container and grab it's stdout comparing to expected
	runInitProcess(t, c1hcsSystem, "hello lcow container one")
	runInitProcess(t, c2hcsSystem, "hello lcow container two")

}

// Helper to run the init process in an LCOW container; verify it exits with exit
// code 0; verify stderr is empty; check output is as expected.
func runInitProcess(t *testing.T, s *hcs.System, expected string) {
	var outB, errB bytes.Buffer
	p, bc, err := lcow.CreateProcess(&lcow.ProcessOptions{
		HCSSystem:   s,
		Stdout:      &outB,
		Stderr:      &errB,
		CopyTimeout: 30 * time.Second,
	})
	if err != nil {
		t.Fatal(err)
	}
	defer p.Close()
	if bc.Err != 0 {
		t.Fatalf("got %d bytes on stderr: %s", bc.Err, errB.String())
	}
	if strings.TrimSpace(outB.String()) != expected {
		t.Fatalf("got %q (%d) expecting %q", outB.String(), bc.Out, expected)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"fmt"
	"math"
)

// GeoMDim is a dimension of a GeoM.
const GeoMDim = 3

// A GeoM represents a matrix to transform geometry when rendering an image.
//
// The initial value is identity.
type GeoM struct {
	a_1 float64 // The actual 'a' value minus 1
	b   float64
	c   float64
	d_1 float64 // The actual 'd' value minus 1
	tx  float64
	ty  float64
}

// String returns a string representation of GeoM.
func (g *GeoM) String() string {
	return fmt.Sprintf("[[%f, %f, %f], [%f, %f, %f]]", g.a_1+1, g.b, g.tx, g.c, g.d_1+1, g.ty)
}

// Reset resets the GeoM as identity.
func (g *GeoM) Reset() {
	g.a_1 = 0
	g.b = 0
	g.c = 0
	g.d_1 = 0
	g.tx = 0
	g.ty = 0
}

// Apply pre-multiplies a vector (x, y, 1) by the matrix.
// In other words, Apply calculates GeoM * (x, y, 1)^T.
// The return value is x and y values of the result vector.
func (g *GeoM) Apply(x, y float64) (float64, float64) {
	return (g.a_1+1)*x + g.b*y + g.tx, g.c*x + (g.d_1+1)*y + g.ty
}

func (g *GeoM) elements32() (a, b, c, d, tx, ty float32) {
	return float32(g.a_1) + 1, float32(g.b), float32(g.c), float32(g.d_1) + 1, float32(g.tx), float32(g.ty)
}

// Element returns a value of a matrix at (i, j).
func (g *GeoM) Element(i, j int) float64 {
	switch {
	case i == 0 && j == 0:
		return g.a_1 + 1
	case i == 0 && j == 1:
		return g.b
	case i == 0 && j == 2:
		return g.tx
	case i == 1 && j == 0:
		return g.c
	case i == 1 && j == 1:
		return g.d_1 + 1
	case i == 1 && j == 2:
		return g.ty
	default:
		panic("ebiten: i or j is out of index")
	}
}

// Concat multiplies a geometry matrix with the other geometry matrix.
// This is same as muptiplying the matrix other and the matrix g in this order.
func (g *GeoM) Concat(other GeoM) {
	a := (other.a_1+1)*(g.a_1+1) + other.b*g.c
	b := (other.a_1+1)*g.b + other.b*(g.d_1+1)
	tx := (other.a_1+1)*g.tx + other.b*g.ty + other.tx
	c := other.c*(g.a_1+1) + (other.d_1+1)*g.c
	d := other.c*g.b + (other.d_1+1)*(g.d_1+1)
	ty := other.c*g.tx + (other.d_1+1)*g.ty + other.ty

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Add adds a matrix, but in a wrong way.
//
// Deprecated: (as of 1.5.0) Do not use this.
//
// Note that this doesn't make sense as an operation for affine matrices.
func (g *GeoM) Add(other GeoM) {
	g.a_1 += other.a_1
	g.b += other.b
	g.c += other.c
	g.d_1 += other.d_1
	g.tx += other.tx
	g.ty += other.ty
}

// Scale scales the matrix by (x, y).
func (g *GeoM) Scale(x, y float64) {
	a := (g.a_1 + 1) * x
	b := g.b * x
	tx := g.tx * x
	c := g.c * y
	d := (g.d_1 + 1) * y
	ty := g.ty * y

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Translate translates the matrix by (tx, ty).
func (g *GeoM) Translate(tx, ty float64) {
	g.tx += tx
	g.ty += ty
}

// Rotate rotates the matrix by theta.
// The unit is radian.
func (g *GeoM) Rotate(theta float64) {
	if theta == 0 {
		return
	}

	sin, cos := math.Sincos(theta)

	a := cos*(g.a_1+1) - sin*g.c
	b := cos*g.b - sin*(g.d_1+1)
	tx := cos*g.tx - sin*g.ty
	c := sin*(g.a_1+1) + cos*g.c
	d := sin*g.b + cos*(g.d_1+1)
	ty := sin*g.tx + cos*g.ty

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Skew skews the matrix by (skewX, skewY). The unit is radian.
func (g *GeoM) Skew(skewX, skewY float64) {
	sx := math.Tan(skewX)
	sy := math.Tan(skewY)

	a := (g.a_1 + 1) + g.c*sx
	b := g.b + (g.d_1+1)*sx
	c := (g.a_1+1)*sy + g.c
	d := g.b*sy + (g.d_1 + 1)
	tx := g.tx + g.ty*sx
	ty := g.ty + g.tx*sy

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

func (g *GeoM) det() float64 {
	return (g.a_1+1)*(g.d_1+1) - g.b*g.c
}

// IsInvertible returns a boolean value indicating
// whether the matrix g is invertible or not.
func (g *GeoM) IsInvertible() bool {
	return g.det() != 0
}

// Invert inverts the matrix.
// If g is not invertible, Invert panics.
func (g *GeoM) Invert() {
	det := g.det()
	if det == 0 {
		panic("ebiten: g is not invertible")
	}

	a := (g.d_1 + 1) / det
	b := -g.b / det
	c := -g.c / det
	d := (g.a_1 + 1) / det
	tx := (-(g.d_1+1)*g.tx + g.b*g.ty) / det
	ty := (g.c*g.tx + -(g.a_1+1)*g.ty) / det

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// SetElement sets an element at (i, j).
func (g *GeoM) SetElement(i, j int, element float64) {
	e := element
	switch {
	case i == 0 && j == 0:
		g.a_1 = e - 1
	case i == 0 && j == 1:
		g.b = e
	case i == 0 && j == 2:
		g.tx = e
	case i == 1 && j == 0:
		g.c = e
	case i == 1 && j == 1:
		g.d_1 = e - 1
	case i == 1 && j == 2:
		g.ty = e
	default:
		panic("ebiten: i or j is out of index")
	}
}

// ScaleGeo returns a geometry matrix for scaling.
//
// Deprecated: (as of 1.2.0) Use Scale instead.
func ScaleGeo(x, y float64) GeoM {
	g := GeoM{}
	g.Scale(x, y)
	return g
}

// TranslateGeo returns a geometry matrix for translating.
//
// Deprecated: (as of 1.2.0) Use Translate instead.
func TranslateGeo(tx, ty float64) GeoM {
	g := GeoM{}
	g.Translate(tx, ty)
	return g
}

// RotateGeo returns a geometry matrix for rotating.
//
// Deprecated: (as of 1.2.0) Use Rotate instead.
func RotateGeo(theta float64) GeoM {
	g := GeoM{}
	g.Rotate(theta)
	return g
}
<commit_msg>ebiten: Rename det to det2x2<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"fmt"
	"math"
)

// GeoMDim is a dimension of a GeoM.
const GeoMDim = 3

// A GeoM represents a matrix to transform geometry when rendering an image.
//
// The initial value is identity.
type GeoM struct {
	a_1 float64 // The actual 'a' value minus 1
	b   float64
	c   float64
	d_1 float64 // The actual 'd' value minus 1
	tx  float64
	ty  float64
}

// String returns a string representation of GeoM.
func (g *GeoM) String() string {
	return fmt.Sprintf("[[%f, %f, %f], [%f, %f, %f]]", g.a_1+1, g.b, g.tx, g.c, g.d_1+1, g.ty)
}

// Reset resets the GeoM as identity.
func (g *GeoM) Reset() {
	g.a_1 = 0
	g.b = 0
	g.c = 0
	g.d_1 = 0
	g.tx = 0
	g.ty = 0
}

// Apply pre-multiplies a vector (x, y, 1) by the matrix.
// In other words, Apply calculates GeoM * (x, y, 1)^T.
// The return value is x and y values of the result vector.
func (g *GeoM) Apply(x, y float64) (float64, float64) {
	return (g.a_1+1)*x + g.b*y + g.tx, g.c*x + (g.d_1+1)*y + g.ty
}

func (g *GeoM) elements32() (a, b, c, d, tx, ty float32) {
	return float32(g.a_1) + 1, float32(g.b), float32(g.c), float32(g.d_1) + 1, float32(g.tx), float32(g.ty)
}

// Element returns a value of a matrix at (i, j).
func (g *GeoM) Element(i, j int) float64 {
	switch {
	case i == 0 && j == 0:
		return g.a_1 + 1
	case i == 0 && j == 1:
		return g.b
	case i == 0 && j == 2:
		return g.tx
	case i == 1 && j == 0:
		return g.c
	case i == 1 && j == 1:
		return g.d_1 + 1
	case i == 1 && j == 2:
		return g.ty
	default:
		panic("ebiten: i or j is out of index")
	}
}

// Concat multiplies a geometry matrix with the other geometry matrix.
// This is same as muptiplying the matrix other and the matrix g in this order.
func (g *GeoM) Concat(other GeoM) {
	a := (other.a_1+1)*(g.a_1+1) + other.b*g.c
	b := (other.a_1+1)*g.b + other.b*(g.d_1+1)
	tx := (other.a_1+1)*g.tx + other.b*g.ty + other.tx
	c := other.c*(g.a_1+1) + (other.d_1+1)*g.c
	d := other.c*g.b + (other.d_1+1)*(g.d_1+1)
	ty := other.c*g.tx + (other.d_1+1)*g.ty + other.ty

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Add adds a matrix, but in a wrong way.
//
// Deprecated: (as of 1.5.0) Do not use this.
//
// Note that this doesn't make sense as an operation for affine matrices.
func (g *GeoM) Add(other GeoM) {
	g.a_1 += other.a_1
	g.b += other.b
	g.c += other.c
	g.d_1 += other.d_1
	g.tx += other.tx
	g.ty += other.ty
}

// Scale scales the matrix by (x, y).
func (g *GeoM) Scale(x, y float64) {
	a := (g.a_1 + 1) * x
	b := g.b * x
	tx := g.tx * x
	c := g.c * y
	d := (g.d_1 + 1) * y
	ty := g.ty * y

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Translate translates the matrix by (tx, ty).
func (g *GeoM) Translate(tx, ty float64) {
	g.tx += tx
	g.ty += ty
}

// Rotate rotates the matrix by theta.
// The unit is radian.
func (g *GeoM) Rotate(theta float64) {
	if theta == 0 {
		return
	}

	sin, cos := math.Sincos(theta)

	a := cos*(g.a_1+1) - sin*g.c
	b := cos*g.b - sin*(g.d_1+1)
	tx := cos*g.tx - sin*g.ty
	c := sin*(g.a_1+1) + cos*g.c
	d := sin*g.b + cos*(g.d_1+1)
	ty := sin*g.tx + cos*g.ty

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// Skew skews the matrix by (skewX, skewY). The unit is radian.
func (g *GeoM) Skew(skewX, skewY float64) {
	sx := math.Tan(skewX)
	sy := math.Tan(skewY)

	a := (g.a_1 + 1) + g.c*sx
	b := g.b + (g.d_1+1)*sx
	c := (g.a_1+1)*sy + g.c
	d := g.b*sy + (g.d_1 + 1)
	tx := g.tx + g.ty*sx
	ty := g.ty + g.tx*sy

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

func (g *GeoM) det2x2() float64 {
	return (g.a_1+1)*(g.d_1+1) - g.b*g.c
}

// IsInvertible returns a boolean value indicating
// whether the matrix g is invertible or not.
func (g *GeoM) IsInvertible() bool {
	return g.det2x2() != 0
}

// Invert inverts the matrix.
// If g is not invertible, Invert panics.
func (g *GeoM) Invert() {
	det := g.det2x2()
	if det == 0 {
		panic("ebiten: g is not invertible")
	}

	a := (g.d_1 + 1) / det
	b := -g.b / det
	c := -g.c / det
	d := (g.a_1 + 1) / det
	tx := (-(g.d_1+1)*g.tx + g.b*g.ty) / det
	ty := (g.c*g.tx + -(g.a_1+1)*g.ty) / det

	g.a_1 = a - 1
	g.b = b
	g.c = c
	g.d_1 = d - 1
	g.tx = tx
	g.ty = ty
}

// SetElement sets an element at (i, j).
func (g *GeoM) SetElement(i, j int, element float64) {
	e := element
	switch {
	case i == 0 && j == 0:
		g.a_1 = e - 1
	case i == 0 && j == 1:
		g.b = e
	case i == 0 && j == 2:
		g.tx = e
	case i == 1 && j == 0:
		g.c = e
	case i == 1 && j == 1:
		g.d_1 = e - 1
	case i == 1 && j == 2:
		g.ty = e
	default:
		panic("ebiten: i or j is out of index")
	}
}

// ScaleGeo returns a geometry matrix for scaling.
//
// Deprecated: (as of 1.2.0) Use Scale instead.
func ScaleGeo(x, y float64) GeoM {
	g := GeoM{}
	g.Scale(x, y)
	return g
}

// TranslateGeo returns a geometry matrix for translating.
//
// Deprecated: (as of 1.2.0) Use Translate instead.
func TranslateGeo(tx, ty float64) GeoM {
	g := GeoM{}
	g.Translate(tx, ty)
	return g
}

// RotateGeo returns a geometry matrix for rotating.
//
// Deprecated: (as of 1.2.0) Use Rotate instead.
func RotateGeo(theta float64) GeoM {
	g := GeoM{}
	g.Rotate(theta)
	return g
}
<|endoftext|>


<commit_before>package hypervisor

import (
	"net"
	"time"
)

const (
	StateStarting      = 0
	StateRunning       = 1
	StateFailedToStart = 2
	StateStopping      = 3
	StateStopped       = 4
	StateDestroying    = 5
)

type AcknowledgeVmRequest struct {
	IpAddress net.IP
}

type AcknowledgeVmResponse struct {
	Error string
}

type AddAddressesToPoolRequest struct {
	Addresses []Address
}

type AddAddressesToPoolResponse struct {
	Error string
}

type Address struct {
	IpAddress  net.IP `json:",omitempty"`
	MacAddress string
}

type AddSubnetsRequest struct {
	Subnets []Subnet
}

type AddSubnetsResponse struct {
	Error string
}

type ChangeVmOwnerUsersRequest struct {
	IpAddress  net.IP
	OwnerUsers []string
}

type ChangeVmOwnerUsersResponse struct {
	Error string
}

type ChangeVmTagsRequest struct {
	IpAddress net.IP
	Tags      map[string]string
}

type ChangeVmTagsResponse struct {
	Error string
}

type CreateVmRequest struct {
	DhcpTimeout      time.Duration
	ImageDataSize    uint64
	ImageTimeout     time.Duration
	MinimumFreeBytes uint64
	RoundupPower     uint64
	SecondaryVolumes []Volume
	UserDataSize     uint64
	VmInfo
} // RAW image data (length=ImageDataSize) and user data (length=UserDataSize)
// are streamed afterwards.

type CreateVmResponse struct { // Multiple responses are sent.
	DhcpTimedOut    bool
	Final           bool // If true, this is the final response.
	IpAddress       net.IP
	ProgressMessage string
	Error           string
}

type DestroyVmRequest struct {
	IpAddress net.IP
}

type DestroyVmResponse struct {
	Error string
}

type DiscardVmOldImageRequest struct {
	IpAddress net.IP
}

type DiscardVmOldImageResponse struct {
	Error string
}

type DiscardVmOldUserDataRequest struct {
	IpAddress net.IP
}

type DiscardVmOldUserDataResponse struct {
	Error string
}

// The GetUpdates() RPC is fully streamed.
// The client sends no information to the server.
// The server sends a stream of Update messages.

type Update struct {
	HaveAddressPool  bool               `json:",omitempty"`
	AddressPool      []Address          `json:",omitempty"` // Used & free.
	HaveNumFree      bool               `json:",omitempty"`
	NumFreeAddresses uint               `json:",omitempty"`
	HaveSubnets      bool               `json:",omitempty"`
	Subnets          []Subnet           `json:",omitempty"`
	HaveVMs          bool               `json:",omitempty"`
	VMs              map[string]*VmInfo `json:",omitempty"` // Key: IP address.
}

type GetVmInfoRequest struct {
	IpAddress net.IP
}

type GetVmInfoResponse struct {
	VmInfo VmInfo
	Error  string
}

type ListVMsRequest struct {
	Sort bool
}

type ListVMsResponse struct {
	IpAddresses []net.IP
}

type ProbeVmPortRequest struct {
	IpAddress  net.IP
	PortNumber uint
	Timeout    time.Duration
}

type ProbeVmPortResponse struct {
	PortIsOpen bool
	Error      string
}

type RemoveExcessAddressesFromPoolRequest struct {
	MaximumFreeAddresses uint
}

type RemoveExcessAddressesFromPoolResponse struct {
	Error string
}

type ReplaceVmImageRequest struct {
	DhcpTimeout      time.Duration
	ImageDataSize    uint64
	ImageName        string `json:",omitempty"`
	ImageTimeout     time.Duration
	ImageURL         string `json:",omitempty"`
	IpAddress        net.IP
	MinimumFreeBytes uint64
	RoundupPower     uint64
} // RAW image data (length=ImageDataSize) is streamed afterwards.

type ReplaceVmImageResponse struct { // Multiple responses are sent.
	DhcpTimedOut    bool
	Final           bool // If true, this is the final response.
	ProgressMessage string
	Error           string
}

type ReplaceVmUserDataRequest struct {
	IpAddress net.IP
	Size      uint64
} // User data (length=Size) are streamed afterwards.

type ReplaceVmUserDataResponse struct {
	Error string
}

type RestoreVmImageRequest struct {
	IpAddress net.IP
}

type RestoreVmImageResponse struct {
	Error string
}

type RestoreVmUserDataRequest struct {
	IpAddress net.IP
}

type RestoreVmUserDataResponse struct {
	Error string
}

type StartVmRequest struct {
	DhcpTimeout time.Duration
	IpAddress   net.IP
}

type StartVmResponse struct {
	DhcpTimedOut bool
	Error        string
}

type StopVmRequest struct {
	IpAddress net.IP
}

type StopVmResponse struct {
	Error string
}

type State uint

type Subnet struct {
	Id                string
	IpGateway         net.IP
	IpMask            net.IP // net.IPMask can't be JSON {en,de}coded.
	DomainName        string `json:",omitempty"`
	DomainNameServers []net.IP
}

type TraceVmMetadataRequest struct {
	IpAddress net.IP
}

type TraceVmMetadataResponse struct {
	Error string
} // A stream of strings (trace paths) follow.

type VmInfo struct {
	Address       Address
	Hostname      string `json:",omitempty"`
	ImageName     string `json:",omitempty"`
	ImageURL      string `json:",omitempty"`
	MemoryInMiB   uint64
	MilliCPUs     uint
	OwnerGroups   []string `json:",omitempty"`
	OwnerUsers    []string
	SpreadVolumes bool
	State         State
	Tags          map[string]string `json:",omitempty"`
	SubnetId      string
	Volumes       []Volume
}

type Volume struct {
	Size uint64
}
<commit_msg>Add snapshot management messages to proto/hypervisor package.<commit_after>package hypervisor

import (
	"net"
	"time"
)

const (
	StateStarting      = 0
	StateRunning       = 1
	StateFailedToStart = 2
	StateStopping      = 3
	StateStopped       = 4
	StateDestroying    = 5
)

type AcknowledgeVmRequest struct {
	IpAddress net.IP
}

type AcknowledgeVmResponse struct {
	Error string
}

type AddAddressesToPoolRequest struct {
	Addresses []Address
}

type AddAddressesToPoolResponse struct {
	Error string
}

type Address struct {
	IpAddress  net.IP `json:",omitempty"`
	MacAddress string
}

type AddSubnetsRequest struct {
	Subnets []Subnet
}

type AddSubnetsResponse struct {
	Error string
}

type ChangeVmOwnerUsersRequest struct {
	IpAddress  net.IP
	OwnerUsers []string
}

type ChangeVmOwnerUsersResponse struct {
	Error string
}

type ChangeVmTagsRequest struct {
	IpAddress net.IP
	Tags      map[string]string
}

type ChangeVmTagsResponse struct {
	Error string
}

type CreateVmRequest struct {
	DhcpTimeout      time.Duration
	ImageDataSize    uint64
	ImageTimeout     time.Duration
	MinimumFreeBytes uint64
	RoundupPower     uint64
	SecondaryVolumes []Volume
	UserDataSize     uint64
	VmInfo
} // RAW image data (length=ImageDataSize) and user data (length=UserDataSize)
// are streamed afterwards.

type CreateVmResponse struct { // Multiple responses are sent.
	DhcpTimedOut    bool
	Final           bool // If true, this is the final response.
	IpAddress       net.IP
	ProgressMessage string
	Error           string
}

type DestroyVmRequest struct {
	IpAddress net.IP
}

type DestroyVmResponse struct {
	Error string
}

type DiscardVmOldImageRequest struct {
	IpAddress net.IP
}

type DiscardVmOldImageResponse struct {
	Error string
}

type DiscardVmOldUserDataRequest struct {
	IpAddress net.IP
}

type DiscardVmOldUserDataResponse struct {
	Error string
}

type DiscardVmSnapshotRequest struct {
	IpAddress net.IP
}

type DiscardVmSnapshotResponse struct {
	Error string
}

// The GetUpdates() RPC is fully streamed.
// The client sends no information to the server.
// The server sends a stream of Update messages.

type Update struct {
	HaveAddressPool  bool               `json:",omitempty"`
	AddressPool      []Address          `json:",omitempty"` // Used & free.
	HaveNumFree      bool               `json:",omitempty"`
	NumFreeAddresses uint               `json:",omitempty"`
	HaveSubnets      bool               `json:",omitempty"`
	Subnets          []Subnet           `json:",omitempty"`
	HaveVMs          bool               `json:",omitempty"`
	VMs              map[string]*VmInfo `json:",omitempty"` // Key: IP address.
}

type GetVmInfoRequest struct {
	IpAddress net.IP
}

type GetVmInfoResponse struct {
	VmInfo VmInfo
	Error  string
}

type ListVMsRequest struct {
	Sort bool
}

type ListVMsResponse struct {
	IpAddresses []net.IP
}

type ProbeVmPortRequest struct {
	IpAddress  net.IP
	PortNumber uint
	Timeout    time.Duration
}

type ProbeVmPortResponse struct {
	PortIsOpen bool
	Error      string
}

type RemoveExcessAddressesFromPoolRequest struct {
	MaximumFreeAddresses uint
}

type RemoveExcessAddressesFromPoolResponse struct {
	Error string
}

type ReplaceVmImageRequest struct {
	DhcpTimeout      time.Duration
	ImageDataSize    uint64
	ImageName        string `json:",omitempty"`
	ImageTimeout     time.Duration
	ImageURL         string `json:",omitempty"`
	IpAddress        net.IP
	MinimumFreeBytes uint64
	RoundupPower     uint64
} // RAW image data (length=ImageDataSize) is streamed afterwards.

type ReplaceVmImageResponse struct { // Multiple responses are sent.
	DhcpTimedOut    bool
	Final           bool // If true, this is the final response.
	ProgressMessage string
	Error           string
}

type ReplaceVmUserDataRequest struct {
	IpAddress net.IP
	Size      uint64
} // User data (length=Size) are streamed afterwards.

type ReplaceVmUserDataResponse struct {
	Error string
}

type RestoreVmFromSnapshotRequest struct {
	IpAddress         net.IP
	ForceIfNotStopped bool
}

type RestoreVmFromSnapshotResponse struct {
	Error string
}

type RestoreVmImageRequest struct {
	IpAddress net.IP
}

type RestoreVmImageResponse struct {
	Error string
}

type RestoreVmUserDataRequest struct {
	IpAddress net.IP
}

type RestoreVmUserDataResponse struct {
	Error string
}

type SnapshotVmRequest struct {
	IpAddress         net.IP
	ForceIfNotStopped bool
	RootOnly          bool
}

type SnapshotVmResponse struct {
	Error string
}

type StartVmRequest struct {
	DhcpTimeout time.Duration
	IpAddress   net.IP
}

type StartVmResponse struct {
	DhcpTimedOut bool
	Error        string
}

type StopVmRequest struct {
	IpAddress net.IP
}

type StopVmResponse struct {
	Error string
}

type State uint

type Subnet struct {
	Id                string
	IpGateway         net.IP
	IpMask            net.IP // net.IPMask can't be JSON {en,de}coded.
	DomainName        string `json:",omitempty"`
	DomainNameServers []net.IP
}

type TraceVmMetadataRequest struct {
	IpAddress net.IP
}

type TraceVmMetadataResponse struct {
	Error string
} // A stream of strings (trace paths) follow.

type VmInfo struct {
	Address       Address
	Hostname      string `json:",omitempty"`
	ImageName     string `json:",omitempty"`
	ImageURL      string `json:",omitempty"`
	MemoryInMiB   uint64
	MilliCPUs     uint
	OwnerGroups   []string `json:",omitempty"`
	OwnerUsers    []string
	SpreadVolumes bool
	State         State
	Tags          map[string]string `json:",omitempty"`
	SubnetId      string
	Volumes       []Volume
}

type Volume struct {
	Size uint64
}
<|endoftext|>


<commit_before>// bridge bridges between IRC clients (RFC1459) and RobustIRC servers.
//
// Bridge instances are supposed to be long-running, and ideally as close to the
// IRC client as possible, e.g. on the same machine. When running on the same
// machine, there should not be any network problems between the IRC client and
// the bridge. Network problems between the bridge and a RobustIRC network are
// handled transparently.
package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"

	"github.com/robustirc/bridge/robustsession"

	"github.com/sorcix/irc"

	// Necessary on go1.0.2 (debian wheezy) to make crypto/tls work with the
	// certificates on robustirc.net (and possibly others).
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
)

var (
	network = flag.String("network",
		"",
		`DNS name to connect to (e.g. "robustirc.net"). The _robustirc._tcp SRV record must be present.`)

	listen = flag.String("listen",
		"localhost:6667",
		"host:port to listen on for IRC connections")

	socks = flag.String("socks",
		"localhost:1080",
		"host:port to listen on for SOCKS5 connections")

	tlsCertPath = flag.String("tls_cert_path",
		"",
		"Path to a .pem file containing the TLS certificate. If unspecified, TLS is not used.")

	tlsKeyPath = flag.String("tls_key_path",
		"",
		"Path to a .pem file containing the TLS private key. If unspecified, TLS is not used.")

	tlsCAFile = flag.String("tls_ca_file",
		"",
		"Use the specified file as trusted CA instead of the system CAs. Useful for testing.")

	motdPath = flag.String("motd_path",
		"/usr/share/robustirc/bridge-motd.txt",
		"Path to a text file containing the message of the day (MOTD) to prefix to the network MOTD.")
)

// TODO(secure): persistent state:
// - the last known server(s) in the network. added to *servers
// - for resuming sessions (later): the last seen message id, perhaps setup messages (JOINs, MODEs, …)
// for hosted mode, this state is stored per-nickname, ideally encrypted with password

// prefixMotd takes an irc.MOTD message from the server and prefixes it with
// our own MOTD. E.g., it takes:
//   :robustirc.net 372 sECuRE :- First line of MOTD\r\n
// and turns that into:
//   :robustirc.net 372 sECuRE :- First line of bridge MOTD\r\n
//   :robustirc.net 372 sECuRE :- Thanks for using this bridge! Enjoy!\r\n
//   :robustirc.net 372 sECuRE :- First line of MOTD\r\n
func prefixMotd(msg string) string {
	// The user chose to not inject a MOTD.
	if *motdPath == "" {
		return msg
	}

	sep := strings.Index(msg[1:], ":")
	if sep == -1 {
		return msg
	}

	prefix := msg[:sep+2] + "- "

	f, err := os.Open(*motdPath)
	if err != nil {
		log.Printf("Cannot inject MOTD: %v\n", err)
		return msg
	}
	defer f.Close()

	var injected []string

	reader := bufio.NewReader(f)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Printf("Cannot inject MOTD: %v\n", err)
			return msg
		}
		if len(line) > 0 && line[len(line)-1] == '\r' {
			line = line[:len(line)-1]
		}
		injected = append(injected, prefix+line)
	}

	return strings.Join(injected, "\r\n") + "\r\n" + msg
}

type bridge struct {
	network string
}

func newBridge(network string) *bridge {
	return &bridge{
		network: network,
	}
}

type ircsession struct {
	Messages chan irc.Message
	Errors   chan error

	conn    *irc.Conn
	rawConn net.Conn
}

func newIrcsession(conn net.Conn) *ircsession {
	s := &ircsession{
		Messages: make(chan irc.Message),
		Errors:   make(chan error),
		conn:     irc.NewConn(conn),
		rawConn:  conn,
	}
	go s.getMessages()
	return s
}

func (s *ircsession) Send(msg []byte) error {
	if _, err := s.conn.Write(msg); err != nil {
		return err
	}
	return nil
}

func (s *ircsession) Delete(killmsg string) error {
	defer s.conn.Close()
	// Read all remaining values to ensure nobody is blocked on sending.
	defer func() {
		go func() {
			for _ = range s.Messages {
			}
		}()
		go func() {
			for _ = range s.Errors {
			}
		}()
	}()

	if killmsg != "" {
		return s.conn.Encode(&irc.Message{
			Command:  "ERROR",
			Trailing: killmsg,
		})
	}

	return nil
}

func (s *ircsession) getMessages() {
	defer close(s.Messages)
	defer close(s.Errors)

	// Read the first byte. If it’s 4 or 5, this is likely SOCKS
	first := make([]byte, 1)
	if _, err := s.rawConn.Read(first); err != nil {
		s.Errors <- err
		return
	}

	// %x04 or %x05 as the first byte is both invalid according to RFC2812
	// section 2.3.1. Valid characters are ":" (%x3A) or %x30-39 (0-9) or
	// %x41-5A / %x61-7A (A-Z / a-z).
	// So we can just close the connection here, and also log that the user is
	// most likely trying to use SOCKS on the wrong port.
	//
	// TODO(secure): With some refactoring, we might even just handle the SOCKS
	// connection properly.
	if first[0] == 4 || first[0] == 5 {
		log.Printf("Read 0x%02x as first byte, which looks like a SOCKS version number. Please connect to %q instead of %q.\n", first[0], *socks, *listen)
		s.Errors <- fmt.Errorf("Read 0x%02x (SOCKS version?) as first byte on an IRC connection", first[0])
		return
	}

	line := []byte{first[0]}

	for first[0] != '\n' {
		if _, err := s.rawConn.Read(first); err != nil {
			s.Errors <- err
			return
		}
		line = append(line, first[0])
	}

	ircmsg := irc.ParseMessage(string(line))
	if ircmsg != nil {
		s.Messages <- *ircmsg
	}

	for {
		ircmsg, err := s.conn.Decode()
		if err != nil {
			s.Errors <- err
			return
		}
		// Skip invalid lines (to prevent nil pointer dereferences).
		if ircmsg == nil {
			continue
		}
		s.Messages <- *ircmsg
	}
}

func (p *bridge) handleIRC(conn net.Conn) {
	var quitmsg, killmsg string
	var waitingForPingReply bool

	ircSession := newIrcsession(conn)

	defer func() {
		log.Printf("Terminating IRC connection from %s. killmsg=%q\n", conn.RemoteAddr(), killmsg)
		if err := ircSession.Delete(killmsg); err != nil {
			log.Printf("Could not properly delete IRC session: %v\n", err)
		}
		// The separator makes it easier to read logs when the client is
		// reconnecting in a loop (which is the most common situation in which
		// you’re interested in the logs at all).
		log.Printf("\n")
	}()

	robustSession, err := robustsession.Create(p.network, *tlsCAFile)
	if err != nil {
		killmsg = fmt.Sprintf("Could not create RobustIRC session: %v", err)
		return
	}

	robustSession.ForwardedFor = conn.RemoteAddr().String()

	log.Printf("[session %s] Created RobustSession for client %s\n", robustSession.SessionId(), conn.RemoteAddr())

	defer func() {
		log.Printf("[session %s] Deleting RobustSession. quitmsg=%q\n", robustSession.SessionId(), quitmsg)
		if err := robustSession.Delete(quitmsg); err != nil {
			log.Printf("Could not properly delete RobustIRC session: %v\n", err)
		}
	}()

	var sendIRC, sendRobust []byte

	keepalivePong := ":" + robustSession.IrcPrefix.String() + " PONG keepalive"
	motdPrefix := ":" + robustSession.IrcPrefix.String() + " 372 "
	welcomePrefix := ":" + robustSession.IrcPrefix.String() + " 001 "
	welcomed := false
	motdInjected := false

	keepaliveToNetwork := time.After(1 * time.Minute)
	keepaliveToClient := time.After(1 * time.Minute)
	for {
		// These two variables contain the messages to be sent to IRC/RobustIRC
		// from the previous iteration of the state machine. That way, there is
		// only one place where the error handling happens.
		if sendIRC != nil {
			if err := ircSession.Send(sendIRC); err != nil {
				quitmsg = fmt.Sprintf("Bridge: Send to IRC client: %v", err)
				return
			}
			sendIRC = nil
		}
		if sendRobust != nil {
			if err := robustSession.PostMessage(string(sendRobust)); err != nil {
				killmsg = fmt.Sprintf("Could not post message to RobustIRC: %v", err)
				return
			}
			keepaliveToNetwork = time.After(1 * time.Minute)
			keepaliveToClient = time.After(1 * time.Minute)
			sendRobust = nil
		}

		select {
		case msg := <-robustSession.Messages:
			// Inject the bridge’s message of the day.
			if !motdInjected && strings.HasPrefix(msg, motdPrefix) {
				sendIRC = []byte(prefixMotd(msg))
				break
			}
			// For debugging purposes, print a log message when the client successfully logs into IRC.
			if !welcomed && strings.HasPrefix(msg, welcomePrefix) {
				log.Printf("[session %s] Successfully logged into IRC.\n", robustSession.SessionId())
				welcomed = true
			}
			if msg == keepalivePong {
				break
			}
			sendIRC = []byte(msg)

		case err := <-robustSession.Errors:
			killmsg = fmt.Sprintf("RobustIRC session error: %v", err)
			return

		case ircmsg := <-ircSession.Messages:
			switch strings.ToUpper(ircmsg.Command) {
			case irc.PONG:
				waitingForPingReply = false

			case irc.PING:
				sendIRC = (&irc.Message{
					Prefix:  robustSession.IrcPrefix,
					Command: irc.PONG,
					Params:  []string{ircmsg.Params[0]},
				}).Bytes()

			case irc.QUIT:
				quitmsg = ircmsg.Trailing
				return

			default:
				sendRobust = ircmsg.Bytes()
			}

		case err := <-ircSession.Errors:
			quitmsg = fmt.Sprintf("Bridge: Read from IRC client: %v", err)
			return

		case <-keepaliveToClient:
			// After no traffic in either direction for 1 minute, we send a PING
			// message. If a PING message was already sent, this means that we did
			// not receive a PONG message, so we close the connection with a
			// timeout.
			if waitingForPingReply {
				quitmsg = "Bridge: ping timeout"
				return
			}
			sendIRC = (&irc.Message{
				Prefix:  robustSession.IrcPrefix,
				Command: irc.PING,
				Params:  []string{"robustirc.bridge"},
			}).Bytes()
			waitingForPingReply = true

		case <-keepaliveToNetwork:
			sendRobust = []byte("PING keepalive")
			keepaliveToNetwork = time.After(1 * time.Minute)
		}
	}
}

// Copied from src/net/http/server.go
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	return tc, nil
}

// maybeTLSListener returns a net.Listener which possibly uses TLS, depending
// on the -tls_cert_path and -tls_key_path flag values.
func maybeTLSListener(addr string) net.Listener {
	if *tlsCertPath == "" || *tlsKeyPath == "" {
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			log.Fatal(err)
		}
		return ln
	}

	tlsconfig := &tls.Config{
		Certificates: make([]tls.Certificate, 1),
	}

	var err error
	tlsconfig.Certificates[0], err = tls.LoadX509KeyPair(*tlsCertPath, *tlsKeyPath)
	if err != nil {
		log.Fatal(err)
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal(err)
	}

	return tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, tlsconfig)
}

func main() {
	flag.Parse()

	rand.Seed(time.Now().Unix())

	if (*network == "" && *socks == "") ||
		(*socks == "" && *listen == "") {
		log.Fatal("You must specify either -network and -listen, or -socks.")
	}

	// SOCKS and IRC
	if *socks != "" && *network != "" && *listen != "" {
		go func() {
			log.Printf("RobustIRC IRC bridge listening on %q (SOCKS)\n", *socks)
			if err := serveSocks(maybeTLSListener(*socks)); err != nil {
				log.Fatal(err)
			}
		}()
	}

	// SOCKS only
	if *socks != "" && (*network == "" || *listen == "") {
		log.Printf("RobustIRC IRC bridge listening on %q (SOCKS)\n", *socks)
		log.Fatal(serveSocks(maybeTLSListener(*socks)))
	}

	// IRC
	if *network != "" && *listen != "" {
		p := newBridge(*network)
		ln := maybeTLSListener(*listen)

		log.Printf("RobustIRC IRC bridge listening on %q (IRC)\n", *listen)

		for {
			conn, err := ln.Accept()
			if err != nil {
				log.Printf("Could not accept IRC client connection: %v\n", err)
				continue
			}
			go p.handleIRC(conn)
		}
	}
}
<commit_msg>Send PING in such a way that ZNC understands it (Thanks Jan).<commit_after>// bridge bridges between IRC clients (RFC1459) and RobustIRC servers.
//
// Bridge instances are supposed to be long-running, and ideally as close to the
// IRC client as possible, e.g. on the same machine. When running on the same
// machine, there should not be any network problems between the IRC client and
// the bridge. Network problems between the bridge and a RobustIRC network are
// handled transparently.
package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"

	"github.com/robustirc/bridge/robustsession"

	"github.com/sorcix/irc"

	// Necessary on go1.0.2 (debian wheezy) to make crypto/tls work with the
	// certificates on robustirc.net (and possibly others).
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
)

var (
	network = flag.String("network",
		"",
		`DNS name to connect to (e.g. "robustirc.net"). The _robustirc._tcp SRV record must be present.`)

	listen = flag.String("listen",
		"localhost:6667",
		"host:port to listen on for IRC connections")

	socks = flag.String("socks",
		"localhost:1080",
		"host:port to listen on for SOCKS5 connections")

	tlsCertPath = flag.String("tls_cert_path",
		"",
		"Path to a .pem file containing the TLS certificate. If unspecified, TLS is not used.")

	tlsKeyPath = flag.String("tls_key_path",
		"",
		"Path to a .pem file containing the TLS private key. If unspecified, TLS is not used.")

	tlsCAFile = flag.String("tls_ca_file",
		"",
		"Use the specified file as trusted CA instead of the system CAs. Useful for testing.")

	motdPath = flag.String("motd_path",
		"/usr/share/robustirc/bridge-motd.txt",
		"Path to a text file containing the message of the day (MOTD) to prefix to the network MOTD.")
)

// TODO(secure): persistent state:
// - the last known server(s) in the network. added to *servers
// - for resuming sessions (later): the last seen message id, perhaps setup messages (JOINs, MODEs, …)
// for hosted mode, this state is stored per-nickname, ideally encrypted with password

// prefixMotd takes an irc.MOTD message from the server and prefixes it with
// our own MOTD. E.g., it takes:
//   :robustirc.net 372 sECuRE :- First line of MOTD\r\n
// and turns that into:
//   :robustirc.net 372 sECuRE :- First line of bridge MOTD\r\n
//   :robustirc.net 372 sECuRE :- Thanks for using this bridge! Enjoy!\r\n
//   :robustirc.net 372 sECuRE :- First line of MOTD\r\n
func prefixMotd(msg string) string {
	// The user chose to not inject a MOTD.
	if *motdPath == "" {
		return msg
	}

	sep := strings.Index(msg[1:], ":")
	if sep == -1 {
		return msg
	}

	prefix := msg[:sep+2] + "- "

	f, err := os.Open(*motdPath)
	if err != nil {
		log.Printf("Cannot inject MOTD: %v\n", err)
		return msg
	}
	defer f.Close()

	var injected []string

	reader := bufio.NewReader(f)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Printf("Cannot inject MOTD: %v\n", err)
			return msg
		}
		if len(line) > 0 && line[len(line)-1] == '\r' {
			line = line[:len(line)-1]
		}
		injected = append(injected, prefix+line)
	}

	return strings.Join(injected, "\r\n") + "\r\n" + msg
}

type bridge struct {
	network string
}

func newBridge(network string) *bridge {
	return &bridge{
		network: network,
	}
}

type ircsession struct {
	Messages chan irc.Message
	Errors   chan error

	conn    *irc.Conn
	rawConn net.Conn
}

func newIrcsession(conn net.Conn) *ircsession {
	s := &ircsession{
		Messages: make(chan irc.Message),
		Errors:   make(chan error),
		conn:     irc.NewConn(conn),
		rawConn:  conn,
	}
	go s.getMessages()
	return s
}

func (s *ircsession) Send(msg []byte) error {
	if _, err := s.conn.Write(msg); err != nil {
		return err
	}
	return nil
}

func (s *ircsession) Delete(killmsg string) error {
	defer s.conn.Close()
	// Read all remaining values to ensure nobody is blocked on sending.
	defer func() {
		go func() {
			for _ = range s.Messages {
			}
		}()
		go func() {
			for _ = range s.Errors {
			}
		}()
	}()

	if killmsg != "" {
		return s.conn.Encode(&irc.Message{
			Command:  "ERROR",
			Trailing: killmsg,
		})
	}

	return nil
}

func (s *ircsession) getMessages() {
	defer close(s.Messages)
	defer close(s.Errors)

	// Read the first byte. If it’s 4 or 5, this is likely SOCKS
	first := make([]byte, 1)
	if _, err := s.rawConn.Read(first); err != nil {
		s.Errors <- err
		return
	}

	// %x04 or %x05 as the first byte is both invalid according to RFC2812
	// section 2.3.1. Valid characters are ":" (%x3A) or %x30-39 (0-9) or
	// %x41-5A / %x61-7A (A-Z / a-z).
	// So we can just close the connection here, and also log that the user is
	// most likely trying to use SOCKS on the wrong port.
	//
	// TODO(secure): With some refactoring, we might even just handle the SOCKS
	// connection properly.
	if first[0] == 4 || first[0] == 5 {
		log.Printf("Read 0x%02x as first byte, which looks like a SOCKS version number. Please connect to %q instead of %q.\n", first[0], *socks, *listen)
		s.Errors <- fmt.Errorf("Read 0x%02x (SOCKS version?) as first byte on an IRC connection", first[0])
		return
	}

	line := []byte{first[0]}

	for first[0] != '\n' {
		if _, err := s.rawConn.Read(first); err != nil {
			s.Errors <- err
			return
		}
		line = append(line, first[0])
	}

	ircmsg := irc.ParseMessage(string(line))
	if ircmsg != nil {
		s.Messages <- *ircmsg
	}

	for {
		ircmsg, err := s.conn.Decode()
		if err != nil {
			s.Errors <- err
			return
		}
		// Skip invalid lines (to prevent nil pointer dereferences).
		if ircmsg == nil {
			continue
		}
		s.Messages <- *ircmsg
	}
}

func (p *bridge) handleIRC(conn net.Conn) {
	var quitmsg, killmsg string
	var waitingForPingReply bool

	ircSession := newIrcsession(conn)

	defer func() {
		log.Printf("Terminating IRC connection from %s. killmsg=%q\n", conn.RemoteAddr(), killmsg)
		if err := ircSession.Delete(killmsg); err != nil {
			log.Printf("Could not properly delete IRC session: %v\n", err)
		}
		// The separator makes it easier to read logs when the client is
		// reconnecting in a loop (which is the most common situation in which
		// you’re interested in the logs at all).
		log.Printf("\n")
	}()

	robustSession, err := robustsession.Create(p.network, *tlsCAFile)
	if err != nil {
		killmsg = fmt.Sprintf("Could not create RobustIRC session: %v", err)
		return
	}

	robustSession.ForwardedFor = conn.RemoteAddr().String()

	log.Printf("[session %s] Created RobustSession for client %s\n", robustSession.SessionId(), conn.RemoteAddr())

	defer func() {
		log.Printf("[session %s] Deleting RobustSession. quitmsg=%q\n", robustSession.SessionId(), quitmsg)
		if err := robustSession.Delete(quitmsg); err != nil {
			log.Printf("Could not properly delete RobustIRC session: %v\n", err)
		}
	}()

	var sendIRC, sendRobust []byte

	keepalivePong := ":" + robustSession.IrcPrefix.String() + " PONG keepalive"
	motdPrefix := ":" + robustSession.IrcPrefix.String() + " 372 "
	welcomePrefix := ":" + robustSession.IrcPrefix.String() + " 001 "
	welcomed := false
	motdInjected := false

	keepaliveToNetwork := time.After(1 * time.Minute)
	keepaliveToClient := time.After(1 * time.Minute)
	for {
		// These two variables contain the messages to be sent to IRC/RobustIRC
		// from the previous iteration of the state machine. That way, there is
		// only one place where the error handling happens.
		if sendIRC != nil {
			if err := ircSession.Send(sendIRC); err != nil {
				quitmsg = fmt.Sprintf("Bridge: Send to IRC client: %v", err)
				return
			}
			sendIRC = nil
		}
		if sendRobust != nil {
			if err := robustSession.PostMessage(string(sendRobust)); err != nil {
				killmsg = fmt.Sprintf("Could not post message to RobustIRC: %v", err)
				return
			}
			keepaliveToNetwork = time.After(1 * time.Minute)
			keepaliveToClient = time.After(1 * time.Minute)
			sendRobust = nil
		}

		select {
		case msg := <-robustSession.Messages:
			// Inject the bridge’s message of the day.
			if !motdInjected && strings.HasPrefix(msg, motdPrefix) {
				sendIRC = []byte(prefixMotd(msg))
				break
			}
			// For debugging purposes, print a log message when the client successfully logs into IRC.
			if !welcomed && strings.HasPrefix(msg, welcomePrefix) {
				log.Printf("[session %s] Successfully logged into IRC.\n", robustSession.SessionId())
				welcomed = true
			}
			if msg == keepalivePong {
				break
			}
			sendIRC = []byte(msg)

		case err := <-robustSession.Errors:
			killmsg = fmt.Sprintf("RobustIRC session error: %v", err)
			return

		case ircmsg := <-ircSession.Messages:
			switch strings.ToUpper(ircmsg.Command) {
			case irc.PONG:
				waitingForPingReply = false

			case irc.PING:
				sendIRC = (&irc.Message{
					Prefix:  robustSession.IrcPrefix,
					Command: irc.PONG,
					Params:  []string{ircmsg.Params[0]},
				}).Bytes()

			case irc.QUIT:
				quitmsg = ircmsg.Trailing
				return

			default:
				sendRobust = ircmsg.Bytes()
			}

		case err := <-ircSession.Errors:
			quitmsg = fmt.Sprintf("Bridge: Read from IRC client: %v", err)
			return

		case <-keepaliveToClient:
			// After no traffic in either direction for 1 minute, we send a PING
			// message. If a PING message was already sent, this means that we did
			// not receive a PONG message, so we close the connection with a
			// timeout.
			if waitingForPingReply {
				quitmsg = "Bridge: ping timeout"
				return
			}
			sendIRC = (&irc.Message{
				Command: irc.PING,
				Params:  []string{"robustirc.bridge"},
			}).Bytes()
			waitingForPingReply = true

		case <-keepaliveToNetwork:
			sendRobust = []byte("PING keepalive")
			keepaliveToNetwork = time.After(1 * time.Minute)
		}
	}
}

// Copied from src/net/http/server.go
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	return tc, nil
}

// maybeTLSListener returns a net.Listener which possibly uses TLS, depending
// on the -tls_cert_path and -tls_key_path flag values.
func maybeTLSListener(addr string) net.Listener {
	if *tlsCertPath == "" || *tlsKeyPath == "" {
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			log.Fatal(err)
		}
		return ln
	}

	tlsconfig := &tls.Config{
		Certificates: make([]tls.Certificate, 1),
	}

	var err error
	tlsconfig.Certificates[0], err = tls.LoadX509KeyPair(*tlsCertPath, *tlsKeyPath)
	if err != nil {
		log.Fatal(err)
	}

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal(err)
	}

	return tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, tlsconfig)
}

func main() {
	flag.Parse()

	rand.Seed(time.Now().Unix())

	if (*network == "" && *socks == "") ||
		(*socks == "" && *listen == "") {
		log.Fatal("You must specify either -network and -listen, or -socks.")
	}

	// SOCKS and IRC
	if *socks != "" && *network != "" && *listen != "" {
		go func() {
			log.Printf("RobustIRC IRC bridge listening on %q (SOCKS)\n", *socks)
			if err := serveSocks(maybeTLSListener(*socks)); err != nil {
				log.Fatal(err)
			}
		}()
	}

	// SOCKS only
	if *socks != "" && (*network == "" || *listen == "") {
		log.Printf("RobustIRC IRC bridge listening on %q (SOCKS)\n", *socks)
		log.Fatal(serveSocks(maybeTLSListener(*socks)))
	}

	// IRC
	if *network != "" && *listen != "" {
		p := newBridge(*network)
		ln := maybeTLSListener(*listen)

		log.Printf("RobustIRC IRC bridge listening on %q (IRC)\n", *listen)

		for {
			conn, err := ln.Accept()
			if err != nil {
				log.Printf("Could not accept IRC client connection: %v\n", err)
				continue
			}
			go p.handleIRC(conn)
		}
	}
}
<|endoftext|>