Skip to content

Commit

Permalink
ndp: implement Nonce option
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Layher <mdlayher@gmail.com>
  • Loading branch information
mdlayher committed Mar 23, 2022
1 parent 0da72a5 commit 0e15311
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 4 deletions.
64 changes: 63 additions & 1 deletion option.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package ndp

import (
"bytes"
"crypto/subtle"
"encoding/binary"
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/netip"
"net/url"
Expand Down Expand Up @@ -34,6 +36,7 @@ const (
optTargetLLA = 2
optPrefixInformation = 3
optMTU = 5
optNonce = 14
optRouteInformation = 24
optRDNSS = 25
optDNSSL = 31
Expand Down Expand Up @@ -699,7 +702,7 @@ func NewCaptivePortal(uri string) (*CaptivePortal, error) {
}

// Code implements Option.
func (CaptivePortal) Code() byte { return optCaptivePortal }
func (*CaptivePortal) Code() byte { return optCaptivePortal }

func (cp *CaptivePortal) marshal() ([]byte, error) {
if len(cp.URI) == 0 {
Expand Down Expand Up @@ -749,6 +752,63 @@ func (cp *CaptivePortal) unmarshal(b []byte) error {
return nil
}

// A Nonce is a Nonce option, as described in RFC 3971, Section 5.3.2.
type Nonce struct {
b []byte
}

// NewNonce creates a Nonce option with an opaque random value.
func NewNonce() *Nonce {
// Minimum is 6 bytes but the final length of the message must be a multiple
// of 8 bytes, so we'll go with 14 for now and re-evaluate later.
const n = 14
b := make([]byte, n)
_, _ = rand.Read(b)

return &Nonce{b: b}
}

// Equal reports whether n and x are the same nonce.
func (n *Nonce) Equal(x *Nonce) bool { return subtle.ConstantTimeCompare(n.b, x.b) == 1 }

// Code implements Option.
func (*Nonce) Code() byte { return optNonce }

func (n *Nonce) marshal() ([]byte, error) {
if len(n.b) == 0 {
return nil, errors.New("ndp: nonce option requires a non-empty nonce value")
}

// Enforce the nonce size matches the next unit of 8 bytes including 2 bytes
// for code and length.
l := len(n.b)
if r := (l + 2) % 8; r != 0 {
return nil, errors.New("ndp: nonce size is invalid")
}

value := make([]byte, l)
copy(value, n.b)

raw := &RawOption{
Type: n.Code(),
Length: (uint8(l) + 2) / 8,
Value: value,
}

return raw.marshal()
}

func (n *Nonce) unmarshal(b []byte) error {
raw := new(RawOption)
if err := raw.unmarshal(b); err != nil {
return err
}

// raw already made a copy.
n.b = raw.Value
return nil
}

var _ Option = &RawOption{}

// A RawOption is an Option in its raw and unprocessed format. Options which
Expand Down Expand Up @@ -851,6 +911,8 @@ func parseOptions(b []byte) ([]Option, error) {
o = new(DNSSearchList)
case optCaptivePortal:
o = new(CaptivePortal)
case optNonce:
o = new(Nonce)
default:
o = new(RawOption)
}
Expand Down
43 changes: 40 additions & 3 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ func TestOptionMarshalUnmarshal(t *testing.T) {
name: "MTU",
subs: []optionSub{{
name: "ok",
os: []Option{
NewMTU(1500),
},
os: []Option{NewMTU(1500)},
bs: [][]byte{
{0x05, 0x01, 0x00, 0x00},
{0x00, 0x00, 0x05, 0xdc},
Expand Down Expand Up @@ -69,6 +67,10 @@ func TestOptionMarshalUnmarshal(t *testing.T) {
name: "captive portal",
subs: cpTests(),
},
{
name: "nonce",
subs: nonceTests(),
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -937,6 +939,41 @@ func cpTests() []optionSub {
}
}

func nonceTests() []optionSub {
nonce := NewNonce()

return []optionSub{
{
name: "bad, empty",
os: []Option{&Nonce{}},
},
{
name: "bad, unaligned",
os: []Option{&Nonce{b: []byte{0xff}}},
},
{
name: "ok, minimum length",
os: []Option{&Nonce{b: make([]byte, 6)}},
bs: [][]byte{
{14, 1},
// Nonce.
ndptest.Zero(6),
},
ok: true,
},
{
name: "ok, random",
os: []Option{nonce},
bs: [][]byte{
{14, 2},
// Nonce.
nonce.b,
},
ok: true,
},
}
}

func mustCaptivePortal(uri string) *CaptivePortal {
cp, err := NewCaptivePortal(uri)
if err != nil {
Expand Down

0 comments on commit 0e15311

Please sign in to comment.