278 lines
5.8 KiB
Go
Vendored
278 lines
5.8 KiB
Go
Vendored
package webauthn
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tinylib/msgp/msgp"
|
|
|
|
"github.com/go-webauthn/webauthn/protocol"
|
|
)
|
|
|
|
func TestAuthenticator_UpdateCounter(t *testing.T) {
|
|
type fields struct {
|
|
AAGUID []byte
|
|
SignCount uint32
|
|
CloneWarning bool
|
|
}
|
|
|
|
type args struct {
|
|
authDataCount uint32
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
expected bool
|
|
}{
|
|
{
|
|
"IncreasedCounter",
|
|
fields{
|
|
AAGUID: make([]byte, 16),
|
|
SignCount: 1,
|
|
CloneWarning: false,
|
|
},
|
|
args{
|
|
authDataCount: 2,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"UnchangedCounter",
|
|
fields{
|
|
AAGUID: make([]byte, 16),
|
|
SignCount: 1,
|
|
CloneWarning: false,
|
|
},
|
|
args{
|
|
authDataCount: 1,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"DecreasedCounter",
|
|
fields{
|
|
AAGUID: make([]byte, 16),
|
|
SignCount: 2,
|
|
CloneWarning: false,
|
|
},
|
|
args{
|
|
authDataCount: 1,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"ZeroCounter",
|
|
fields{
|
|
AAGUID: make([]byte, 16),
|
|
SignCount: 0,
|
|
CloneWarning: false,
|
|
},
|
|
args{
|
|
authDataCount: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"CounterReturnedToZero",
|
|
fields{
|
|
AAGUID: make([]byte, 16),
|
|
SignCount: 1,
|
|
CloneWarning: false,
|
|
},
|
|
args{
|
|
authDataCount: 0,
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
authenticator := &Authenticator{
|
|
AAGUID: tc.fields.AAGUID,
|
|
SignCount: tc.fields.SignCount,
|
|
CloneWarning: tc.fields.CloneWarning,
|
|
}
|
|
|
|
signCount := authenticator.SignCount
|
|
authenticator.UpdateCounter(tc.args.authDataCount)
|
|
|
|
assert.Equal(t, tc.expected, authenticator.CloneWarning)
|
|
|
|
if authenticator.CloneWarning {
|
|
assert.Equal(t, signCount, authenticator.SignCount)
|
|
} else {
|
|
assert.Equal(t, tc.args.authDataCount, authenticator.SignCount)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSelectAuthenticator(t *testing.T) {
|
|
type args struct {
|
|
att string
|
|
rrk *bool
|
|
uv string
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
args args
|
|
expected protocol.AuthenticatorSelection
|
|
}{
|
|
{"GenerateCorrectAuthenticatorSelection",
|
|
args{
|
|
att: "platform",
|
|
rrk: protocol.ResidentKeyNotRequired(),
|
|
uv: "preferred",
|
|
},
|
|
protocol.AuthenticatorSelection{
|
|
AuthenticatorAttachment: protocol.Platform,
|
|
RequireResidentKey: protocol.ResidentKeyNotRequired(),
|
|
UserVerification: protocol.VerificationPreferred,
|
|
},
|
|
},
|
|
{"GenerateCorrectAuthenticatorSelection",
|
|
args{
|
|
att: "cross-platform",
|
|
rrk: protocol.ResidentKeyRequired(),
|
|
uv: "required",
|
|
},
|
|
protocol.AuthenticatorSelection{
|
|
AuthenticatorAttachment: protocol.CrossPlatform,
|
|
RequireResidentKey: protocol.ResidentKeyRequired(),
|
|
UserVerification: protocol.VerificationRequired,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert.Equal(t, tc.expected, SelectAuthenticator(tc.args.att, tc.args.rrk, tc.args.uv))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthenticator_MsgpRoundTrip(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
original Authenticator
|
|
}{
|
|
{
|
|
"FullyPopulated",
|
|
Authenticator{
|
|
AAGUID: bytes.Repeat([]byte{0xAB}, 16),
|
|
SignCount: 1234,
|
|
CloneWarning: true,
|
|
Attachment: protocol.Platform,
|
|
},
|
|
},
|
|
{
|
|
"CrossPlatformNoCloneWarning",
|
|
Authenticator{
|
|
AAGUID: bytes.Repeat([]byte{0x01}, 16),
|
|
SignCount: 1,
|
|
CloneWarning: false,
|
|
Attachment: protocol.CrossPlatform,
|
|
},
|
|
},
|
|
{
|
|
"Zero",
|
|
Authenticator{},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
data, err := tc.original.MarshalMsg(nil)
|
|
require.NoError(t, err)
|
|
|
|
var decoded Authenticator
|
|
|
|
left, err := decoded.UnmarshalMsg(data)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, left, "UnmarshalMsg should consume all bytes")
|
|
assert.Equal(t, tc.original, decoded)
|
|
assert.LessOrEqual(t, len(data), tc.original.Msgsize())
|
|
|
|
var buf bytes.Buffer
|
|
|
|
require.NoError(t, msgp.Encode(&buf, &tc.original))
|
|
|
|
var streamDecoded Authenticator
|
|
|
|
require.NoError(t, msgp.Decode(&buf, &streamDecoded))
|
|
assert.Equal(t, tc.original, streamDecoded)
|
|
})
|
|
}
|
|
|
|
t.Run("UnmarshalSkipsUnknownKeys", func(t *testing.T) {
|
|
tiny := []byte{0x81, 0xa3, 'x', 'y', 'z', 0xc3}
|
|
|
|
var decoded Authenticator
|
|
|
|
left, err := decoded.UnmarshalMsg(tiny)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, left)
|
|
assert.Equal(t, Authenticator{}, decoded)
|
|
})
|
|
}
|
|
|
|
func TestAuthenticator_MsgpEncodeErrorPaths(t *testing.T) {
|
|
v := Authenticator{
|
|
AAGUID: bytes.Repeat([]byte{0xAB}, 16),
|
|
SignCount: 1234,
|
|
CloneWarning: true,
|
|
Attachment: protocol.Platform,
|
|
}
|
|
|
|
data, err := v.MarshalMsg(nil)
|
|
require.NoError(t, err)
|
|
|
|
exerciseEncodeMsgErrorPaths(t, &v, data)
|
|
}
|
|
|
|
func TestAuthenticator_DecodeMsgInvalidTypes(t *testing.T) {
|
|
t.Run("NotAMap", func(t *testing.T) {
|
|
var a Authenticator
|
|
|
|
_, err := a.UnmarshalMsg(msgpString("not a map"))
|
|
require.Error(t, err)
|
|
|
|
var a2 Authenticator
|
|
|
|
require.Error(t, msgp.Decode(bytes.NewReader(msgpString("not a map")), &a2))
|
|
})
|
|
|
|
testCases := []struct {
|
|
name string
|
|
data []byte
|
|
wantSub string
|
|
}{
|
|
{"AAGUIDAsBool", msgpOneFieldMap("aaguid", msgpBool(true)), "AAGUID"},
|
|
{"SignCountAsString", msgpOneFieldMap("sc", msgpString("x")), "SignCount"},
|
|
{"CloneWarningAsInt", msgpOneFieldMap("cw", msgpInt64(42)), "CloneWarning"},
|
|
{"AttachmentAsBool", msgpOneFieldMap("aa", msgpBool(true)), "Attachment"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var a Authenticator
|
|
|
|
_, err := a.UnmarshalMsg(tc.data)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), tc.wantSub)
|
|
|
|
var a2 Authenticator
|
|
|
|
streamErr := msgp.Decode(bytes.NewReader(tc.data), &a2)
|
|
require.Error(t, streamErr)
|
|
assert.Contains(t, streamErr.Error(), tc.wantSub)
|
|
})
|
|
}
|
|
}
|