tacquito/crypt_test.go

194 lines
5.7 KiB
Go

/*
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
package tacquito
import (
"fmt"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/assert"
)
// returns an encrypted TACACs+ packet's byte values, contains the 12 byte header
// encrypted with secret []byte("fooman")
func getEncryptedBytes() []byte {
return []byte{0xc1, 0x01, 0x01, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x2c, 0x9c, 0xed, 0x73,
0xaa, 0x3d, 0x6d, 0x2f, 0x1f, 0xef, 0x62, 0x98, 0x73, 0xf0, 0xac, 0x2f, 0x11, 0x8a, 0xe2, 0x89, 0x8a,
0xcb, 0x50, 0x72, 0xb2, 0x6d, 0xd2, 0xec, 0xab, 0xe1, 0x4e, 0x22, 0x64, 0x4c, 0x7c, 0xb2, 0xe, 0x43,
0xe, 0x33, 0x92, 0x85, 0x47, 0xca, 0xfc}
}
// returns a decrypted TACACs+ packet's byte values, does NOT contain the 12 byte header
func getDecryptedBytes() []byte {
return []byte{0x01, 0x01, 0x01, 0x01, 0x05, 0x0B, 0x14, 0x00, 0x61, 0x64, 0x6D, 0x69, 0x6E, 0x63, 0x6F,
0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x2D, 0x61, 0x70, 0x69, 0x32, 0x30, 0x30, 0x31, 0x3A, 0x34, 0x38,
0x36, 0x30, 0x3A, 0x34, 0x38, 0x36, 0x30, 0x3A, 0x3A, 0x38, 0x38, 0x38, 0x38}
}
func TestDecrypt(t *testing.T) {
encrypted := getEncryptedBytes()
decrypted := getDecryptedBytes()
var header Header
err := Unmarshal(encrypted[:12], &header)
assert.NoError(t, err)
packet := &Packet{Header: &header, Body: encrypted[12:]}
err = crypt([]byte("fooman"), packet)
assert.NoError(t, err)
assert.Equal(t, decrypted, packet.Body)
var body AuthenStart
err = Unmarshal(packet.Body, &body)
assert.NoError(t, err)
t.Log(spew.Sdump(body))
}
func TestEncrypt(t *testing.T) {
encrypted := getEncryptedBytes()
decrypted := getDecryptedBytes()
body, _ := NewAuthenStart(
SetAuthenStartAction(AuthenActionLogin),
SetAuthenStartPrivLvl(PrivLvlUser),
SetAuthenStartType(AuthenTypeASCII),
SetAuthenStartService(AuthenServiceLogin),
SetAuthenStartUser("admin"),
SetAuthenStartPort("command-api"),
SetAuthenStartRemAddr("2001:4860:4860::8888"),
).MarshalBinary()
packet := NewPacket(
SetPacketHeader(
NewHeader(
SetHeaderVersion(Version{MajorVersion: MajorVersion, MinorVersion: MinorVersionOne}),
SetHeaderType(Authenticate),
SetHeaderSessionID(12345),
),
),
SetPacketBody(body),
)
t.Log(spew.Sdump(packet))
err := crypt([]byte("fooman"), packet)
assert.NoError(t, err)
t.Log(spew.Sdump(packet))
assert.Equal(t, encrypted[12:], packet.Body)
err = crypt([]byte("fooman"), packet)
assert.NoError(t, err)
t.Log(spew.Sdump(packet))
assert.Equal(t, decrypted, packet.Body)
}
func TestEncryptDecryptSecretMismatch(t *testing.T) {
body := NewAuthenReply(
SetAuthenReplyStatus(AuthenStatusGetUser),
SetAuthenReplyServerMsg("\nUser Access Verification\n\nUsername:"),
)
b, _ := body.MarshalBinary()
packet := NewPacket(
SetPacketHeader(
NewHeader(
SetHeaderVersion(Version{MajorVersion: MajorVersion, MinorVersion: MinorVersionOne}),
SetHeaderType(Authenticate),
SetHeaderSessionID(12345),
),
),
SetPacketBody(b),
)
secret := []byte("chilled cow")
err := crypt(secret, packet)
assert.NoError(t, err)
// We need to ensure Encrypt and Decrypt operations result in an error if a secret mismatches
// ensure secret mismatch causes an error
secret = []byte("imma bad secret")
err = crypt(secret, packet)
assert.NoError(t, err)
// Unmarshal decrypted bytes back into original packet body type
// this should cause a malformed packet error because of a secret mismatch when encrypt/decrypt
newAuthenReply := &AuthenReply{}
err = newAuthenReply.UnmarshalBinary(packet.Body)
assert.Error(t, err, "a bad secret change should have caused this packet to be malformed")
assert.NotEqual(t, *body, *newAuthenReply)
}
func TestPacketEncryptDecryptUnencryptFlagSet(t *testing.T) {
body := NewAuthenReply(
SetAuthenReplyStatus(AuthenStatusGetUser),
SetAuthenReplyServerMsg("\nUser Access Verification\n\nUsername:"),
)
b, _ := body.MarshalBinary()
packet := NewPacket(
SetPacketHeader(
NewHeader(
SetHeaderVersion(Version{MajorVersion: MajorVersion, MinorVersion: MinorVersionOne}),
SetHeaderType(Authenticate),
SetHeaderFlag(UnencryptedFlag),
SetHeaderSessionID(12345),
),
),
SetPacketBody(b),
)
secret := []byte("chilled cow")
err := crypt(secret, packet)
assert.NoError(t, err)
assert.Equal(t, b, packet.Body)
err = crypt(secret, packet)
assert.NoError(t, err)
assert.Equal(t, b, packet.Body)
}
// benchTest is used for allocation testing
type benchTest struct {
name string
fn func(b *testing.B)
expected func(name string, r testing.BenchmarkResult)
}
func TestCrypterAllocation(t *testing.T) {
tests := []benchTest{
{
name: "encrypt",
fn: BenchmarkCrypterAllocation,
expected: func(name string, r testing.BenchmarkResult) {
t.Log(spew.Sdump(r))
expectedAllocs := 4
actual := r.AllocsPerOp()
assert.EqualValues(t, expectedAllocs, actual, fmt.Sprintf("%s allocations were not nominal; wanted %v got %v", name, expectedAllocs, actual))
},
},
}
for _, test := range tests {
r := testing.Benchmark(test.fn)
test.expected(test.name, r)
}
}
// BenchmarkCrypterAllocation benchmarks the allocs/op crypter takes when called with crypted
// or decrypted bytes. Since the op is the same in both directions we only test one form of it
func BenchmarkCrypterAllocation(b *testing.B) {
encrypted := getEncryptedBytes()
var header Header
Unmarshal(encrypted[:12], &header)
packet := &Packet{Header: &header, Body: encrypted[12:]}
secret := []byte("fooman")
// record allocations regardless of go test -test.bench
b.ReportAllocs()
for range b.N {
crypt(secret, packet)
}
}