From 4cf9dffba0119653a46bba2f522c29670699b417 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Tue, 21 Feb 2023 00:43:29 +0000 Subject: [PATCH 01/12] Result type experiments Only change the internal API --- errors.go | 46 +++++++----- go.mod | 1 + go.sum | 2 + internal/encoding/base64.go | 8 +- message.go | 131 ++++++++++++-------------------- parser.go | 99 ++++++++---------------- paseto.go | 48 ++++++------ token.go | 29 ++++---- v2.go | 21 +++--- v2_payloads.go | 14 ++-- v3.go | 22 +++--- v3_payloads.go | 14 ++-- v4.go | 21 +++--- v4_payloads.go | 14 ++-- vectors_test.go | 145 ++++++++++++++++++------------------ 15 files changed, 285 insertions(+), 330 deletions(-) diff --git a/errors.go b/errors.go index a93f449..7ee4efc 100644 --- a/errors.go +++ b/errors.go @@ -9,21 +9,25 @@ type TokenError struct { e error } -func (e *TokenError) Error() string { +func newTokenError(e error) TokenError { + return TokenError{e} +} + +func (e TokenError) Error() string { return e.e.Error() } -func (_ *TokenError) Is(e error) bool { +func (_ TokenError) Is(e error) bool { _, ok := e.(*TokenError) return ok } -func (e *TokenError) Unwrap() error { +func (e TokenError) Unwrap() error { return e.e } -func (e *TokenError) wrapWith(msg string) *TokenError { - return &TokenError{fmt.Errorf("%s: %w", msg, e)} +func (e TokenError) wrapWith(msg string) TokenError { + return TokenError{fmt.Errorf("%s: %w", msg, e)} } // Any error which is the result of a rule failure (distinct from a TokenError) @@ -34,16 +38,20 @@ type RuleError struct { e error } -func (e *RuleError) Error() string { +func newRuleError(e error) RuleError { + return RuleError{e} +} + +func (e RuleError) Error() string { return e.e.Error() } -func (_ *RuleError) Is(e error) bool { +func (_ RuleError) Is(e error) bool { _, ok := e.(*RuleError) return ok } -func (e *RuleError) Unwrap() error { +func (e RuleError) Unwrap() error { return e.e } @@ -55,19 +63,19 @@ func errorSeedLength(expected, given int) error { return fmt.Errorf("seed length incorrect (%d), expected %d", given, expected) } -func errorMessageParts(given int) *TokenError { - return &TokenError{fmt.Errorf("invalid number of message parts in token (%d)", given)} +func errorMessageParts(given int) TokenError { + return newTokenError(fmt.Errorf("invalid number of message parts in token (%d)", given)) } -func errorMessageHeader(expected Protocol, givenHeader string) *TokenError { - return &TokenError{fmt.Errorf("message header `%s' is not valid, expected `%s'", givenHeader, expected.Header())} +func errorMessageHeader(expected Protocol, givenHeader string) TokenError { + return newTokenError(fmt.Errorf("message header `%s' is not valid, expected `%s'", givenHeader, expected.Header())) } -func errorMessageHeaderDecrypt(expected Protocol, givenHeader string) *TokenError { +func errorMessageHeaderDecrypt(expected Protocol, givenHeader string) TokenError { return errorMessageHeader(expected, givenHeader).wrapWith("cannot decrypt message") } -func errorMessageHeaderVerify(expected Protocol, givenHeader string) *TokenError { +func errorMessageHeaderVerify(expected Protocol, givenHeader string) TokenError { return errorMessageHeader(expected, givenHeader).wrapWith("cannot verify message") } @@ -75,12 +83,12 @@ var unsupportedPasetoVersion = fmt.Errorf("unsupported PASETO version") var unsupportedPasetoPurpose = fmt.Errorf("unsupported PASETO purpose") var unsupportedPayload = fmt.Errorf("unsupported payload") -var errorPayloadShort = &TokenError{fmt.Errorf("payload is not long enough to be a valid PASETO message")} -var errorBadSignature = &TokenError{fmt.Errorf("bad signature")} -var errorBadMAC = &TokenError{fmt.Errorf("bad message authentication code")} +var errorPayloadShort = newTokenError(fmt.Errorf("payload is not long enough to be a valid PASETO message")) +var errorBadSignature = newTokenError(fmt.Errorf("bad signature")) +var errorBadMAC = newTokenError(fmt.Errorf("bad message authentication code")) var errorKeyInvalid = fmt.Errorf("key was not valid") -func errorDecrypt(err error) *TokenError { - return (&TokenError{err}).wrapWith("the message could not be decrypted") +func errorDecrypt(err error) TokenError { + return newTokenError(err).wrapWith("the message could not be decrypted") } diff --git a/go.mod b/go.mod index 1023799..e43ed97 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index 15e2eb6..11e511f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68 h1:LBGFT4p6h1cw7CgJ53HY2wJb0GWBmCAqD/F8Lo6XfbA= +aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/encoding/base64.go b/internal/encoding/base64.go index b9c4685..6b0aebc 100644 --- a/internal/encoding/base64.go +++ b/internal/encoding/base64.go @@ -4,6 +4,8 @@ import ( "encoding/base64" "errors" "strings" + + t "aidanwoods.dev/go-result" ) var b64 = base64.RawURLEncoding.Strict() @@ -14,12 +16,12 @@ func Encode(bytes []byte) string { } // Decode Standard decoding for Paseto is URL safe base64 with no padding -func Decode(encoded string) ([]byte, error) { +func Decode(encoded string) t.Result[[]byte] { // From: https://pkg.go.dev/encoding/base64#Encoding.Strict // Note that the input is still malleable, as new line characters (CR and LF) are still ignored. if strings.ContainsAny(encoded, "\n\r") { - return nil, errors.New("Input may not contain new lines") + return t.Err[[]byte](errors.New("Input may not contain new lines")) } - return b64.DecodeString(encoded) + return t.NewResult(b64.DecodeString(encoded)) } diff --git a/message.go b/message.go index c0fe61c..7d818ba 100644 --- a/message.go +++ b/message.go @@ -4,6 +4,7 @@ import ( "strings" "aidanwoods.dev/go-paseto/internal/encoding" + t "aidanwoods.dev/go-result" ) // Message is a building block type, only use if you need to use Paseto @@ -17,32 +18,23 @@ type message struct { // NewMessage creates a new message from the given token, with an expected // protocol. If the given token does not match the given token, or if the // token cannot be parsed, will return an error instead. -func newMessage(protocol Protocol, token string) (message, error) { - header, encodedPayload, encodedFooter, err := deconstructToken(token) - if err != nil { - return message{}, err - } - - if header != protocol.Header() { - return message{}, errorMessageHeader(protocol, header) - } - - payloadBytes, err := encoding.Decode(encodedPayload) - if err != nil { - return message{}, &TokenError{err} - } - - footer, err := encoding.Decode(encodedFooter) - if err != nil { - return message{}, &TokenError{err} - } - - payload, err := protocol.newPayload(payloadBytes) - if err != nil { - return message{}, err - } - - return newMessageFromPayload(payload, footer), nil +func newMessage(protocol Protocol, token string) t.Result[message] { + parts := t.AndThen(deconstructToken(token), func(parts deconstructedToken) t.Result[deconstructedToken] { + if parts.header != protocol.Header() { + return t.Err[deconstructedToken](errorMessageHeader(protocol, parts.header)) + } + + return t.Ok(parts) + }) + + return t.AndThen(parts, func(parts deconstructedToken) t.Result[message] { + return t.MapErr(newTokenError, t.Map2( + t.AndThen(encoding.Decode(parts.encodedPayload), protocol.newPayload), + encoding.Decode(parts.encodedFooter), + + newMessageFromPayloadAndFooter, + )) + }) } // Header returns the header string for a Paseto message. @@ -67,104 +59,79 @@ func (m message) encoded() string { return main + "." + encoding.Encode(m.footer) } -func newMessageFromPayload(payload payload, footer []byte) message { - if protocol, err := protocolForPayload(payload); err == nil { - return message{protocol, payload, footer} - } - +func newMessageFromPayloadAndFooter(payload payload, footer []byte) message { // Assume internal callers won't construct bad payloads - panic("Sanity check for payload failed") + protocol := protocolForPayload(payload).Expect("sanity check for payload failed") + return message{protocol, payload, footer} +} + +type deconstructedToken struct { + header string + encodedPayload string + encodedFooter string } -func deconstructToken(token string) (header string, encodedPayload string, encodedFooter string, err error) { +func deconstructToken(token string) t.Result[deconstructedToken] { parts := strings.Split(token, ".") partsLen := len(parts) if partsLen != 3 && partsLen != 4 { - err = errorMessageParts(len(parts)) - return + return t.Err[deconstructedToken](errorMessageParts(len(parts))) } - header = parts[0] + "." + parts[1] + "." - encodedPayload = parts[2] + header := parts[0] + "." + parts[1] + "." + encodedPayload := parts[2] + encodedFooter := "" if partsLen == 4 { encodedFooter = parts[3] - } else { - encodedFooter = "" } - return header, encodedPayload, encodedFooter, nil + return t.Ok(deconstructedToken{ + header: header, + encodedPayload: encodedPayload, + encodedFooter: encodedFooter, + }) } // V2Verify will verify a v2 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v2Verify(key V2AsymmetricPublicKey) (*Token, error) { - packet, err := v2PublicVerify(m, key) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v2Verify(key V2AsymmetricPublicKey) t.Result[Token] { + return t.AndThen(v2PublicVerify(m, key), packet.token) } // V2Decrypt will decrypt a v2 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v2Decrypt(key V2SymmetricKey) (*Token, error) { - packet, err := v2LocalDecrypt(m, key) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v2Decrypt(key V2SymmetricKey) t.Result[Token] { + return t.AndThen(v2LocalDecrypt(m, key), packet.token) } // V3Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v3Verify(key V3AsymmetricPublicKey, implicit []byte) (*Token, error) { - packet, err := v3PublicVerify(m, key, implicit) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v3Verify(key V3AsymmetricPublicKey, implicit []byte) t.Result[Token] { + return t.AndThen(v3PublicVerify(m, key, implicit), packet.token) } // V3Decrypt will decrypt a v3 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v3Decrypt(key V3SymmetricKey, implicit []byte) (*Token, error) { - packet, err := v3LocalDecrypt(m, key, implicit) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v3Decrypt(key V3SymmetricKey, implicit []byte) t.Result[Token] { + return t.AndThen(v3LocalDecrypt(m, key, implicit), packet.token) } // V4Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v4Verify(key V4AsymmetricPublicKey, implicit []byte) (*Token, error) { - packet, err := v4PublicVerify(m, key, implicit) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v4Verify(key V4AsymmetricPublicKey, implicit []byte) t.Result[Token] { + return t.AndThen(v4PublicVerify(m, key, implicit), packet.token) } // V4Decrypt will decrypt a v4 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. -func (m message) v4Decrypt(key V4SymmetricKey, implicit []byte) (*Token, error) { - packet, err := v4LocalDecrypt(m, key, implicit) - if err != nil { - return nil, err - } - - return packet.token() +func (m message) v4Decrypt(key V4SymmetricKey, implicit []byte) t.Result[Token] { + return t.AndThen(v4LocalDecrypt(m, key, implicit), packet.token) } diff --git a/parser.go b/parser.go index 95ea197..3be5876 100644 --- a/parser.go +++ b/parser.go @@ -2,6 +2,8 @@ package paseto import ( "time" + + t "aidanwoods.dev/go-result" ) // Parser is used to verify or decrypt a token, and can be provided with @@ -34,109 +36,68 @@ func MakeParser(rules []Rule) Parser { // ParseV2Local will parse and decrypt a v2 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV2Local(key V2SymmetricKey, tainted string) (*Token, error) { - message, err := newMessage(V2Local, tainted) - if err != nil { - return nil, err - } - - token, err := message.v2Decrypt(key) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V2Local, tainted), func(m message) t.Result[Token] { + return m.v2Decrypt(key) + }) - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // ParseV2Public will parse and verify a v2 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV2Public(key V2AsymmetricPublicKey, tainted string) (*Token, error) { - message, err := newMessage(V2Public, tainted) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V2Public, tainted), func(m message) t.Result[Token] { + return m.v2Verify(key) + }) - token, err := message.v2Verify(key) - if err != nil { - return nil, err - } - - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // ParseV3Local will parse and decrypt a v3 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV3Local(key V3SymmetricKey, tainted string, implicit []byte) (*Token, error) { - message, err := newMessage(V3Local, tainted) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V3Local, tainted), func(m message) t.Result[Token] { + return m.v3Decrypt(key, implicit) + }) - token, err := message.v3Decrypt(key, implicit) - if err != nil { - return nil, err - } - - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // ParseV3Public will parse and verify a v3 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV3Public(key V3AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - message, err := newMessage(V3Public, tainted) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V3Public, tainted), func(m message) t.Result[Token] { + return m.v3Verify(key, implicit) + }) - token, err := message.v3Verify(key, implicit) - if err != nil { - return nil, err - } - - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // ParseV4Local will parse and decrypt a v4 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV4Local(key V4SymmetricKey, tainted string, implicit []byte) (*Token, error) { - message, err := newMessage(V4Local, tainted) - if err != nil { - return nil, err - } - - token, err := message.v4Decrypt(key, implicit) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V4Local, tainted), func(m message) t.Result[Token] { + return m.v4Decrypt(key, implicit) + }) - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // ParseV4Public will parse and verify a v4 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV4Public(key V4AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - message, err := newMessage(V4Public, tainted) - if err != nil { - return nil, err - } - - token, err := message.v4Verify(key, implicit) - if err != nil { - return nil, err - } + token := t.AndThen(newMessage(V4Public, tainted), func(m message) t.Result[Token] { + return m.v4Verify(key, implicit) + }) - return p.validate(*token) + return t.AndThen(token, p.validate).Results() } // UnsafeParseFooter returns the footer of a Paseto message. Beware that this // footer is not cryptographically verified at this stage, nor are any claims // validated. func (p Parser) UnsafeParseFooter(protocol Protocol, tainted string) ([]byte, error) { - message, err := newMessage(protocol, tainted) - if err != nil { - return nil, err - } - - return message.unsafeFooter(), nil + return t.Map(newMessage(protocol, tainted), message.unsafeFooter).UnwrappedResults() } // SetRules will overwrite any currently set rules with those specified. @@ -149,12 +110,12 @@ func (p *Parser) AddRule(rule ...Rule) { p.rules = append(p.rules, rule...) } -func (p Parser) validate(token Token) (*Token, error) { +func (p Parser) validate(token Token) t.Result[Token] { for _, rule := range p.rules { if err := rule(token); err != nil { - return nil, &RuleError{err} + return t.Err[Token](RuleError{err}) } } - return &token, nil + return t.Ok(token) } diff --git a/paseto.go b/paseto.go index 5860650..405b5b1 100644 --- a/paseto.go +++ b/paseto.go @@ -5,6 +5,8 @@ package paseto import ( "fmt" + + t "aidanwoods.dev/go-result" ) // Purpose represents either local or public paseto mode @@ -101,36 +103,40 @@ func (p Protocol) Purpose() Purpose { return p.purpose } -func (p Protocol) newPayload(bytes []byte) (payload, error) { +func upcastPayload[P payload](p P) payload { + return p +} + +func (p Protocol) newPayload(bytes []byte) t.Result[payload] { switch p.version { default: - return nil, unsupportedPasetoVersion + return t.Err[payload](unsupportedPasetoVersion) case Version2: switch p.purpose { default: - return nil, unsupportedPasetoPurpose + return t.Err[payload](unsupportedPasetoPurpose) case Local: - return newV2LocalPayload(bytes) + return t.Map(newV2LocalPayload(bytes), upcastPayload[v2LocalPayload]) case Public: - return newV2PublicPayload(bytes) + return t.Map(newV2PublicPayload(bytes), upcastPayload[v2PublicPayload]) } case Version3: switch p.purpose { default: - return nil, unsupportedPasetoPurpose + return t.Err[payload](unsupportedPasetoPurpose) case Local: - return newV3LocalPayload(bytes) + return t.Map(newV3LocalPayload(bytes), upcastPayload[v3LocalPayload]) case Public: - return newV3PublicPayload(bytes) + return t.Map(newV3PublicPayload(bytes), upcastPayload[v3PublicPayload]) } case Version4: switch p.purpose { default: - return nil, unsupportedPasetoPurpose + return t.Err[payload](unsupportedPasetoPurpose) case Local: - return newV4LocalPayload(bytes) + return t.Map(newV4LocalPayload(bytes), upcastPayload[v4LocalPayload]) case Public: - return newV4PublicPayload(bytes) + return t.Map(newV4PublicPayload(bytes), upcastPayload[v4PublicPayload]) } } } @@ -139,22 +145,22 @@ type payload interface { bytes() []byte } -func protocolForPayload(payload payload) (Protocol, error) { +func protocolForPayload(payload payload) t.Result[Protocol] { switch payload.(type) { default: - return Protocol{}, unsupportedPayload + return t.Err[Protocol](unsupportedPayload) case v2LocalPayload: - return V2Local, nil + return t.Ok(V2Local) case v2PublicPayload: - return V2Public, nil + return t.Ok(V2Public) case v3LocalPayload: - return V3Local, nil + return t.Ok(V3Local) case v3PublicPayload: - return V3Public, nil + return t.Ok(V3Public) case v4LocalPayload: - return V4Local, nil + return t.Ok(V4Local) case v4PublicPayload: - return V4Public, nil + return t.Ok(V4Public) } } @@ -167,6 +173,6 @@ func newPacket(content []byte, footer []byte) packet { return packet{content, footer} } -func (p packet) token() (*Token, error) { - return NewTokenFromClaimsJSON(p.content, p.footer) +func (p packet) token() t.Result[Token] { + return t.NewPtrResult(NewTokenFromClaimsJSON(p.content, p.footer)) } diff --git a/token.go b/token.go index f5a613c..f9d52a6 100644 --- a/token.go +++ b/token.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "time" + + t "aidanwoods.dev/go-result" ) // Token is a set of paseto claims, and a footer @@ -46,15 +48,13 @@ func NewTokenFromClaimsJSON(claimsData []byte, footer []byte) (*Token, error) { // Set sets the key with the specified value. Note that this value needs to // be serialisable to JSON using encoding/json. // Set will check this and return an error if it is not serialisable. -func (t *Token) Set(key string, value interface{}) error { - v, err := newTokenValue(value) - if err != nil { - return fmt.Errorf("could not set key `%s': %w", key, err) - } - - t.claims[key] = *v - - return nil +func (token *Token) Set(key string, value interface{}) error { + return t.AndThen(marshalTokenValue(value), func(value tokenValue) t.Result[any] { + token.claims[key] = value + return t.Ok[any](nil) + }). + WrapErr("could not set key `" + key + "': %w"). + UnwrapErrOr(nil) } // Get gets the given key and writes the value into output (which should be a @@ -206,11 +206,10 @@ type tokenValue struct { rawValue []byte } -func newTokenValue(value interface{}) (*tokenValue, error) { - bytes, err := json.Marshal(value) - if err != nil { - return nil, err - } +func newTokenValue(bytes []byte) tokenValue { + return tokenValue{bytes} +} - return &tokenValue{bytes}, nil +func marshalTokenValue(value interface{}) t.Result[tokenValue] { + return t.Map(t.NewResult(json.Marshal(value)), newTokenValue) } diff --git a/v2.go b/v2.go index 7c977dc..e3fa758 100644 --- a/v2.go +++ b/v2.go @@ -6,6 +6,7 @@ import ( "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/hashing" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" "golang.org/x/crypto/chacha20poly1305" ) @@ -24,13 +25,13 @@ func v2PublicSign(packet packet, key V2AsymmetricSecretKey) message { var signature [64]byte copy(signature[:], sig) - return newMessageFromPayload(v2PublicPayload{data, signature}, footer) + return newMessageFromPayloadAndFooter(v2PublicPayload{data, signature}, footer) } -func v2PublicVerify(msg message, key V2AsymmetricPublicKey) (packet, error) { +func v2PublicVerify(msg message, key V2AsymmetricPublicKey) t.Result[packet] { payload, ok := msg.p.(v2PublicPayload) if msg.header() != V2Public.Header() || !ok { - return packet{}, errorMessageHeaderVerify(V2Public, msg.header()) + return t.Err[packet](errorMessageHeaderVerify(V2Public, msg.header())) } header, footer := []byte(msg.header()), msg.footer @@ -39,10 +40,10 @@ func v2PublicVerify(msg message, key V2AsymmetricPublicKey) (packet, error) { m2 := encoding.Pae(header, data, footer) if !ed25519.Verify(key.material, m2, payload.signature[:]) { - return packet{}, errorBadSignature + return t.Err[packet](errorBadSignature) } - return packet{data, footer}, nil + return t.Ok(packet{data, footer}) } func v2LocalEncrypt(p packet, key V2SymmetricKey, unitTestNonce []byte) message { @@ -63,13 +64,13 @@ func v2LocalEncrypt(p packet, key V2SymmetricKey, unitTestNonce []byte) message cipherText := cipher.Seal(nil, nonce[:], p.content, preAuth) - return newMessageFromPayload(v2LocalPayload{nonce, cipherText}, p.footer) + return newMessageFromPayloadAndFooter(v2LocalPayload{nonce, cipherText}, p.footer) } -func v2LocalDecrypt(msg message, key V2SymmetricKey) (packet, error) { +func v2LocalDecrypt(msg message, key V2SymmetricKey) t.Result[packet] { payload, ok := msg.p.(v2LocalPayload) if msg.header() != V2Local.Header() || !ok { - return packet{}, errorMessageHeaderDecrypt(V2Local, msg.header()) + return t.Err[packet](errorMessageHeaderDecrypt(V2Local, msg.header())) } nonce, cipherText := payload.nonce, payload.cipherText @@ -85,8 +86,8 @@ func v2LocalDecrypt(msg message, key V2SymmetricKey) (packet, error) { plainText, err := cipher.Open(nil, nonce[:], cipherText, preAuth) if err != nil { - return packet{}, errorDecrypt(err) + return t.Err[packet](errorDecrypt(err)) } - return packet{plainText, msg.footer}, nil + return t.Ok(packet{plainText, msg.footer}) } diff --git a/v2_payloads.go b/v2_payloads.go index f0a8f11..dcebbd9 100644 --- a/v2_payloads.go +++ b/v2_payloads.go @@ -1,5 +1,7 @@ package paseto +import t "aidanwoods.dev/go-result" + type v2PublicPayload struct { message []byte signature [64]byte @@ -9,10 +11,10 @@ func (p v2PublicPayload) bytes() []byte { return append(p.message, p.signature[:]...) } -func newV2PublicPayload(bytes []byte) (v2PublicPayload, error) { +func newV2PublicPayload(bytes []byte) t.Result[v2PublicPayload] { signatureOffset := len(bytes) - 64 if signatureOffset < 0 { - return v2PublicPayload{}, errorPayloadShort + return t.Err[v2PublicPayload](errorPayloadShort) } message := make([]byte, len(bytes)-64) @@ -21,7 +23,7 @@ func newV2PublicPayload(bytes []byte) (v2PublicPayload, error) { var signature [64]byte copy(signature[:], bytes[signatureOffset:]) - return v2PublicPayload{message, signature}, nil + return t.Ok(v2PublicPayload{message, signature}) } type v2LocalPayload struct { @@ -33,9 +35,9 @@ func (p v2LocalPayload) bytes() []byte { return append(p.nonce[:], p.cipherText...) } -func newV2LocalPayload(bytes []byte) (v2LocalPayload, error) { +func newV2LocalPayload(bytes []byte) t.Result[v2LocalPayload] { if len(bytes) <= 24 { - return v2LocalPayload{}, errorPayloadShort + return t.Err[v2LocalPayload](errorPayloadShort) } var nonce [24]byte copy(nonce[:], bytes[0:24]) @@ -43,5 +45,5 @@ func newV2LocalPayload(bytes []byte) (v2LocalPayload, error) { cipherText := make([]byte, len(bytes)-24) copy(cipherText, bytes[24:]) - return v2LocalPayload{nonce, cipherText}, nil + return t.Ok(v2LocalPayload{nonce, cipherText}) } diff --git a/v3.go b/v3.go index 90c5293..9039812 100644 --- a/v3.go +++ b/v3.go @@ -11,6 +11,7 @@ import ( "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" ) func v3PublicSign(packet packet, key V3AsymmetricSecretKey, implicit []byte) message { @@ -41,13 +42,13 @@ func v3PublicSign(packet packet, key V3AsymmetricSecretKey, implicit []byte) mes var signature [96]byte copy(signature[:], sig) - return newMessageFromPayload(v3PublicPayload{data, signature}, footer) + return newMessageFromPayloadAndFooter(v3PublicPayload{data, signature}, footer) } -func v3PublicVerify(msg message, key V3AsymmetricPublicKey, implicit []byte) (packet, error) { +func v3PublicVerify(msg message, key V3AsymmetricPublicKey, implicit []byte) t.Result[packet] { payload, ok := msg.p.(v3PublicPayload) if msg.header() != V3Public.Header() || !ok { - return packet{}, errorMessageHeaderVerify(V3Public, msg.header()) + return t.Err[packet](errorMessageHeaderVerify(V3Public, msg.header())) } header, footer := []byte(msg.header()), msg.footer @@ -61,10 +62,10 @@ func v3PublicVerify(msg message, key V3AsymmetricPublicKey, implicit []byte) (pa s := new(big.Int).SetBytes(payload.signature[48:]) if !ecdsa.Verify(&key.material, hash[:], r, s) { - return packet{}, errorBadSignature + return t.Err[packet](errorBadSignature) } - return packet{data, footer}, nil + return t.Ok(packet{data, footer}) } func v3LocalEncrypt(p packet, key V3SymmetricKey, implicit []byte, unitTestNonce []byte) message { @@ -92,13 +93,13 @@ func v3LocalEncrypt(p packet, key V3SymmetricKey, implicit []byte, unitTestNonce var tag [48]byte copy(tag[:], hm.Sum(nil)) - return newMessageFromPayload(v3LocalPayload{nonce, cipherText, tag}, p.footer) + return newMessageFromPayloadAndFooter(v3LocalPayload{nonce, cipherText, tag}, p.footer) } -func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) (packet, error) { +func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) t.Result[packet] { payload, ok := msg.p.(v3LocalPayload) if msg.header() != V3Local.Header() || !ok { - return packet{}, errorMessageHeaderDecrypt(V3Local, msg.header()) + return t.Err[packet](errorMessageHeaderDecrypt(V3Local, msg.header())) } nonce, cipherText, givenTag := payload.nonce, payload.cipherText, payload.tag @@ -116,8 +117,7 @@ func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) (packet, e copy(expectedTag[:], hm.Sum(nil)) if !hmac.Equal(expectedTag[:], givenTag[:]) { - var p packet - return p, errorBadMAC + return t.Err[packet](errorBadMAC) } blockCipher, err := aes.NewCipher(encKey[:]) @@ -128,5 +128,5 @@ func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) (packet, e plainText := make([]byte, len(cipherText)) cipher.NewCTR(blockCipher, nonce2[:]).XORKeyStream(plainText, cipherText) - return packet{plainText, msg.footer}, nil + return t.Ok(packet{plainText, msg.footer}) } diff --git a/v3_payloads.go b/v3_payloads.go index 36e12b9..33a50ad 100644 --- a/v3_payloads.go +++ b/v3_payloads.go @@ -1,5 +1,7 @@ package paseto +import t "aidanwoods.dev/go-result" + type v3PublicPayload struct { message []byte signature [96]byte @@ -9,11 +11,11 @@ func (p v3PublicPayload) bytes() []byte { return append(p.message, p.signature[:]...) } -func newV3PublicPayload(bytes []byte) (v3PublicPayload, error) { +func newV3PublicPayload(bytes []byte) t.Result[v3PublicPayload] { signatureOffset := len(bytes) - 96 if signatureOffset < 0 { - return v3PublicPayload{}, errorPayloadShort + return t.Err[v3PublicPayload](errorPayloadShort) } message := make([]byte, len(bytes)-96) @@ -22,7 +24,7 @@ func newV3PublicPayload(bytes []byte) (v3PublicPayload, error) { var signature [96]byte copy(signature[:], bytes[signatureOffset:]) - return v3PublicPayload{message, signature}, nil + return t.Ok(v3PublicPayload{message, signature}) } type v3LocalPayload struct { @@ -35,9 +37,9 @@ func (p v3LocalPayload) bytes() []byte { return append(append(p.nonce[:], p.cipherText...), p.tag[:]...) } -func newV3LocalPayload(bytes []byte) (v3LocalPayload, error) { +func newV3LocalPayload(bytes []byte) t.Result[v3LocalPayload] { if len(bytes) <= 32+48 { - return v3LocalPayload{}, errorPayloadShort + return t.Err[v3LocalPayload](errorPayloadShort) } macOffset := len(bytes) - 48 @@ -51,5 +53,5 @@ func newV3LocalPayload(bytes []byte) (v3LocalPayload, error) { var tag [48]byte copy(tag[:], bytes[macOffset:]) - return v3LocalPayload{nonce, cipherText, tag}, nil + return t.Ok(v3LocalPayload{nonce, cipherText, tag}) } diff --git a/v4.go b/v4.go index 10b7a21..d321971 100644 --- a/v4.go +++ b/v4.go @@ -7,6 +7,7 @@ import ( "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/hashing" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" "golang.org/x/crypto/chacha20" ) @@ -24,13 +25,13 @@ func v4PublicSign(packet packet, key V4AsymmetricSecretKey, implicit []byte) mes var signature [64]byte copy(signature[:], sig) - return newMessageFromPayload(v4PublicPayload{data, signature}, footer) + return newMessageFromPayloadAndFooter(v4PublicPayload{data, signature}, footer) } -func v4PublicVerify(msg message, key V4AsymmetricPublicKey, implicit []byte) (packet, error) { +func v4PublicVerify(msg message, key V4AsymmetricPublicKey, implicit []byte) t.Result[packet] { payload, ok := msg.p.(v4PublicPayload) if msg.header() != V4Public.Header() || !ok { - return packet{}, errorMessageHeaderVerify(V4Public, msg.header()) + return t.Err[packet](errorMessageHeaderVerify(V4Public, msg.header())) } header, footer := []byte(msg.header()), msg.footer @@ -39,10 +40,10 @@ func v4PublicVerify(msg message, key V4AsymmetricPublicKey, implicit []byte) (pa m2 := encoding.Pae(header, data, footer, implicit) if !ed25519.Verify(key.material, m2, payload.signature[:]) { - return packet{}, errorBadSignature + return t.Err[packet](errorBadSignature) } - return packet{data, footer}, nil + return t.Ok(packet{data, footer}) } func v4LocalEncrypt(p packet, key V4SymmetricKey, implicit []byte, unitTestNonce []byte) message { @@ -66,13 +67,13 @@ func v4LocalEncrypt(p packet, key V4SymmetricKey, implicit []byte, unitTestNonce var tag [32]byte hashing.GenericHash(preAuth, tag[:], authKey[:]) - return newMessageFromPayload(v4LocalPayload{nonce, cipherText, tag}, p.footer) + return newMessageFromPayloadAndFooter(v4LocalPayload{nonce, cipherText, tag}, p.footer) } -func v4LocalDecrypt(msg message, key V4SymmetricKey, implicit []byte) (packet, error) { +func v4LocalDecrypt(msg message, key V4SymmetricKey, implicit []byte) t.Result[packet] { payload, ok := msg.p.(v4LocalPayload) if msg.header() != V4Local.Header() || !ok { - return packet{}, errorMessageHeaderDecrypt(V4Local, msg.header()) + return t.Err[packet](errorMessageHeaderDecrypt(V4Local, msg.header())) } nonce, cipherText, givenTag := payload.nonce, payload.cipherText, payload.tag @@ -86,7 +87,7 @@ func v4LocalDecrypt(msg message, key V4SymmetricKey, implicit []byte) (packet, e hashing.GenericHash(preAuth, expectedTag[:], authKey[:]) if !hmac.Equal(expectedTag[:], givenTag[:]) { - return packet{}, errorBadMAC + return t.Err[packet](errorBadMAC) } cipher, err := chacha20.NewUnauthenticatedCipher(encKey[:], nonce2[:]) @@ -97,5 +98,5 @@ func v4LocalDecrypt(msg message, key V4SymmetricKey, implicit []byte) (packet, e plainText := make([]byte, len(cipherText)) cipher.XORKeyStream(plainText, cipherText) - return packet{plainText, msg.footer}, nil + return t.Ok(packet{plainText, msg.footer}) } diff --git a/v4_payloads.go b/v4_payloads.go index 436e6df..9404aec 100644 --- a/v4_payloads.go +++ b/v4_payloads.go @@ -1,5 +1,7 @@ package paseto +import t "aidanwoods.dev/go-result" + type v4PublicPayload struct { message []byte signature [64]byte @@ -9,11 +11,11 @@ func (p v4PublicPayload) bytes() []byte { return append(p.message, p.signature[:]...) } -func newV4PublicPayload(bytes []byte) (v4PublicPayload, error) { +func newV4PublicPayload(bytes []byte) t.Result[v4PublicPayload] { signatureOffset := len(bytes) - 64 if signatureOffset < 0 { - return v4PublicPayload{}, errorPayloadShort + return t.Err[v4PublicPayload](errorPayloadShort) } message := make([]byte, len(bytes)-64) @@ -22,7 +24,7 @@ func newV4PublicPayload(bytes []byte) (v4PublicPayload, error) { var signature [64]byte copy(signature[:], bytes[signatureOffset:]) - return v4PublicPayload{message, signature}, nil + return t.Ok(v4PublicPayload{message, signature}) } type v4LocalPayload struct { @@ -35,9 +37,9 @@ func (p v4LocalPayload) bytes() []byte { return append(append(p.nonce[:], p.cipherText...), p.tag[:]...) } -func newV4LocalPayload(bytes []byte) (v4LocalPayload, error) { +func newV4LocalPayload(bytes []byte) t.Result[v4LocalPayload] { if len(bytes) <= 32+32 { - return v4LocalPayload{}, errorPayloadShort + return t.Err[v4LocalPayload](errorPayloadShort) } macOffset := len(bytes) - 32 @@ -51,5 +53,5 @@ func newV4LocalPayload(bytes []byte) (v4LocalPayload, error) { var tag [32]byte copy(tag[:], bytes[macOffset:]) - return v4LocalPayload{nonce, cipherText, tag}, nil + return t.Ok(v4LocalPayload{nonce, cipherText, tag}) } diff --git a/vectors_test.go b/vectors_test.go index 65c2d3c..475616e 100644 --- a/vectors_test.go +++ b/vectors_test.go @@ -8,6 +8,7 @@ import ( "testing" "aidanwoods.dev/go-paseto" + ty "aidanwoods.dev/go-result" "github.com/stretchr/testify/require" ) @@ -39,7 +40,7 @@ func TestV2(t *testing.T) { for _, test := range tests.Tests { t.Run(test.Name, func(t *testing.T) { - var decoded paseto.Packet + var decoded ty.Result[paseto.Packet] switch test.Key { // Local mode @@ -47,50 +48,50 @@ func TestV2(t *testing.T) { sk, err := paseto.V2SymmetricKeyFromHex(test.Key) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V2Local, test.Token) + message := paseto.NewMessage(paseto.V2Local, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V2LocalDecrypt(message, sk) + decoded = paseto.V2LocalDecrypt(message.Unwrap(), sk) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") // Public mode case "": pk, err := paseto.NewV2AsymmetricPublicKeyFromHex(test.PublicKey) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V2Public, test.Token) + message := paseto.NewMessage(paseto.V2Public, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V2PublicVerify(message, pk) + decoded = paseto.V2PublicVerify(message.Unwrap(), pk) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") } - require.Equal(t, test.Payload, string(decoded.Content())) - require.Equal(t, test.Footer, string(decoded.Footer())) + require.Equal(t, test.Payload, string(decoded.Unwrap().Content())) + require.Equal(t, test.Footer, string(decoded.Unwrap().Footer())) packet := paseto.NewPacket([]byte(test.Payload), []byte(test.Footer)) @@ -132,7 +133,7 @@ func TestV3(t *testing.T) { for _, test := range tests.Tests { t.Run(test.Name, func(t *testing.T) { - var decoded paseto.Packet + var decoded ty.Result[paseto.Packet] switch test.Key { // Local mode @@ -140,50 +141,50 @@ func TestV3(t *testing.T) { sk, err := paseto.V3SymmetricKeyFromHex(test.Key) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V3Local, test.Token) + message := paseto.NewMessage(paseto.V3Local, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V3LocalDecrypt(message, sk, []byte(test.ImplicitAssertation)) + decoded = paseto.V3LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") // Public mode case "": pk, err := paseto.NewV3AsymmetricPublicKeyFromHex(test.PublicKey) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V3Public, test.Token) + message := paseto.NewMessage(paseto.V3Public, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V3PublicVerify(message, pk, []byte(test.ImplicitAssertation)) + decoded = paseto.V3PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") } - require.Equal(t, test.Payload, string(decoded.Content())) - require.Equal(t, test.Footer, string(decoded.Footer())) + require.Equal(t, test.Payload, string(decoded.Unwrap().Content())) + require.Equal(t, test.Footer, string(decoded.Unwrap().Footer())) packet := paseto.NewPacket([]byte(test.Payload), []byte(test.Footer)) implicit := []byte(test.ImplicitAssertation) @@ -214,11 +215,11 @@ func TestV3(t *testing.T) { pk, err := paseto.NewV3AsymmetricPublicKeyFromHex(test.PublicKey) require.NoError(t, err) - decoded, err = paseto.V3PublicVerify(signed, pk, []byte(test.ImplicitAssertation)) + decoded = paseto.V3PublicVerify(signed, pk, []byte(test.ImplicitAssertation)) require.NoError(t, err) - require.Equal(t, test.Payload, string(decoded.Content())) - require.Equal(t, test.Footer, string(decoded.Footer())) + require.Equal(t, test.Payload, string(decoded.Unwrap().Content())) + require.Equal(t, test.Footer, string(decoded.Unwrap().Footer())) } }) } @@ -240,7 +241,7 @@ func TestV4(t *testing.T) { for _, test := range tests.Tests { t.Run(test.Name, func(t *testing.T) { - var decoded paseto.Packet + var decoded ty.Result[paseto.Packet] switch test.Key { // Local mode @@ -248,50 +249,50 @@ func TestV4(t *testing.T) { sk, err := paseto.V4SymmetricKeyFromHex(test.Key) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V4Local, test.Token) + message := paseto.NewMessage(paseto.V4Local, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V4LocalDecrypt(message, sk, []byte(test.ImplicitAssertation)) + decoded = paseto.V4LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") // Public mode case "": pk, err := paseto.NewV4AsymmetricPublicKeyFromHex(test.PublicKey) require.NoError(t, err) - message, err := paseto.NewMessage(paseto.V4Public, test.Token) + message := paseto.NewMessage(paseto.V4Public, test.Token) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *message.Err()) + require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + message.Expect("message should be present") - decoded, err = paseto.V4PublicVerify(message, pk, []byte(test.ImplicitAssertation)) + decoded = paseto.V4PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, err) - require.ErrorIs(t, err, &paseto.TokenError{}) - require.NotErrorIs(t, err, &paseto.RuleError{}) + require.Error(t, *decoded.Err()) + require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) + require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) return } - require.NoError(t, err) + decoded.Expect("decoded should be present") } - require.Equal(t, test.Payload, string(decoded.Content())) - require.Equal(t, test.Footer, string(decoded.Footer())) + require.Equal(t, test.Payload, string(decoded.Unwrap().Content())) + require.Equal(t, test.Footer, string(decoded.Unwrap().Footer())) packet := paseto.NewPacket([]byte(test.Payload), []byte(test.Footer)) implicit := []byte(test.ImplicitAssertation) From 8ef9c38d3095febedc4a213486168156b4bb273a Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Tue, 21 Feb 2023 21:37:19 +0000 Subject: [PATCH 02/12] Remove nested calls for readability --- message.go | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/message.go b/message.go index 7d818ba..7dd590d 100644 --- a/message.go +++ b/message.go @@ -19,22 +19,32 @@ type message struct { // protocol. If the given token does not match the given token, or if the // token cannot be parsed, will return an error instead. func newMessage(protocol Protocol, token string) t.Result[message] { - parts := t.AndThen(deconstructToken(token), func(parts deconstructedToken) t.Result[deconstructedToken] { - if parts.header != protocol.Header() { - return t.Err[deconstructedToken](errorMessageHeader(protocol, parts.header)) - } + var parts deconstructedToken + if partsRes := deconstructToken(token); partsRes.IsErr() { + return t.Err[message](partsRes.UnwrapErr()) + } else { + parts = partsRes.Unwrap() + } - return t.Ok(parts) - }) + if parts.header != protocol.Header() { + return t.Err[message](errorMessageHeader(protocol, parts.header)) + } - return t.AndThen(parts, func(parts deconstructedToken) t.Result[message] { - return t.MapErr(newTokenError, t.Map2( - t.AndThen(encoding.Decode(parts.encodedPayload), protocol.newPayload), - encoding.Decode(parts.encodedFooter), + var pload payload + if payloadRes := t.AndThen(encoding.Decode(parts.encodedPayload), protocol.newPayload); payloadRes.IsErr() { + return t.Err[message](newTokenError(payloadRes.UnwrapErr())) + } else { + pload = payloadRes.Unwrap() + } - newMessageFromPayloadAndFooter, - )) - }) + var encodedFooter []byte + if footerRes := encoding.Decode(parts.encodedFooter); footerRes.IsErr() { + return t.Err[message](footerRes.UnwrapErr()) + } else { + encodedFooter = footerRes.Unwrap() + } + + return t.Ok(newMessageFromPayloadAndFooter(pload, encodedFooter)) } // Header returns the header string for a Paseto message. From 9b9523746d52cf6c3cde05c66bc9f18e1697a427 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Tue, 21 Feb 2023 23:39:25 +0000 Subject: [PATCH 03/12] Use UnwrapErr --- vectors_test.go | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/vectors_test.go b/vectors_test.go index 475616e..fa64169 100644 --- a/vectors_test.go +++ b/vectors_test.go @@ -50,18 +50,18 @@ func TestV2(t *testing.T) { message := paseto.NewMessage(paseto.V2Local, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V2LocalDecrypt(message.Unwrap(), sk) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -73,18 +73,18 @@ func TestV2(t *testing.T) { message := paseto.NewMessage(paseto.V2Public, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V2PublicVerify(message.Unwrap(), pk) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -143,18 +143,18 @@ func TestV3(t *testing.T) { message := paseto.NewMessage(paseto.V3Local, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V3LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -166,18 +166,18 @@ func TestV3(t *testing.T) { message := paseto.NewMessage(paseto.V3Public, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V3PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -251,18 +251,18 @@ func TestV4(t *testing.T) { message := paseto.NewMessage(paseto.V4Local, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V4LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -274,18 +274,18 @@ func TestV4(t *testing.T) { message := paseto.NewMessage(paseto.V4Public, test.Token) if test.ExpectFail { - require.Error(t, *message.Err()) - require.ErrorIs(t, *message.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *message.Err(), &paseto.RuleError{}) + require.Error(t, message.UnwrapErr()) + require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) return } message.Expect("message should be present") decoded = paseto.V4PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { - require.Error(t, *decoded.Err()) - require.ErrorIs(t, *decoded.Err(), &paseto.TokenError{}) - require.NotErrorIs(t, *decoded.Err(), &paseto.RuleError{}) + require.Error(t, decoded.UnwrapErr()) + require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return } decoded.Expect("decoded should be present") From dd4237e030ed46fa582aafbc17b25c3398d42de9 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 22 Feb 2023 00:51:17 +0000 Subject: [PATCH 04/12] Rewrite using apply helper --- message.go | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/message.go b/message.go index 7dd590d..c2817e0 100644 --- a/message.go +++ b/message.go @@ -19,32 +19,23 @@ type message struct { // protocol. If the given token does not match the given token, or if the // token cannot be parsed, will return an error instead. func newMessage(protocol Protocol, token string) t.Result[message] { - var parts deconstructedToken - if partsRes := deconstructToken(token); partsRes.IsErr() { - return t.Err[message](partsRes.UnwrapErr()) - } else { - parts = partsRes.Unwrap() - } - - if parts.header != protocol.Header() { - return t.Err[message](errorMessageHeader(protocol, parts.header)) - } - - var pload payload - if payloadRes := t.AndThen(encoding.Decode(parts.encodedPayload), protocol.newPayload); payloadRes.IsErr() { - return t.Err[message](newTokenError(payloadRes.UnwrapErr())) - } else { - pload = payloadRes.Unwrap() - } - - var encodedFooter []byte - if footerRes := encoding.Decode(parts.encodedFooter); footerRes.IsErr() { - return t.Err[message](footerRes.UnwrapErr()) - } else { - encodedFooter = footerRes.Unwrap() - } - - return t.Ok(newMessageFromPayloadAndFooter(pload, encodedFooter)) + return t.Out[message]( + deconstructToken(token)).AndThen(func(parts deconstructedToken) t.Result[message] { + if parts.header != protocol.Header() { + return t.Err[message](errorMessageHeader(protocol, parts.header)) + } + + return t.Out2[message, payload]( + encoding.Decode(parts.encodedPayload)). + AndThen(protocol.newPayload). + AndThen(func(p payload) t.Result[message] { + return t.Out[message]( + encoding.Decode(parts.encodedFooter)). + AndThen(func(footer []byte) t.Result[message] { + return t.Ok(newMessageFromPayloadAndFooter(p, footer)) + }) + }).MapError(t.ErrMapUp(newTokenError)) + }) } // Header returns the header string for a Paseto message. From 6a81caf5bf64e397685404775af1dbc9265a3e9c Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 22 Feb 2023 01:07:42 +0000 Subject: [PATCH 05/12] Formatting --- message.go | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/message.go b/message.go index c2817e0..3485000 100644 --- a/message.go +++ b/message.go @@ -20,22 +20,24 @@ type message struct { // token cannot be parsed, will return an error instead. func newMessage(protocol Protocol, token string) t.Result[message] { return t.Out[message]( - deconstructToken(token)).AndThen(func(parts deconstructedToken) t.Result[message] { - if parts.header != protocol.Header() { - return t.Err[message](errorMessageHeader(protocol, parts.header)) - } - - return t.Out2[message, payload]( - encoding.Decode(parts.encodedPayload)). - AndThen(protocol.newPayload). - AndThen(func(p payload) t.Result[message] { - return t.Out[message]( - encoding.Decode(parts.encodedFooter)). - AndThen(func(footer []byte) t.Result[message] { - return t.Ok(newMessageFromPayloadAndFooter(p, footer)) - }) - }).MapError(t.ErrMapUp(newTokenError)) - }) + deconstructToken(token)). + AndThen(func(parts deconstructedToken) t.Result[message] { + if parts.header != protocol.Header() { + return t.Err[message](errorMessageHeader(protocol, parts.header)) + } + + return t.Out2[message, payload]( + encoding.Decode(parts.encodedPayload)). + AndThen(protocol.newPayload). + AndThen(func(p payload) t.Result[message] { + return t.Out[message]( + encoding.Decode(parts.encodedFooter)). + AndThen(func(footer []byte) t.Result[message] { + return t.Ok(newMessageFromPayloadAndFooter(p, footer)) + }) + }) + }). + MapError(t.ErrMapUp(newTokenError)) } // Header returns the header string for a Paseto message. From 3a3f7d18f73b71d8dc376abcd8354a0fdc999cb6 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 22 Feb 2023 01:31:46 +0000 Subject: [PATCH 06/12] Detect type match even if not pointer --- errors.go | 10 ++++++---- vectors_test.go | 45 +++++++++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/errors.go b/errors.go index 7ee4efc..090c310 100644 --- a/errors.go +++ b/errors.go @@ -18,8 +18,9 @@ func (e TokenError) Error() string { } func (_ TokenError) Is(e error) bool { - _, ok := e.(*TokenError) - return ok + _, ok1 := e.(TokenError) + _, ok2 := e.(*TokenError) + return ok1 || ok2 } func (e TokenError) Unwrap() error { @@ -47,8 +48,9 @@ func (e RuleError) Error() string { } func (_ RuleError) Is(e error) bool { - _, ok := e.(*RuleError) - return ok + _, ok1 := e.(RuleError) + _, ok2 := e.(*RuleError) + return ok1 || ok2 } func (e RuleError) Unwrap() error { diff --git a/vectors_test.go b/vectors_test.go index fa64169..2be9621 100644 --- a/vectors_test.go +++ b/vectors_test.go @@ -51,8 +51,8 @@ func TestV2(t *testing.T) { message := paseto.NewMessage(paseto.V2Local, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -60,8 +60,8 @@ func TestV2(t *testing.T) { decoded = paseto.V2LocalDecrypt(message.Unwrap(), sk) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) - require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, decoded.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -74,8 +74,8 @@ func TestV2(t *testing.T) { message := paseto.NewMessage(paseto.V2Public, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -83,8 +83,8 @@ func TestV2(t *testing.T) { decoded = paseto.V2PublicVerify(message.Unwrap(), pk) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) - require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, decoded.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -144,8 +144,8 @@ func TestV3(t *testing.T) { message := paseto.NewMessage(paseto.V3Local, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -153,8 +153,8 @@ func TestV3(t *testing.T) { decoded = paseto.V3LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) - require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, decoded.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -167,8 +167,8 @@ func TestV3(t *testing.T) { message := paseto.NewMessage(paseto.V3Public, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -176,8 +176,8 @@ func TestV3(t *testing.T) { decoded = paseto.V3PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) - require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, decoded.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -252,8 +252,8 @@ func TestV4(t *testing.T) { message := paseto.NewMessage(paseto.V4Local, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -261,8 +261,8 @@ func TestV4(t *testing.T) { decoded = paseto.V4LocalDecrypt(message.Unwrap(), sk, []byte(test.ImplicitAssertation)) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) - require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, decoded.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, decoded.UnwrapErr(), paseto.RuleError{}) return } decoded.Expect("decoded should be present") @@ -275,8 +275,8 @@ func TestV4(t *testing.T) { message := paseto.NewMessage(paseto.V4Public, test.Token) if test.ExpectFail { require.Error(t, message.UnwrapErr()) - require.ErrorIs(t, message.UnwrapErr(), &paseto.TokenError{}) - require.NotErrorIs(t, message.UnwrapErr(), &paseto.RuleError{}) + require.ErrorIs(t, message.UnwrapErr(), paseto.TokenError{}) + require.NotErrorIs(t, message.UnwrapErr(), paseto.RuleError{}) return } message.Expect("message should be present") @@ -284,6 +284,7 @@ func TestV4(t *testing.T) { decoded = paseto.V4PublicVerify(message.Unwrap(), pk, []byte(test.ImplicitAssertation)) if test.ExpectFail { require.Error(t, decoded.UnwrapErr()) + // check pointer errors still recognised require.ErrorIs(t, decoded.UnwrapErr(), &paseto.TokenError{}) require.NotErrorIs(t, decoded.UnwrapErr(), &paseto.RuleError{}) return From af7de6cb295b60de2b2d7e7b43eca96aef0f4cae Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 22 Feb 2023 01:55:28 +0000 Subject: [PATCH 07/12] Chaining for all --- message.go | 12 +++++----- parser.go | 65 ++++++++++++++++++++++++++++-------------------------- token.go | 10 +++++---- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/message.go b/message.go index 3485000..b71a77a 100644 --- a/message.go +++ b/message.go @@ -101,40 +101,40 @@ func deconstructToken(token string) t.Result[deconstructedToken] { // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v2Verify(key V2AsymmetricPublicKey) t.Result[Token] { - return t.AndThen(v2PublicVerify(m, key), packet.token) + return t.Out[Token](v2PublicVerify(m, key)).AndThen(packet.token) } // V2Decrypt will decrypt a v2 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v2Decrypt(key V2SymmetricKey) t.Result[Token] { - return t.AndThen(v2LocalDecrypt(m, key), packet.token) + return t.Out[Token](v2LocalDecrypt(m, key)).AndThen(packet.token) } // V3Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v3Verify(key V3AsymmetricPublicKey, implicit []byte) t.Result[Token] { - return t.AndThen(v3PublicVerify(m, key, implicit), packet.token) + return t.Out[Token](v3PublicVerify(m, key, implicit)).AndThen(packet.token) } // V3Decrypt will decrypt a v3 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v3Decrypt(key V3SymmetricKey, implicit []byte) t.Result[Token] { - return t.AndThen(v3LocalDecrypt(m, key, implicit), packet.token) + return t.Out[Token](v3LocalDecrypt(m, key, implicit)).AndThen(packet.token) } // V4Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v4Verify(key V4AsymmetricPublicKey, implicit []byte) t.Result[Token] { - return t.AndThen(v4PublicVerify(m, key, implicit), packet.token) + return t.Out[Token](v4PublicVerify(m, key, implicit)).AndThen(packet.token) } // V4Decrypt will decrypt a v4 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v4Decrypt(key V4SymmetricKey, implicit []byte) t.Result[Token] { - return t.AndThen(v4LocalDecrypt(m, key, implicit), packet.token) + return t.Out[Token](v4LocalDecrypt(m, key, implicit)).AndThen(packet.token) } diff --git a/parser.go b/parser.go index 3be5876..fdcc035 100644 --- a/parser.go +++ b/parser.go @@ -36,68 +36,71 @@ func MakeParser(rules []Rule) Parser { // ParseV2Local will parse and decrypt a v2 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV2Local(key V2SymmetricKey, tainted string) (*Token, error) { - token := t.AndThen(newMessage(V2Local, tainted), func(m message) t.Result[Token] { - return m.v2Decrypt(key) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V2Local, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v2Decrypt(key) }). + AndThen(p.validate). + Results() } // ParseV2Public will parse and verify a v2 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV2Public(key V2AsymmetricPublicKey, tainted string) (*Token, error) { - token := t.AndThen(newMessage(V2Public, tainted), func(m message) t.Result[Token] { - return m.v2Verify(key) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V2Public, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v2Verify(key) }). + AndThen(p.validate). + Results() } // ParseV3Local will parse and decrypt a v3 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV3Local(key V3SymmetricKey, tainted string, implicit []byte) (*Token, error) { - token := t.AndThen(newMessage(V3Local, tainted), func(m message) t.Result[Token] { - return m.v3Decrypt(key, implicit) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V3Local, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v3Decrypt(key, implicit) }). + AndThen(p.validate). + Results() } // ParseV3Public will parse and verify a v3 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV3Public(key V3AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - token := t.AndThen(newMessage(V3Public, tainted), func(m message) t.Result[Token] { - return m.v3Verify(key, implicit) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V3Public, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v3Verify(key, implicit) }). + AndThen(p.validate). + Results() } // ParseV4Local will parse and decrypt a v4 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV4Local(key V4SymmetricKey, tainted string, implicit []byte) (*Token, error) { - token := t.AndThen(newMessage(V4Local, tainted), func(m message) t.Result[Token] { - return m.v4Decrypt(key, implicit) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V4Local, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v4Decrypt(key, implicit) }). + AndThen(p.validate). + Results() } // ParseV4Public will parse and verify a v4 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV4Public(key V4AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - token := t.AndThen(newMessage(V4Public, tainted), func(m message) t.Result[Token] { - return m.v4Verify(key, implicit) - }) - - return t.AndThen(token, p.validate).Results() + return t.Out2[Token, Token]( + newMessage(V4Public, tainted)). + AndThen(func(m message) t.Result[Token] { return m.v4Verify(key, implicit) }). + AndThen(p.validate). + Results() } // UnsafeParseFooter returns the footer of a Paseto message. Beware that this // footer is not cryptographically verified at this stage, nor are any claims // validated. func (p Parser) UnsafeParseFooter(protocol Protocol, tainted string) ([]byte, error) { - return t.Map(newMessage(protocol, tainted), message.unsafeFooter).UnwrappedResults() + return t.Out[[]byte]( + newMessage(protocol, tainted)). + Map(message.unsafeFooter). + UnwrappedResults() } // SetRules will overwrite any currently set rules with those specified. diff --git a/token.go b/token.go index f9d52a6..24b188a 100644 --- a/token.go +++ b/token.go @@ -49,10 +49,12 @@ func NewTokenFromClaimsJSON(claimsData []byte, footer []byte) (*Token, error) { // be serialisable to JSON using encoding/json. // Set will check this and return an error if it is not serialisable. func (token *Token) Set(key string, value interface{}) error { - return t.AndThen(marshalTokenValue(value), func(value tokenValue) t.Result[any] { - token.claims[key] = value - return t.Ok[any](nil) - }). + return t.Out[any]( + marshalTokenValue(value)). + AndThen(func(value tokenValue) t.Result[any] { + token.claims[key] = value + return t.Ok[any](nil) + }). WrapErr("could not set key `" + key + "': %w"). UnwrapErrOr(nil) } From 8a5086377beb1dd2b85daf3eab76a6dff3fe1f45 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 22 Feb 2023 02:02:10 +0000 Subject: [PATCH 08/12] Update dep --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e43ed97..1b64e7d 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( ) require ( - aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68 // indirect + aidanwoods.dev/go-result v0.0.0-20230222015312-30be9e9946f2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.6.0 // indirect From 1de44d2d20afb14ed3eaa7306931b588459280b4 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 24 Feb 2023 23:25:41 +0000 Subject: [PATCH 09/12] Use unwrapping helpers --- message.go | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/message.go b/message.go index b71a77a..ca63b71 100644 --- a/message.go +++ b/message.go @@ -19,25 +19,27 @@ type message struct { // protocol. If the given token does not match the given token, or if the // token cannot be parsed, will return an error instead. func newMessage(protocol Protocol, token string) t.Result[message] { - return t.Out[message]( - deconstructToken(token)). - AndThen(func(parts deconstructedToken) t.Result[message] { - if parts.header != protocol.Header() { - return t.Err[message](errorMessageHeader(protocol, parts.header)) - } - - return t.Out2[message, payload]( - encoding.Decode(parts.encodedPayload)). - AndThen(protocol.newPayload). - AndThen(func(p payload) t.Result[message] { - return t.Out[message]( - encoding.Decode(parts.encodedFooter)). - AndThen(func(footer []byte) t.Result[message] { - return t.Ok(newMessageFromPayloadAndFooter(p, footer)) - }) - }) - }). - MapError(t.ErrMapUp(newTokenError)) + var parts deconstructedToken + if err := deconstructToken(token).Ok(&parts); err != nil { + return t.Err[message](err) + } + + if parts.header != protocol.Header() { + return t.Err[message](errorMessageHeader(protocol, parts.header)) + } + + var p payload + if err := t.Out[payload](encoding.Decode(parts.encodedPayload)). + AndThen(protocol.newPayload).Ok(&p); err != nil { + return t.Err[message](newTokenError(err)) + } + + var footer []byte + if err := encoding.Decode(parts.encodedFooter).Ok(&footer); err != nil { + return t.Err[message](newTokenError(err)) + } + + return t.Ok(newMessageFromPayloadAndFooter(p, footer)) } // Header returns the header string for a Paseto message. From 6e515dd5f9368b1a60d7be6183df09b8e3deda3a Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 5 Mar 2023 21:09:37 +0000 Subject: [PATCH 10/12] Update --- go.mod | 2 +- go.sum | 37 ++----------------------------------- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index 1b64e7d..3984c43 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module aidanwoods.dev/go-paseto go 1.18 require ( + aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.7.0 ) require ( - aidanwoods.dev/go-result v0.0.0-20230222015312-30be9e9946f2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index 11e511f..08c86d6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68 h1:LBGFT4p6h1cw7CgJ53HY2wJb0GWBmCAqD/F8Lo6XfbA= -aidanwoods.dev/go-result v0.0.0-20230221001413-7df9f8edcd68/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= +aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261 h1:ag2fikjGlercXesY1VrmIOAR2BdBbcPkvIeDEfBHPSw= +aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -8,43 +8,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 0c148b9e6b2680da7af5773f518d5547ef8317ce Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 5 Mar 2023 21:12:00 +0000 Subject: [PATCH 11/12] User ruleerror constructor --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index fdcc035..bbbb19a 100644 --- a/parser.go +++ b/parser.go @@ -116,7 +116,7 @@ func (p *Parser) AddRule(rule ...Rule) { func (p Parser) validate(token Token) t.Result[Token] { for _, rule := range p.rules { if err := rule(token); err != nil { - return t.Err[Token](RuleError{err}) + return t.Err[Token](newRuleError(err)) } } From 3a3c0c6182d366eca6d3393c4f35bb735e19524d Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 10 Mar 2023 13:50:53 +0000 Subject: [PATCH 12/12] Rephrase internal error handling using results --- go.mod | 2 +- go.sum | 4 +-- internal/encoding/hex.go | 17 +++++++++++ internal/encoding/paseto.go | 18 ++++++------ internal/hashing/hashing.go | 16 ++++------- internal/random/random.go | 8 ++---- message.go | 14 ++++----- parser.go | 14 ++++----- token.go | 14 ++++----- v2.go | 18 +++++------- v2_keys.go | 45 +++++++++++++---------------- v3.go | 29 +++++++------------ v3_keys.go | 38 ++++++++++--------------- v4.go | 12 +++----- v4_keys.go | 57 ++++++++++++++++--------------------- 15 files changed, 139 insertions(+), 167 deletions(-) create mode 100644 internal/encoding/hex.go diff --git a/go.mod b/go.mod index 3984c43..8e99af5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module aidanwoods.dev/go-paseto go 1.18 require ( - aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261 + aidanwoods.dev/go-result v0.0.0-20230310133209-26c34aabd0c7 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.7.0 ) diff --git a/go.sum b/go.sum index 08c86d6..61f63dc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261 h1:ag2fikjGlercXesY1VrmIOAR2BdBbcPkvIeDEfBHPSw= -aidanwoods.dev/go-result v0.0.0-20230304000535-be9423cc2261/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= +aidanwoods.dev/go-result v0.0.0-20230310133209-26c34aabd0c7 h1:bVUDSxup1h+a2DenqxeTWRschTdSMmo1Cy5/LxnzCtc= +aidanwoods.dev/go-result v0.0.0-20230310133209-26c34aabd0c7/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/encoding/hex.go b/internal/encoding/hex.go new file mode 100644 index 0000000..64a8ac9 --- /dev/null +++ b/internal/encoding/hex.go @@ -0,0 +1,17 @@ +package encoding + +import ( + "encoding/hex" + + t "aidanwoods.dev/go-result" +) + +// Encode hex +func HexEncode(bytes []byte) string { + return hex.EncodeToString(bytes) +} + +// Decode hex +func HexDecode(encoded string) t.Result[[]byte] { + return t.NewResult(hex.DecodeString(encoded)) +} diff --git a/internal/encoding/paseto.go b/internal/encoding/paseto.go index dc11439..391e918 100644 --- a/internal/encoding/paseto.go +++ b/internal/encoding/paseto.go @@ -3,6 +3,8 @@ package encoding import ( "bytes" "encoding/binary" + + t "aidanwoods.dev/go-result" ) // Pae Pre Auth Encode @@ -10,18 +12,16 @@ func Pae(pieces ...[]byte) []byte { buffer := &bytes.Buffer{} // MSB should be zero - if err := binary.Write(buffer, binary.LittleEndian, int64(len(pieces))); err != nil { - panic("Cannot write to buffer") - } + t.NewVoidResult(binary.Write(buffer, binary.LittleEndian, int64(len(pieces)))). + Expect("writing to buffer should not fail") for i := range pieces { // MSB should be zero - if err := binary.Write(buffer, binary.LittleEndian, int64(len(pieces[i]))); err != nil { - panic("Cannot write to buffer") - } - if _, err := buffer.Write(pieces[i]); err != nil { - panic("Cannot write to buffer") - } + t.NewVoidResult(binary.Write(buffer, binary.LittleEndian, int64(len(pieces[i])))). + Expect("writing to buffer should not fail") + + t.NewResult(buffer.Write(pieces[i])). + Expect("writing to buffer should not fail") } return buffer.Bytes() diff --git a/internal/hashing/hashing.go b/internal/hashing/hashing.go index 90c3105..e187524 100644 --- a/internal/hashing/hashing.go +++ b/internal/hashing/hashing.go @@ -1,23 +1,17 @@ package hashing import ( - "hash" - + t "aidanwoods.dev/go-result" "golang.org/x/crypto/blake2b" ) // GenericHash The same as crypto_generichash as referred to in the Paseto spec func GenericHash(in, out, key []byte) { - var blake hash.Hash - var err error - - if blake, err = blake2b.New(len(out), key); err != nil { - panic(err) - } + blake := t.NewResult(blake2b.New(len(out), key)). + Expect("blake2 hasher construction should be provided with valid length inputs") - if _, err = blake.Write(in); err != nil { - panic(err) - } + t.NewResult(blake.Write(in)). + Expect("writing into hasher should not fail") copy(out, blake.Sum(nil)) } diff --git a/internal/random/random.go b/internal/random/random.go index 041293b..cfcd560 100644 --- a/internal/random/random.go +++ b/internal/random/random.go @@ -3,15 +3,13 @@ package random import ( "crypto/rand" "io" + + t "aidanwoods.dev/go-result" ) // FillBytes fills out with random bytes from the OS CSPRNG, or panics func FillBytes(out []byte) { - _, err := io.ReadFull(rand.Reader, out[:]) - - if err != nil { - panic("CSPRNG failure") - } + t.NewResult(io.ReadFull(rand.Reader, out[:])).Expect("CSPRNG failure") } // UseProvidedOrFillBytes will fill `out' with unitTestNonce, provided it is diff --git a/message.go b/message.go index ca63b71..bdb3a12 100644 --- a/message.go +++ b/message.go @@ -29,7 +29,7 @@ func newMessage(protocol Protocol, token string) t.Result[message] { } var p payload - if err := t.Out[payload](encoding.Decode(parts.encodedPayload)). + if err := t.Chain[payload](encoding.Decode(parts.encodedPayload)). AndThen(protocol.newPayload).Ok(&p); err != nil { return t.Err[message](newTokenError(err)) } @@ -103,40 +103,40 @@ func deconstructToken(token string) t.Result[deconstructedToken] { // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v2Verify(key V2AsymmetricPublicKey) t.Result[Token] { - return t.Out[Token](v2PublicVerify(m, key)).AndThen(packet.token) + return t.Chain[Token](v2PublicVerify(m, key)).AndThen(packet.token) } // V2Decrypt will decrypt a v2 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v2Decrypt(key V2SymmetricKey) t.Result[Token] { - return t.Out[Token](v2LocalDecrypt(m, key)).AndThen(packet.token) + return t.Chain[Token](v2LocalDecrypt(m, key)).AndThen(packet.token) } // V3Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v3Verify(key V3AsymmetricPublicKey, implicit []byte) t.Result[Token] { - return t.Out[Token](v3PublicVerify(m, key, implicit)).AndThen(packet.token) + return t.Chain[Token](v3PublicVerify(m, key, implicit)).AndThen(packet.token) } // V3Decrypt will decrypt a v3 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v3Decrypt(key V3SymmetricKey, implicit []byte) t.Result[Token] { - return t.Out[Token](v3LocalDecrypt(m, key, implicit)).AndThen(packet.token) + return t.Chain[Token](v3LocalDecrypt(m, key, implicit)).AndThen(packet.token) } // V4Verify will verify a v4 public paseto message. Will return a pointer to // the verified token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v4Verify(key V4AsymmetricPublicKey, implicit []byte) t.Result[Token] { - return t.Out[Token](v4PublicVerify(m, key, implicit)).AndThen(packet.token) + return t.Chain[Token](v4PublicVerify(m, key, implicit)).AndThen(packet.token) } // V4Decrypt will decrypt a v4 local paseto message. Will return a pointer to // the decrypted token (but not validated with rules) if successful, or error in // the event of failure. func (m message) v4Decrypt(key V4SymmetricKey, implicit []byte) t.Result[Token] { - return t.Out[Token](v4LocalDecrypt(m, key, implicit)).AndThen(packet.token) + return t.Chain[Token](v4LocalDecrypt(m, key, implicit)).AndThen(packet.token) } diff --git a/parser.go b/parser.go index bbbb19a..7970266 100644 --- a/parser.go +++ b/parser.go @@ -36,7 +36,7 @@ func MakeParser(rules []Rule) Parser { // ParseV2Local will parse and decrypt a v2 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV2Local(key V2SymmetricKey, tainted string) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V2Local, tainted)). AndThen(func(m message) t.Result[Token] { return m.v2Decrypt(key) }). AndThen(p.validate). @@ -46,7 +46,7 @@ func (p Parser) ParseV2Local(key V2SymmetricKey, tainted string) (*Token, error) // ParseV2Public will parse and verify a v2 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV2Public(key V2AsymmetricPublicKey, tainted string) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V2Public, tainted)). AndThen(func(m message) t.Result[Token] { return m.v2Verify(key) }). AndThen(p.validate). @@ -56,7 +56,7 @@ func (p Parser) ParseV2Public(key V2AsymmetricPublicKey, tainted string) (*Token // ParseV3Local will parse and decrypt a v3 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV3Local(key V3SymmetricKey, tainted string, implicit []byte) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V3Local, tainted)). AndThen(func(m message) t.Result[Token] { return m.v3Decrypt(key, implicit) }). AndThen(p.validate). @@ -66,7 +66,7 @@ func (p Parser) ParseV3Local(key V3SymmetricKey, tainted string, implicit []byte // ParseV3Public will parse and verify a v3 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV3Public(key V3AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V3Public, tainted)). AndThen(func(m message) t.Result[Token] { return m.v3Verify(key, implicit) }). AndThen(p.validate). @@ -76,7 +76,7 @@ func (p Parser) ParseV3Public(key V3AsymmetricPublicKey, tainted string, implici // ParseV4Local will parse and decrypt a v4 local paseto and validate against // any parser rules. Error if parsing, decryption, or any rule fails. func (p Parser) ParseV4Local(key V4SymmetricKey, tainted string, implicit []byte) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V4Local, tainted)). AndThen(func(m message) t.Result[Token] { return m.v4Decrypt(key, implicit) }). AndThen(p.validate). @@ -86,7 +86,7 @@ func (p Parser) ParseV4Local(key V4SymmetricKey, tainted string, implicit []byte // ParseV4Public will parse and verify a v4 public paseto and validate against // any parser rules. Error if parsing, verification, or any rule fails. func (p Parser) ParseV4Public(key V4AsymmetricPublicKey, tainted string, implicit []byte) (*Token, error) { - return t.Out2[Token, Token]( + return t.Chain2[Token, Token]( newMessage(V4Public, tainted)). AndThen(func(m message) t.Result[Token] { return m.v4Verify(key, implicit) }). AndThen(p.validate). @@ -97,7 +97,7 @@ func (p Parser) ParseV4Public(key V4AsymmetricPublicKey, tainted string, implici // footer is not cryptographically verified at this stage, nor are any claims // validated. func (p Parser) UnsafeParseFooter(protocol Protocol, tainted string) ([]byte, error) { - return t.Out[[]byte]( + return t.Chain[[]byte]( newMessage(protocol, tainted)). Map(message.unsafeFooter). UnwrappedResults() diff --git a/token.go b/token.go index 24b188a..335ff56 100644 --- a/token.go +++ b/token.go @@ -49,7 +49,7 @@ func NewTokenFromClaimsJSON(claimsData []byte, footer []byte) (*Token, error) { // be serialisable to JSON using encoding/json. // Set will check this and return an error if it is not serialisable. func (token *Token) Set(key string, value interface{}) error { - return t.Out[any]( + return t.Chain[any]( marshalTokenValue(value)). AndThen(func(value tokenValue) t.Result[any] { token.claims[key] = value @@ -131,13 +131,11 @@ func (t Token) Claims() map[string]interface{} { } // ClaimsJSON gets the stored claims as JSON. -func (t Token) ClaimsJSON() []byte { - data, err := json.Marshal(t.Claims()) - if err != nil { - // these were *just* unmarshalled (and a top level of string keys added) - // it is *very* unexpected if this is not reversable - panic(err) - } +func (token Token) ClaimsJSON() []byte { + // these were *just* unmarshalled (and a top level of string keys added) + // it is *very* unexpected if this is not reversable + data := t.NewResult(json.Marshal(token.Claims())). + Expect("internal claims data should be well formed JSON") return data } diff --git a/v2.go b/v2.go index e3fa758..01e81e4 100644 --- a/v2.go +++ b/v2.go @@ -53,10 +53,8 @@ func v2LocalEncrypt(p packet, key V2SymmetricKey, unitTestNonce []byte) message var nonce [24]byte hashing.GenericHash(p.content, nonce[:], b[:]) - cipher, err := chacha20poly1305.NewX(key.material[:]) - if err != nil { - panic("Cannot construct cipher") - } + cipher := t.NewResult(chacha20poly1305.NewX(key.material[:])). + Expect("constructing cipher should not fail") header := []byte(V2Local.Header()) @@ -79,15 +77,13 @@ func v2LocalDecrypt(msg message, key V2SymmetricKey) t.Result[packet] { preAuth := encoding.Pae(header, nonce[:], msg.footer) - cipher, err := chacha20poly1305.NewX(key.material[:]) - if err != nil { - panic("Cannot construct cipher") - } + cipher := t.NewResult(chacha20poly1305.NewX(key.material[:])). + Expect("constructing cipher should not fail") - plainText, err := cipher.Open(nil, nonce[:], cipherText, preAuth) - if err != nil { + var plaintext []byte + if err := t.NewResult(cipher.Open(nil, nonce[:], cipherText, preAuth)).Ok(&plaintext); err != nil { return t.Err[packet](errorDecrypt(err)) } - return t.Ok(packet{plainText, msg.footer}) + return t.Ok(packet{plaintext, msg.footer}) } diff --git a/v2_keys.go b/v2_keys.go index 7f81726..b10eee8 100644 --- a/v2_keys.go +++ b/v2_keys.go @@ -4,7 +4,9 @@ import ( "crypto/ed25519" "encoding/hex" + "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" ) // V2AsymmetricPublicKey V2 public public key @@ -14,8 +16,8 @@ type V2AsymmetricPublicKey struct { // NewV2AsymmetricPublicKeyFromHex Construct a v2 public key from hex func NewV2AsymmetricPublicKeyFromHex(hexEncoded string) (V2AsymmetricPublicKey, error) { - publicKey, err := hex.DecodeString(hexEncoded) - if err != nil { + var publicKey []byte + if err := encoding.HexDecode(hexEncoded).Ok(&publicKey); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV2AsymmetricSecretKey().Public(), err @@ -37,7 +39,7 @@ func NewV2AsymmetricPublicKeyFromBytes(publicKey []byte) (V2AsymmetricPublicKey, // ExportHex export a V2AsymmetricPublicKey to hex for storage func (k V2AsymmetricPublicKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V2AsymmetricPublicKey to raw byte array @@ -52,18 +54,15 @@ type V2AsymmetricSecretKey struct { // Public returns the corresponding public key for a secret key func (k V2AsymmetricSecretKey) Public() V2AsymmetricPublicKey { - material, ok := k.material.Public().(ed25519.PublicKey) - - if !ok { - panic("Wrong public key returned") + return V2AsymmetricPublicKey{ + material: t.Cast[ed25519.PublicKey](k.material.Public()). + Expect("wrong public key returned"), } - - return V2AsymmetricPublicKey{material} } // ExportHex export a V2AsymmetricSecretKey to hex for storage func (k V2AsymmetricSecretKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V2AsymmetricSecretKey to raw byte array @@ -73,27 +72,24 @@ func (k V2AsymmetricSecretKey) ExportBytes() []byte { // ExportSeedHex export a V2AsymmetricSecretKey's seed to hex for storage func (k V2AsymmetricSecretKey) ExportSeedHex() string { - return hex.EncodeToString(k.material.Seed()) + return encoding.HexEncode(k.material.Seed()) } // NewV2AsymmetricSecretKey generate a new secret key for use with asymmetric // cryptography. Don't forget to export the public key for sharing, DO NOT share // this secret key. func NewV2AsymmetricSecretKey() V2AsymmetricSecretKey { - _, privateKey, err := ed25519.GenerateKey(nil) - - if err != nil { - panic("CSPRNG failure") + return V2AsymmetricSecretKey{ + material: t.NewTupleResult(ed25519.GenerateKey(nil)). + Expect("CSPRNG should not fail"). + Second, } - - return V2AsymmetricSecretKey{privateKey} } // NewV2AsymmetricSecretKeyFromHex creates a secret key from hex func NewV2AsymmetricSecretKeyFromHex(hexEncoded string) (V2AsymmetricSecretKey, error) { - privateKey, err := hex.DecodeString(hexEncoded) - - if err != nil { + var privateKey []byte + if err := encoding.HexDecode(hexEncoded).Ok(&privateKey); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV2AsymmetricSecretKey(), err @@ -123,9 +119,8 @@ func NewV2AsymmetricSecretKeyFromBytes(privateKey []byte) (V2AsymmetricSecretKey // NewV2AsymmetricSecretKeyFromSeed creates a secret key from a seed (hex) func NewV2AsymmetricSecretKeyFromSeed(hexEncoded string) (V2AsymmetricSecretKey, error) { - seedBytes, err := hex.DecodeString(hexEncoded) - - if err != nil { + var seedBytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&seedBytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV2AsymmetricSecretKey(), err @@ -165,8 +160,8 @@ func (k V2SymmetricKey) ExportBytes() []byte { // V2SymmetricKeyFromHex constructs a key from hex func V2SymmetricKeyFromHex(hexEncoded string) (V2SymmetricKey, error) { - bytes, err := hex.DecodeString(hexEncoded) - if err != nil { + var bytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&bytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV2SymmetricKey(), err diff --git a/v3.go b/v3.go index 9039812..0be9d8b 100644 --- a/v3.go +++ b/v3.go @@ -22,10 +22,9 @@ func v3PublicSign(packet packet, key V3AsymmetricSecretKey, implicit []byte) mes hash := sha512.Sum384(m2) - r, s, err := ecdsa.Sign(rand.Reader, &key.material, hash[:]) - if err != nil { - panic("Failed to sign") - } + r, s := t.NewTupleResult(ecdsa.Sign(rand.Reader, &key.material, hash[:])). + Expect("sign should always succeed"). + Destructure() var rBytes [48]byte var sBytes [48]byte @@ -74,10 +73,8 @@ func v3LocalEncrypt(p packet, key V3SymmetricKey, implicit []byte, unitTestNonce encKey, authKey, nonce2 := key.split(nonce) - blockCipher, err := aes.NewCipher(encKey[:]) - if err != nil { - panic("Cannot construct cipher") - } + blockCipher := t.NewResult(aes.NewCipher(encKey[:])). + Expect("cipher should construct") cipherText := make([]byte, len(p.content)) cipher.NewCTR(blockCipher, nonce2[:]).XORKeyStream(cipherText, p.content) @@ -87,9 +84,8 @@ func v3LocalEncrypt(p packet, key V3SymmetricKey, implicit []byte, unitTestNonce preAuth := encoding.Pae(header, nonce[:], cipherText, p.footer, implicit) hm := hmac.New(sha512.New384, authKey[:]) - if _, err := hm.Write(preAuth); err != nil { - panic(err) - } + t.NewResult(hm.Write(preAuth)).Expect("hmac write should succeed") + var tag [48]byte copy(tag[:], hm.Sum(nil)) @@ -110,9 +106,8 @@ func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) t.Result[p preAuth := encoding.Pae(header, nonce[:], cipherText, msg.footer, implicit) hm := hmac.New(sha512.New384, authKey[:]) - if _, err := hm.Write(preAuth); err != nil { - panic(err) - } + t.NewResult(hm.Write(preAuth)).Expect("hmac write should succeed") + var expectedTag [48]byte copy(expectedTag[:], hm.Sum(nil)) @@ -120,10 +115,8 @@ func v3LocalDecrypt(msg message, key V3SymmetricKey, implicit []byte) t.Result[p return t.Err[packet](errorBadMAC) } - blockCipher, err := aes.NewCipher(encKey[:]) - if err != nil { - panic("Cannot construct cipher") - } + blockCipher := t.NewResult(aes.NewCipher(encKey[:])). + Expect("cipher should construct") plainText := make([]byte, len(cipherText)) cipher.NewCTR(blockCipher, nonce2[:]).XORKeyStream(plainText, cipherText) diff --git a/v3_keys.go b/v3_keys.go index 3df2539..ab293f0 100644 --- a/v3_keys.go +++ b/v3_keys.go @@ -5,11 +5,12 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/sha512" - "encoding/hex" "io" "math/big" + "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" "golang.org/x/crypto/hkdf" ) @@ -20,9 +21,8 @@ type V3AsymmetricPublicKey struct { // NewV3AsymmetricPublicKeyFromHex Construct a v3 public key from hex func NewV3AsymmetricPublicKeyFromHex(hexEncoded string) (V3AsymmetricPublicKey, error) { - publicKeyBytes, err := hex.DecodeString(hexEncoded) - - if err != nil { + var publicKeyBytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&publicKeyBytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV3AsymmetricSecretKey().Public(), err @@ -52,7 +52,7 @@ func (k V3AsymmetricPublicKey) compressed() []byte { // ExportHex export a V3AsymmetricPublicKey to hex for storage func (k V3AsymmetricPublicKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V3AsymmetricPublicKey to raw byte array @@ -72,7 +72,7 @@ func (k V3AsymmetricSecretKey) Public() V3AsymmetricPublicKey { // ExportHex export a V3AsymmetricSecretKey to hex for storage func (k V3AsymmetricSecretKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V3AsymmetricSecretKey to raw byte array @@ -84,20 +84,16 @@ func (k V3AsymmetricSecretKey) ExportBytes() []byte { // cryptography. Don't forget to export the public key for sharing, DO NOT share // this secret key. func NewV3AsymmetricSecretKey() V3AsymmetricSecretKey { - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - - if err != nil { - panic("CSPRNG failure") - } + privateKey := t.NewResult(ecdsa.GenerateKey(elliptic.P384(), rand.Reader)). + Expect("CSPRNG should not fail") return V3AsymmetricSecretKey{*privateKey} } // NewV3AsymmetricSecretKeyFromHex creates a secret key from hex func NewV3AsymmetricSecretKeyFromHex(hexEncoded string) (V3AsymmetricSecretKey, error) { - secretBytes, err := hex.DecodeString(hexEncoded) - - if err != nil { + var secretBytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&secretBytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV3AsymmetricSecretKey(), err @@ -141,7 +137,7 @@ func NewV3SymmetricKey() V3SymmetricKey { // ExportHex exports the key as hex for storage func (k V3SymmetricKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes exports the key as raw byte array @@ -151,8 +147,8 @@ func (k V3SymmetricKey) ExportBytes() []byte { // V3SymmetricKeyFromHex constructs a key from hex func V3SymmetricKeyFromHex(hexEncoded string) (V3SymmetricKey, error) { - bytes, err := hex.DecodeString(hexEncoded) - if err != nil { + var bytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&bytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV3SymmetricKey(), err @@ -184,9 +180,7 @@ func (k V3SymmetricKey) split(nonce [32]byte) (encKey [32]byte, authKey [48]byte ) var tmp [48]byte - if _, err := io.ReadFull(kdf, tmp[:]); err != nil { - panic(err) - } + t.NewResult(io.ReadFull(kdf, tmp[:])).Expect("hkdf should not fail") copy(encKey[:], tmp[0:32]) copy(nonce2[:], tmp[32:48]) @@ -197,9 +191,7 @@ func (k V3SymmetricKey) split(nonce [32]byte) (encKey [32]byte, authKey [48]byte nil, append([]byte("paseto-auth-key-for-aead"), nonce[:]...), ) - if _, err := io.ReadFull(kdf, authKey[:]); err != nil { - panic(err) - } + t.NewResult(io.ReadFull(kdf, authKey[:])).Expect("hkdf should not fail") return encKey, authKey, nonce2 } diff --git a/v4.go b/v4.go index d321971..4eea543 100644 --- a/v4.go +++ b/v4.go @@ -52,10 +52,8 @@ func v4LocalEncrypt(p packet, key V4SymmetricKey, implicit []byte, unitTestNonce encKey, authKey, nonce2 := key.split(nonce) - cipher, err := chacha20.NewUnauthenticatedCipher(encKey[:], nonce2[:]) - if err != nil { - panic("Cannot construct cipher") - } + cipher := t.NewResult(chacha20.NewUnauthenticatedCipher(encKey[:], nonce2[:])). + Expect("cipher should construct") cipherText := make([]byte, len(p.content)) cipher.XORKeyStream(cipherText, p.content) @@ -90,10 +88,8 @@ func v4LocalDecrypt(msg message, key V4SymmetricKey, implicit []byte) t.Result[p return t.Err[packet](errorBadMAC) } - cipher, err := chacha20.NewUnauthenticatedCipher(encKey[:], nonce2[:]) - if err != nil { - panic("Cannot construct cipher") - } + cipher := t.NewResult(chacha20.NewUnauthenticatedCipher(encKey[:], nonce2[:])). + Expect("cipher should construct") plainText := make([]byte, len(cipherText)) cipher.XORKeyStream(plainText, cipherText) diff --git a/v4_keys.go b/v4_keys.go index 3f78b24..7f09a5d 100644 --- a/v4_keys.go +++ b/v4_keys.go @@ -2,10 +2,11 @@ package paseto import ( "crypto/ed25519" - "encoding/hex" + "aidanwoods.dev/go-paseto/internal/encoding" "aidanwoods.dev/go-paseto/internal/hashing" "aidanwoods.dev/go-paseto/internal/random" + t "aidanwoods.dev/go-result" ) // V4AsymmetricPublicKey v4 public public key @@ -15,9 +16,8 @@ type V4AsymmetricPublicKey struct { // NewV4AsymmetricPublicKeyFromHex Construct a v4 public key from hex func NewV4AsymmetricPublicKeyFromHex(hexEncoded string) (V4AsymmetricPublicKey, error) { - publicKey, err := hex.DecodeString(hexEncoded) - - if err != nil { + var publicKey []byte + if err := encoding.HexDecode(hexEncoded).Ok(&publicKey); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV4AsymmetricSecretKey().Public(), err @@ -39,7 +39,7 @@ func NewV4AsymmetricPublicKeyFromBytes(publicKey []byte) (V4AsymmetricPublicKey, // ExportHex export a V4AsymmetricPublicKey to hex for storage func (k V4AsymmetricPublicKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V4AsymmetricPublicKey to raw byte array @@ -54,18 +54,15 @@ type V4AsymmetricSecretKey struct { // Public returns the corresponding public key for a secret key func (k V4AsymmetricSecretKey) Public() V4AsymmetricPublicKey { - material, ok := k.material.Public().(ed25519.PublicKey) - - if !ok { - panic("Wrong public key returned") + return V4AsymmetricPublicKey{ + material: t.Cast[ed25519.PublicKey](k.material.Public()). + Expect("should produce ed25519 public key"), } - - return V4AsymmetricPublicKey{material} } // ExportHex export a V4AsymmetricSecretKey to hex for storage func (k V4AsymmetricSecretKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes export a V4AsymmetricSecretKey to raw byte array @@ -75,27 +72,24 @@ func (k V4AsymmetricSecretKey) ExportBytes() []byte { // ExportSeedHex export a V4AsymmetricSecretKey's seed to hex for storage func (k V4AsymmetricSecretKey) ExportSeedHex() string { - return hex.EncodeToString(k.material.Seed()) + return encoding.HexEncode(k.material.Seed()) } // NewV4AsymmetricSecretKey generate a new secret key for use with asymmetric // cryptography. Don't forget to export the public key for sharing, DO NOT share // this secret key. func NewV4AsymmetricSecretKey() V4AsymmetricSecretKey { - _, privateKey, err := ed25519.GenerateKey(nil) - - if err != nil { - panic("CSPRNG failure") + return V4AsymmetricSecretKey{ + material: t.NewTupleResult(ed25519.GenerateKey(nil)). + Expect("CSPRNG should succeed"). + Second, } - - return V4AsymmetricSecretKey{privateKey} } // NewV4AsymmetricSecretKeyFromHex creates a secret key from hex func NewV4AsymmetricSecretKeyFromHex(hexEncoded string) (V4AsymmetricSecretKey, error) { - privateKey, err := hex.DecodeString(hexEncoded) - - if err != nil { + var privateKey []byte + if err := encoding.HexDecode(hexEncoded).Ok(&privateKey); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV4AsymmetricSecretKey(), err @@ -107,8 +101,10 @@ func NewV4AsymmetricSecretKeyFromHex(hexEncoded string) (V4AsymmetricSecretKey, func isEd25519KeyPairMalformed(privateKey []byte) bool { seed := privateKey[:32] - pubKeyFromGiven := ed25519.PrivateKey(privateKey).Public().(ed25519.PublicKey) - pubKeyFromSeed := ed25519.NewKeyFromSeed(seed).Public().(ed25519.PublicKey) + pubKeyFromGiven := t.Cast[ed25519.PublicKey](ed25519.PrivateKey(privateKey).Public()). + Expect("should return ed25519 public key") + pubKeyFromSeed := t.Cast[ed25519.PublicKey](ed25519.NewKeyFromSeed(seed).Public()). + Expect("should return ed25519 public key") return !pubKeyFromGiven.Equal(pubKeyFromSeed) } @@ -134,9 +130,8 @@ func NewV4AsymmetricSecretKeyFromBytes(privateKey []byte) (V4AsymmetricSecretKey // NewV4AsymmetricSecretKeyFromSeed creates a secret key from a seed (hex) func NewV4AsymmetricSecretKeyFromSeed(hexEncoded string) (V4AsymmetricSecretKey, error) { - seedBytes, err := hex.DecodeString(hexEncoded) - - if err != nil { + var seedBytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&seedBytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV4AsymmetricSecretKey(), err @@ -166,7 +161,7 @@ func NewV4SymmetricKey() V4SymmetricKey { // ExportHex exports the key as hex for storage func (k V4SymmetricKey) ExportHex() string { - return hex.EncodeToString(k.ExportBytes()) + return encoding.HexEncode(k.ExportBytes()) } // ExportBytes exports the key as raw byte array @@ -176,9 +171,8 @@ func (k V4SymmetricKey) ExportBytes() []byte { // V4SymmetricKeyFromHex constructs a key from hex func V4SymmetricKeyFromHex(hexEncoded string) (V4SymmetricKey, error) { - bytes, err := hex.DecodeString(hexEncoded) - - if err != nil { + var bytes []byte + if err := encoding.HexDecode(hexEncoded).Ok(&bytes); err != nil { // even though we return error, return a random key here rather than // a nil key return NewV4SymmetricKey(), err @@ -196,7 +190,6 @@ func V4SymmetricKeyFromBytes(bytes []byte) (V4SymmetricKey, error) { } var material [32]byte - copy(material[:], bytes) return V4SymmetricKey{material}, nil