Skip to content

Commit

Permalink
Resolve several TODO's (#103)
Browse files Browse the repository at this point in the history
Remove RequestMatcher TODO
Resolve cassette.Decrypt TODO
Resolve Response TODO: Close, Uncompressed
Resolve nonceGen validation TODO
  • Loading branch information
seborama authored Aug 28, 2022
1 parent ab75a40 commit a5f71b9
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 12 deletions.
1 change: 0 additions & 1 deletion cassette/cassette.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ func getEncryptionMarker(data []byte) string {

// Decrypt is a utility function that decrypts the cassette raw data
// with the use of the supplied crypter.
// TODO: move this method to the encryption package??
func Decrypt(data []byte, crypter Crypter) ([]byte, error) {
encMarker := getEncryptionMarker(data)
markerLen := len(encMarker)
Expand Down
4 changes: 4 additions & 0 deletions cassette/track/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ type Response struct {
Body []byte
ContentLength int64
TransferEncoding []string
Close bool
Uncompressed bool
Trailer http.Header
TLS *tls.ConnectionState

Expand Down Expand Up @@ -219,6 +221,8 @@ func ToResponse(httpResponse *http.Response) *Response {
Body: bodyClone,
ContentLength: httpResponse.ContentLength,
TransferEncoding: tsfEncodingClone,
Close: httpResponse.Close,
Uncompressed: httpResponse.Uncompressed,
Trailer: trailerClone,
TLS: tlsClone,
}
Expand Down
3 changes: 2 additions & 1 deletion cassette/track/track.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ func (trk Track) ToHTTPResponse() *http.Response {
httpResponse.Body = bodyReadCloser
httpResponse.ContentLength = trk.Response.ContentLength
httpResponse.TransferEncoding = trk.Response.TransferEncoding
// TODO: Close, Uncompressed ?
httpResponse.Close = trk.Response.Close
httpResponse.Uncompressed = trk.Response.Uncompressed
httpResponse.Trailer = trk.Response.Trailer
httpResponse.TLS = trk.Response.TLS

Expand Down
6 changes: 4 additions & 2 deletions cassette/track/track_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func TestTrack_ToHTTPResponse(t *testing.T) {
Body: []byte("resp body"),
ContentLength: 826,
TransferEncoding: []string{"resp tsf_encoding"},
Close: true,
Uncompressed: true,
Trailer: map[string][]string{
"resptr1": {"resptrva11", "resptrva22"},
},
Expand Down Expand Up @@ -104,8 +106,8 @@ func TestTrack_ToHTTPResponse(t *testing.T) {
Body: nil, // we assert Body separately because it's an io.Reader
ContentLength: 826,
TransferEncoding: []string{"resp tsf_encoding"},
Close: false,
Uncompressed: false,
Close: true,
Uncompressed: true,
Trailer: map[string][]string{
"resptr1": {"resptrva11", "resptrva22"},
},
Expand Down
8 changes: 6 additions & 2 deletions encryption/aesgcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"crypto/aes"
"crypto/cipher"

"github.com/pkg/errors"

cryptoerr "github.com/seborama/govcr/v12/encryption/errors"
)

Expand All @@ -21,8 +23,6 @@ func NewAESGCMWithRandomNonceGenerator(key []byte) (*Crypter, error) {
//
// If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
//
// TODO: add a nonceGenerator validator i.e. call it 1000 times, ensures no dupes.
func NewAESGCM(key []byte, nonceGenerator NonceGenerator) (*Crypter, error) {
if len(key) != 16 && len(key) != 32 {
return nil, cryptoerr.NewErrCrypto("key size is not 16 or 32 bytes")
Expand All @@ -42,5 +42,9 @@ func NewAESGCM(key []byte, nonceGenerator NonceGenerator) (*Crypter, error) {
nonceGenerator = NewRandomNonceGenerator(aesgcm.NonceSize())
}

if err = validateNonceGenerator(nonceGenerator); err != nil {
return nil, errors.Wrap(err, "nonce generator is not valid")
}

return NewCrypter(aesgcm, "aesgcm", nonceGenerator), nil
}
7 changes: 5 additions & 2 deletions encryption/chacha20poly1305.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package encryption

import (
"github.com/pkg/errors"
"golang.org/x/crypto/chacha20poly1305"
)

Expand All @@ -17,8 +18,6 @@ func NewChaCha20Poly1305WithRandomNonceGenerator(key []byte) (*Crypter, error) {
// The key should be 32 bytes long.
//
// If you want to convert a passphrase to a key, you can use a function such as Argon2.
//
// TODO: add a nonceGenerator validator i.e. call it 1000 times, ensures no dupes.
func NewChaCha20Poly1305(key []byte, nonceGenerator NonceGenerator) (*Crypter, error) {
cc20px, err := chacha20poly1305.NewX(key)
if err != nil {
Expand All @@ -29,5 +28,9 @@ func NewChaCha20Poly1305(key []byte, nonceGenerator NonceGenerator) (*Crypter, e
nonceGenerator = NewRandomNonceGenerator(cc20px.NonceSize())
}

if err = validateNonceGenerator(nonceGenerator); err != nil {
return nil, errors.Wrap(err, "nonce generator is not valid")
}

return NewCrypter(cc20px, "chacha20poly1305", nonceGenerator), nil
}
23 changes: 23 additions & 0 deletions encryption/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package encryption

import (
"crypto/rand"
"fmt"
"io"

"github.com/pkg/errors"
)

// RandomNonceGenerator is a random generator of nonce of the specified size.
Expand All @@ -28,3 +31,23 @@ func (ng RandomNonceGenerator) Generate() ([]byte, error) {

return nonce, nil
}

func validateNonceGenerator(nonceGenerator NonceGenerator) error {
nonces := make(map[string]struct{})

for i := 1; i <= 1000; i++ {
n, err := nonceGenerator.Generate()
if err != nil {
return errors.Wrap(err, "nonceGenerator failure")
}

nStr := fmt.Sprintf("%x", n)
if _, ok := nonces[nStr]; ok {
return errors.New("nonceGenerator produces frequent duplicates")
}

nonces[nStr] = struct{}{}
}

return nil
}
37 changes: 37 additions & 0 deletions encryption/nonce_wb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package encryption

import (
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func Test_validateNonceGenerator_Passes(t *testing.T) {
err := validateNonceGenerator(NewRandomNonceGenerator(32))
assert.NoError(t, err)
}

func Test_validateNonceGenerator_Fails_NonceGenErr(t *testing.T) {
err := validateNonceGenerator(brokenNonceGenerator{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "nonceGenerator failure: broken nonce")
}

func Test_validateNonceGenerator_Fails_WeakNonceGen(t *testing.T) {
err := validateNonceGenerator(weakNonceGenerator{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "nonceGenerator produces frequent duplicates")
}

type brokenNonceGenerator struct{}

func (ng brokenNonceGenerator) Generate() ([]byte, error) {
return nil, errors.New("broken nonce")
}

type weakNonceGenerator struct{}

func (ng weakNonceGenerator) Generate() ([]byte, error) {
return []byte("static nonce"), nil
}
4 changes: 0 additions & 4 deletions pcb.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,6 @@ func (pcb *PrintedCircuitBoard) ClearReplayingMutators() {

// RequestMatcher is an interface that exposes the method to perform request comparison.
// request comparison involves the HTTP request and the track request recorded on cassette.
//
// TODO:
// there could be a case to have RequestMatchers (plural) that would work akin to track.Mutators.
// I.e. they could be chained and conditional.
type RequestMatcher interface {
Match(httpRequest, trackRequest *track.Request) bool
}

0 comments on commit a5f71b9

Please sign in to comment.