Add support for Authenticated Encryption with Associated Data (AEAD) (#147)

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2016-06-29 09:10:41 -07:00 committed by Vish Ishaya
parent 77483a0e69
commit 2b8dd8b419
5 changed files with 152 additions and 9 deletions

View File

@ -10,6 +10,7 @@ const (
SizeofXfrmUsersaInfo = 0xe0
SizeofXfrmAlgo = 0x44
SizeofXfrmAlgoAuth = 0x48
SizeofXfrmAlgoAEAD = 0x48
SizeofXfrmEncapTmpl = 0x18
SizeofXfrmUsersaFlush = 0x8
)
@ -194,6 +195,35 @@ func (msg *XfrmAlgoAuth) Serialize() []byte {
// char alg_key[0];
// }
type XfrmAlgoAEAD struct {
AlgName [64]byte
AlgKeyLen uint32
AlgICVLen uint32
AlgKey []byte
}
func (msg *XfrmAlgoAEAD) Len() int {
return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8)
}
func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD {
ret := XfrmAlgoAEAD{}
copy(ret.AlgName[:], b[0:64])
ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68]))
ret.AlgKey = b[72:ret.Len()]
return &ret
}
func (msg *XfrmAlgoAEAD) Serialize() []byte {
b := make([]byte, msg.Len())
copy(b[0:64], msg.AlgName[:])
copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:])
copy(b[72:msg.Len()], msg.AlgKey[:])
return b
}
// struct xfrm_encap_tmpl {
// __u16 encap_type;
// __be16 encap_sport;

View File

@ -239,3 +239,39 @@ func TestXfrmMarkDeserializeSerialize(t *testing.T) {
msg := DeserializeXfrmMark(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}
func (msg *XfrmAlgoAEAD) write(b []byte) {
native := NativeEndian()
copy(b[0:64], msg.AlgName[:])
native.PutUint32(b[64:68], msg.AlgKeyLen)
native.PutUint32(b[68:72], msg.AlgICVLen)
copy(b[72:msg.Len()], msg.AlgKey[:])
}
func (msg *XfrmAlgoAEAD) serializeSafe() []byte {
b := make([]byte, msg.Len())
msg.write(b)
return b
}
func deserializeXfrmAlgoAEADSafe(b []byte) *XfrmAlgoAEAD {
var msg = XfrmAlgoAEAD{}
copy(msg.AlgName[:], b[0:64])
binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgICVLen)
msg.AlgKey = b[72:msg.Len()]
return &msg
}
func TestXfrmXfrmAlgoAeadDeserializeSerialize(t *testing.T) {
native := NativeEndian()
// use a 32 byte key len
var orig = make([]byte, SizeofXfrmAlgoAEAD+36)
rand.Read(orig)
// set the key len to (256 + 32) bits
var KeyLen uint32 = 0x00000120
native.PutUint32(orig[64:68], KeyLen)
safemsg := deserializeXfrmAlgoAEADSafe(orig)
msg := DeserializeXfrmAlgoAEAD(orig)
testDeserializeSerialize(t, orig, safemsg, msg)
}

View File

