Skip to content

Commit

Permalink
yktoken: remove encoding.binary's Read()/Write()
Browse files Browse the repository at this point in the history
Using reflection is slow and we know what's inside a token.  Just call
the read and write bits we need.  In my benchmarking, this moves from
~2000ns/op to about 40ns/op.

This patch ends up touching more code because by removing error the
Read() and Write() calls, a number of routines no longer have error
codes to return and so this patch recursively removes those up the stack
as well.
  • Loading branch information
dgryski committed Oct 8, 2013
1 parent 0a79645 commit 613e3b0
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 52 deletions.
20 changes: 5 additions & 15 deletions yk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,7 @@ func TestParse(t *testing.T) {
continue
}

buf, err := res.Bytes()
if err != nil {
t.Errorf("TestParse test #%d failed: %v", x, err)
continue
}
buf := res.Bytes()

if !bytes.Equal(buf, test.result) {
t.Errorf("TestParse test #%d failed: got: %x want: %x",
Expand All @@ -307,21 +303,15 @@ func TestOtp(t *testing.T) {
continue
}
key := NewKey(string(test.key))
otp, err := token.Generate(key)
if err != nil {
t.Errorf("TestOtp test #%d failed: %v", x, err)
continue
}
otp := token.Generate(key)

res, err := otp.Parse(key)
if err != nil {
t.Errorf("TestOtp test #%d failed: %v", x, err)
continue
}
buf, err := res.Bytes()
if err != nil {
t.Errorf("TestOtp test #%d failed: %v", x, err)
continue
}
buf := res.Bytes()

if !bytes.Equal(test.token, buf) {
t.Errorf("TestOtp test #%d failed: got: %v want: %v",
x, test.token, buf)
Expand Down
Empty file removed ykcrc.go
Empty file.
81 changes: 44 additions & 37 deletions yktoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package yubikey

import (
"bytes"
"encoding/binary"
"errors"
)
Expand Down Expand Up @@ -45,7 +44,7 @@ var (
// NewToken is a helper function to create a new Token.
// The CRC is calculated for the caller.
func NewToken(uid Uid, ctr, tstpl uint16, tstph,
use uint8, rnd uint16) (*Token, error) {
use uint8, rnd uint16) *Token {
token := Token{
Uid: uid,
Ctr: ctr,
Expand All @@ -56,47 +55,49 @@ func NewToken(uid Uid, ctr, tstpl uint16, tstph,
}

// Calculate CRC
var buf bytes.Buffer
if err := binary.Write(&buf, binary.LittleEndian, token); err != nil {
return nil, err
}
token.Crc = ^crc16(buf.Bytes()[:14])
buf := token.Bytes()

return &token, nil
token.Crc = ^crc16(buf[:14])

return &token
}

// NewTokenFromBytes converts a byte stream into a Token.
// An error will be returned on a CRC failure.
func NewTokenFromBytes(buf []byte) (*Token, error) {
var token Token

reader := bytes.NewBuffer(buf)
if err := binary.Read(reader, binary.LittleEndian, &token); err != nil {
return nil, err
}

if !token.CrcOkP() {
// This goes first to avoid buf->token->buf dance that would happen if it was at the end
if len(buf) != 16 || !Crc16BufOkP(buf) {
return nil, ErrCrcFailure
}

copy(token.Uid[:], buf[:6])

token.Ctr = binary.LittleEndian.Uint16(buf[6:])
token.Tstpl = binary.LittleEndian.Uint16(buf[8:])

token.Tstph = buf[10]
token.Use = buf[11]

token.Rnd = binary.LittleEndian.Uint16(buf[12:])
token.Crc = binary.LittleEndian.Uint16(buf[14:])

return &token, nil
}

// Generate encrypts a Token with the specified Key
// and returns a OTP.
func (t *Token) Generate(key Key) (*OTP, error) {
buf, err := t.Bytes()
if err != nil {
return nil, err
}
func (t *Token) Generate(key Key) *OTP {
buf := t.Bytes()

aesenc := AesEncrypt(buf, key)
modenc := ModHexEncode(aesenc)

var o OTP
copy(o[:], modenc)

return &o, nil
return &o
}

func (t *Token) Capslock() uint16 {
Expand All @@ -113,11 +114,15 @@ func (t *Token) CrcOkP() bool {

// Crc16 returns the CRC associated with the Token.
func (t *Token) Crc16() uint16 {
buf, _ := t.Bytes()
buf := t.Bytes()

return crc16(buf)
}

func Crc16BufOkP(buf []byte) bool {
return crc16(buf) == CrcOkResidue
}

func crc16(buf []byte) uint16 {
m_crc := uint16(0xffff)
for _, val := range buf {
Expand All @@ -136,12 +141,18 @@ func crc16(buf []byte) uint16 {

// Bytes returns the byte stream associated with
// the Token.
func (t *Token) Bytes() ([]byte, error) {
var buf bytes.Buffer
if err := binary.Write(&buf, binary.LittleEndian, t); err != nil {
return nil, err
}
return buf.Bytes(), nil
func (t *Token) Bytes() []byte {
buf := make([]byte, 16)

copy(buf[:6], t.Uid[:])
binary.LittleEndian.PutUint16(buf[6:], t.Ctr)
binary.LittleEndian.PutUint16(buf[8:], t.Tstpl)
buf[10] = t.Tstph
buf[11] = t.Use
binary.LittleEndian.PutUint16(buf[12:], t.Rnd)
binary.LittleEndian.PutUint16(buf[14:], t.Crc)

return buf
}

// NewKey the specified string to a Key structure.
Expand All @@ -163,10 +174,7 @@ func NewOtp(buf string) OTP {
// Parse decodes and decrypts the OTP with the specified Key
// returning a Token.
func (o OTP) Parse(key Key) (*Token, error) {
buf, err := o.Bytes()
if err != nil {
return nil, err
}
buf := o.Bytes()

moddec := ModHexDecode(buf)
aesdec := AesDecrypt(moddec, key)
Expand All @@ -181,12 +189,11 @@ func (o OTP) Parse(key Key) (*Token, error) {

// Bytes returns the byte stream associated with
// the OTP.
func (o OTP) Bytes() ([]byte, error) {
var buf bytes.Buffer
if err := binary.Write(&buf, binary.LittleEndian, o); err != nil {
return nil, err
}
return buf.Bytes(), nil
func (o OTP) Bytes() []byte {
buf := make([]byte, len(o))
copy(buf, o[:])

return buf
}

// NewUid returns a UID structure.
Expand Down

0 comments on commit 613e3b0

Please sign in to comment.