Skip to content

Commit

Permalink
feat(optim): reduce allocations
Browse files Browse the repository at this point in the history
Signed-off-by: Thibault Normand <me@zenithar.org>
  • Loading branch information
Zenithar committed Jan 15, 2023
1 parent df4b88d commit a6d9c58
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 35 deletions.
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,36 +47,36 @@ More examples - [here](example_test.go)

## Benchmarks

> Go version 1.19.3 / Mac M1
> Go version 1.19.5 / Mac M1
### V3

```sh
$ go test -bench=. -test.benchtime=1s
go test -bench=. -test.benchtime=1s
goos: darwin
goarch: arm64
pkg: zntr.io/paseto/v3
Benchmark_Paseto_Encrypt-10 75150 15071 ns/op 9328 B/op 76 allocs/op
Benchmark_Paseto_Decrypt-10 82350 14535 ns/op 8200 B/op 69 allocs/op
Benchmark_Paseto_Sign-10 7507 159817 ns/op 10004 B/op 101 allocs/op
Benchmark_Paseto_Verify-10 1945 614350 ns/op 3770 B/op 61 allocs/op
Benchmark_Paseto_Encrypt-10 74997 14869 ns/op 8800 B/op 70 allocs/op
Benchmark_Paseto_Decrypt-10 83835 14272 ns/op 7912 B/op 66 allocs/op
Benchmark_Paseto_Sign-10 7545 156930 ns/op 9812 B/op 99 allocs/op
Benchmark_Paseto_Verify-10 1986 615306 ns/op 3578 B/op 59 allocs/op
PASS
ok zntr.io/paseto/v3 5.276s
ok zntr.io/paseto/v3 5.400s
```

### V4

```sh
$ go test -bench=. -test.benchtime=1s
go test -bench=. -test.benchtime=1s
goos: darwin
goarch: arm64
pkg: zntr.io/paseto/v4
Benchmark_Paseto_Encrypt-10 423649 2823 ns/op 3296 B/op 29 allocs/op
Benchmark_Paseto_Decrypt-10 503470 2389 ns/op 2208 B/op 22 allocs/op
Benchmark_Paseto_Sign-10 46567 25378 ns/op 1720 B/op 17 allocs/op
Benchmark_Paseto_Verify-10 22294 53853 ns/op 792 B/op 12 allocs/op
Benchmark_Paseto_Encrypt-10 426136 2811 ns/op 2752 B/op 23 allocs/op
Benchmark_Paseto_Decrypt-10 517602 2244 ns/op 1920 B/op 19 allocs/op
Benchmark_Paseto_Sign-10 48186 24887 ns/op 1480 B/op 15 allocs/op
Benchmark_Paseto_Verify-10 22714 52885 ns/op 552 B/op 10 allocs/op
PASS
ok zntr.io/paseto/v4 6.723s
ok zntr.io/paseto/v4 6.851s
```