@ -10,10 +10,18 @@ type XfrmStateAlgo struct {
Name string
Key []byte
TruncateLen int // Auth only
ICVLen int // AEAD only
}
func (a XfrmStateAlgo) String() string {
return fmt.Sprintf("{Name: %s, Key: 0x%x, TruncateLen: %d}", a.Name, a.Key, a.TruncateLen)
base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key)
if a.TruncateLen != 0 {
base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen)
}
if a.ICVLen != 0 {
base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen)
}
return fmt.Sprintf("%s}", base)
}
// EncapType is an enum representing the optional packet encapsulation.
@ -73,12 +81,13 @@ type XfrmState struct {
Mark *XfrmMark
Auth *XfrmStateAlgo
Crypt *XfrmStateAlgo
Aead *XfrmStateAlgo
Encap *XfrmStateEncap
}
func (sa XfrmState) String() string {
return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Encap: %v",
sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Encap)
return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v,Encap: %v",
sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap)
}
func (sa XfrmState) Print(stats bool) string {
if !stats {

View File

@ -35,6 +35,20 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
return algo.Serialize()
}
func writeStateAlgoAead(a *XfrmStateAlgo) []byte {
algo := nl.XfrmAlgoAEAD{
AlgKeyLen: uint32(len(a.Key) * 8),
AlgICVLen: uint32(a.ICVLen),
AlgKey: a.Key,
}
end := len(a.Name)
if end > 64 {
end = 64
}
copy(algo.AlgName[:end], a.Name)
return algo.Serialize()
}
func writeMark(m *XfrmMark) []byte {
mark := &nl.XfrmMark{
Value: m.Value,
@ -97,6 +111,10 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
req.AddData(out)
}
if state.Aead != nil {
out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead))
req.AddData(out)
}
if state.Encap != nil {
encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
encap := nl.DeserializeXfrmEncapTmpl(encapData)
@ -271,6 +289,12 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
state.Auth.Name = nl.BytesToString(algo.AlgName[:])
state.Auth.Key = algo.AlgKey
state.Auth.TruncateLen = int(algo.AlgTruncLen)
case nl.XFRMA_ALG_AEAD:
state.Aead = new(XfrmStateAlgo)
algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:])
state.Aead.Name = nl.BytesToString(algo.AlgName[:])
state.Aead.Key = algo.AlgKey
state.Aead.ICVLen = int(algo.AlgICVLen)
case nl.XFRMA_ENCAP:
encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
state.Encap = new(XfrmStateEncap)

View File

@ -2,16 +2,20 @@ package netlink
import (
"bytes"
"encoding/hex"
"net"
"testing"
)
func TestXfrmStateAddGetDel(t *testing.T) {
for _, s := range []*XfrmState{getBaseState(), getAeadState()} {
testXfrmStateAddGetDel(t, s)
}
}
func testXfrmStateAddGetDel(t *testing.T, state *XfrmState) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
state := getBaseState()
if err := XfrmStateAdd(state); err != nil {
t.Fatal(err)
}
@ -176,6 +180,23 @@ func getBaseState() *XfrmState {
}
}
func getAeadState() *XfrmState {
// 128 key bits + 32 salt bits
k, _ := hex.DecodeString("d0562776bf0e75830ba3f7f8eb6c09b555aa1177")
return &XfrmState{
Src: net.ParseIP("192.168.1.1"),
Dst: net.ParseIP("192.168.2.2"),
Proto: XFRM_PROTO_ESP,
Mode: XFRM_MODE_TUNNEL,
Spi: 2,
Aead: &XfrmStateAlgo{
Name: "rfc4106(gcm(aes))",
Key: k,
ICVLen: 64,
},
}
}
func compareStates(a, b *XfrmState) bool {
if a == b {
return true
@ -185,9 +206,10 @@ func compareStates(a, b *XfrmState) bool {
}
return a.Src.Equal(b.Src) && a.Dst.Equal(b.Dst) &&
a.Mode == b.Mode && a.Spi == b.Spi && a.Proto == b.Proto &&
a.Auth.Name == b.Auth.Name && bytes.Equal(a.Auth.Key, b.Auth.Key) &&
a.Crypt.Name == b.Crypt.Name && bytes.Equal(a.Crypt.Key, b.Crypt.Key) &&
a.Mark.Value == b.Mark.Value && a.Mark.Mask == b.Mark.Mask
compareAlgo(a.Auth, b.Auth) &&
compareAlgo(a.Crypt, b.Crypt) &&
compareAlgo(a.Aead, b.Aead) &&
compareMarks(a.Mark, b.Mark)
}
func compareLimits(a, b *XfrmState) bool {
@ -200,3 +222,25 @@ func compareLimits(a, b *XfrmState) bool {
a.Limits.TimeUseHard == b.Limits.TimeUseHard &&
a.Limits.TimeUseSoft == b.Limits.TimeUseSoft
}
func compareAlgo(a, b *XfrmStateAlgo) bool {
if a == b {
return true
}
if a == nil || b == nil {
return false
}
return a.Name == b.Name && bytes.Equal(a.Key, b.Key) &&
(a.TruncateLen == 0 || a.TruncateLen == b.TruncateLen) &&
(a.ICVLen == 0 || a.ICVLen == b.ICVLen)
}
func compareMarks(a, b *XfrmMark) bool {
if a == b {
return true
}
if a == nil || b == nil {
return false
}
return a.Value == b.Value && a.Mask == b.Mask
}