## License
Expand Down
10 changes: 10 additions & 0 deletions internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ import (
func PreAuthenticationEncoding(pieces ...[]byte) ([]byte, error) {
output := &bytes.Buffer{}

// Precompute length to allocate the buffer
// PieceCount (8B) || ( PieceLen (8B) || Piece (*B) )*
bufLen := 8
for i := range pieces {
bufLen += 8 + len(pieces[i])
}

// Pre-allocate the buffer
output.Grow(bufLen)

// Encode piece count
count := len(pieces)
if err := binary.Write(output, binary.LittleEndian, uint64(count)); err != nil {
Expand Down
20 changes: 9 additions & 11 deletions v3/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ func Encrypt(r io.Reader, key *LocalKey, m, f, i []byte) ([]byte, error) {
return nil, fmt.Errorf("paseto: invalid key length, it must be %d bytes long", KeyLength)
}

// Pre-allocate body
body := make([]byte, nonceLength+len(m), nonceLength+len(m)+macLength)

// Create random seed
var n [nonceLength]byte
if _, err := io.ReadFull(r, n[:]); err != nil {
if _, err := io.ReadFull(r, body[:nonceLength]); err != nil {
return nil, fmt.Errorf("paseto: unable to generate random seed: %w", err)
}

// Derive keys from seed and secret key
ek, n2, ak, err := kdf(key, n[:])
ek, n2, ak, err := kdf(key, body[:nonceLength])
if err != nil {
return nil, fmt.Errorf("paseto: unable to derive keys from seed: %w", err)
}
Expand All @@ -86,19 +88,16 @@ func Encrypt(r io.Reader, key *LocalKey, m, f, i []byte) ([]byte, error) {
ciph := cipher.NewCTR(block, n2)

// Encrypt the payload
c := make([]byte, len(m))
ciph.XORKeyStream(c, m)
ciph.XORKeyStream(body[nonceLength:], m)

// Compute MAC
t, err := mac(ak, []byte(LocalPrefix), n[:], c, f, i)
t, err := mac(ak, []byte(LocalPrefix), body[:nonceLength], body[nonceLength:], f, i)
if err != nil {
return nil, fmt.Errorf("paseto: unable to compute MAC: %w", err)
}

// Serialize final token
// h || base64url(n || c || t)
body := append([]byte{}, n[:]...)
body = append(body, c...)
body = append(body, t...)

// Encode body as RawURLBase64
Expand Down Expand Up @@ -201,9 +200,8 @@ func Decrypt(key *LocalKey, input, f, i []byte) ([]byte, error) {
ciph := cipher.NewCTR(block, n2)

// Decrypt the payload
m := make([]byte, len(c))
ciph.XORKeyStream(m, c)
ciph.XORKeyStream(c, c)

// No error
return m, nil
return c, nil
}
20 changes: 9 additions & 11 deletions v4/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ func Encrypt(r io.Reader, key *LocalKey, m, f, i []byte) ([]byte, error) {
return nil, fmt.Errorf("paseto: invalid key length, it must be %d bytes long", KeyLength)
}

// Pre-allocate body
body := make([]byte, nonceLength+len(m), nonceLength+len(m)+macLength)

// Create random seed
var n [nonceLength]byte
if _, err := io.ReadFull(r, n[:]); err != nil {
if _, err := io.ReadFull(r, body[:nonceLength]); err != nil {
return nil, fmt.Errorf("paseto: unable to generate random seed: %w", err)
}

// Derive keys from seed and secret key
ek, n2, ak, err := kdf(key, n[:])
ek, n2, ak, err := kdf(key, body[:nonceLength])
if err != nil {
return nil, fmt.Errorf("paseto: unable to derive keys from seed: %w", err)
}
Expand All @@ -85,19 +87,16 @@ func Encrypt(r io.Reader, key *LocalKey, m, f, i []byte) ([]byte, error) {
}

// Encrypt the payload
c := make([]byte, len(m))
ciph.XORKeyStream(c, m)
ciph.XORKeyStream(body[nonceLength:], m)

// Compute MAC
t, err := mac(ak, []byte(LocalPrefix), n[:], c, f, i)
t, err := mac(ak, []byte(LocalPrefix), body[:nonceLength], body[nonceLength:], f, i)
if err != nil {
return nil, fmt.Errorf("paseto: unable to compute MAC: %w", err)
}

// Serialize final token
// h || base64url(n || c || t)
body := append([]byte{}, n[:]...)
body = append(body, c...)
body = append(body, t...)

// Encode body as RawURLBase64
Expand Down Expand Up @@ -199,9 +198,8 @@ func Decrypt(key *LocalKey, input, f, i []byte) ([]byte, error) {
}

// Decrypt the payload
m := make([]byte, len(c))
ciph.XORKeyStream(m, c)
ciph.XORKeyStream(c, c)

// No error
return m, nil
return c, nil
}

0 comments on commit a6d9c58

Please sign in to comment.