diff --git a/v1/ast/syncpools.go b/v1/ast/syncpools.go index 82977c836b..acd2a0db11 100644 --- a/v1/ast/syncpools.go +++ b/v1/ast/syncpools.go @@ -1,32 +1,63 @@ package ast import ( + "bytes" "strings" "sync" ) -type termPtrPool struct { - pool sync.Pool +var ( + TermPtrPool = NewSyncPool[Term]() + BytesReaderPool = NewSyncPool[bytes.Reader]() + IndexResultPool = NewSyncPool[IndexResult]() + // Needs custom pool because of custom Put logic. + sbPool = &stringBuilderPool{ + pool: sync.Pool{ + New: func() any { + return &strings.Builder{} + }, + }, + } + // Needs custom pool because of custom Put logic. + varVisitorPool = &vvPool{ + pool: sync.Pool{ + New: func() any { + return NewVarVisitor() + }, + }, + } +) + +type ( + syncPool[T any] struct { + pool sync.Pool + } + stringBuilderPool struct { + pool sync.Pool + } + vvPool struct { + pool sync.Pool + } +) + +func NewSyncPool[T any]() *syncPool[T] { + return &syncPool[T]{ + pool: sync.Pool{ + New: func() any { + return new(T) + }, + }, + } } -type stringBuilderPool struct { - pool sync.Pool +func (p *syncPool[T]) Get() *T { + return p.pool.Get().(*T) } -type indexResultPool struct { - pool sync.Pool -} - -type vvPool struct { - pool sync.Pool -} - -func (p *termPtrPool) Get() *Term { - return p.pool.Get().(*Term) -} - -func (p *termPtrPool) Put(t *Term) { - p.pool.Put(t) +func (p *syncPool[T]) Put(x *T) { + if x != nil { + p.pool.Put(x) + } } func (p *stringBuilderPool) Get() *strings.Builder { @@ -38,16 +69,6 @@ func (p *stringBuilderPool) Put(sb *strings.Builder) { p.pool.Put(sb) } -func (p *indexResultPool) Get() *IndexResult { - return p.pool.Get().(*IndexResult) -} - -func (p *indexResultPool) Put(x *IndexResult) { - if x != nil { - p.pool.Put(x) - } -} - func (p *vvPool) Get() *VarVisitor { return p.pool.Get().(*VarVisitor) } @@ -58,35 +79,3 @@ func (p *vvPool) Put(vv *VarVisitor) { p.pool.Put(vv) } } - -var TermPtrPool = &termPtrPool{ - pool: sync.Pool{ - New: func() any { - return &Term{} - }, - }, -} - -var sbPool = &stringBuilderPool{ - pool: sync.Pool{ - New: func() any { - return &strings.Builder{} - }, - }, -} - -var varVisitorPool = &vvPool{ - pool: sync.Pool{ - New: func() any { - return NewVarVisitor() - }, - }, -} - -var IndexResultPool = &indexResultPool{ - pool: sync.Pool{ - New: func() any { - return &IndexResult{} - }, - }, -} diff --git a/v1/topdown/builtins/builtins.go b/v1/topdown/builtins/builtins.go index 7a1bdede6b..98d80b5f9b 100644 --- a/v1/topdown/builtins/builtins.go +++ b/v1/topdown/builtins/builtins.go @@ -209,8 +209,7 @@ func SetOperand(x ast.Value, pos int) (ast.Set, error) { return s, nil } -// StringOperand converts x to a string. If the cast fails, a descriptive error is -// returned. +// StringOperand returns x as [ast.String], or a descriptive error if the conversion fails. func StringOperand(x ast.Value, pos int) (ast.String, error) { s, ok := x.(ast.String) if !ok { @@ -219,6 +218,17 @@ func StringOperand(x ast.Value, pos int) (ast.String, error) { return s, nil } +// StringOperandByteSlice returns x a []byte, assuming x is [ast.String], or a descriptive error +// if that is not the case. The returned byte slice points directly at the underlying array backing +// the string, and should not be modified. +func StringOperandByteSlice(x ast.Value, pos int) ([]byte, error) { + s, err := StringOperand(x, pos) + if err != nil { + return nil, err + } + return util.StringToByteSlice(string(s)), nil +} + // ObjectOperand converts x to an object. If the cast fails, a descriptive // error is returned. func ObjectOperand(x ast.Value, pos int) (ast.Object, error) { diff --git a/v1/topdown/crypto.go b/v1/topdown/crypto.go index 144c01ee95..f4ca23fae5 100644 --- a/v1/topdown/crypto.go +++ b/v1/topdown/crypto.go @@ -255,17 +255,17 @@ func extractVerifyOpts(options ast.Object) (verifyOpt x509.VerifyOptions, err er } func builtinCryptoX509ParseKeyPair(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - certificate, err := builtins.StringOperand(operands[0].Value, 1) + certificate, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - key, err := builtins.StringOperand(operands[1].Value, 1) + key, err := builtins.StringOperandByteSlice(operands[1].Value, 1) if err != nil { return err } - certs, err := getTLSx509KeyPairFromString([]byte(certificate), []byte(key)) + certs, err := getTLSx509KeyPairFromString(certificate, key) if err != nil { return err } @@ -326,10 +326,7 @@ func builtinCryptoX509ParseCertificateRequest(_ BuiltinContext, operands []*ast. } func builtinCryptoJWKFromPrivateKey(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - var x any - - a := operands[0].Value - input, err := builtins.StringOperand(a, 1) + input, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { return err } @@ -371,6 +368,7 @@ func builtinCryptoJWKFromPrivateKey(_ BuiltinContext, operands []*ast.Term, iter return err } + var x any if err := util.UnmarshalJSON(jsonKey, &x); err != nil { return err } @@ -430,53 +428,51 @@ func toHexEncodedString(src []byte) string { } func builtinCryptoMd5(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - s, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - md5sum := md5.Sum([]byte(s)) + md5sum := md5.Sum(bs) return iter(ast.StringTerm(toHexEncodedString(md5sum[:]))) } func builtinCryptoSha1(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - s, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - sha1sum := sha1.Sum([]byte(s)) + sha1sum := sha1.Sum(bs) return iter(ast.StringTerm(toHexEncodedString(sha1sum[:]))) } func builtinCryptoSha256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - s, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - sha256sum := sha256.Sum256([]byte(s)) + sha256sum := sha256.Sum256(bs) return iter(ast.StringTerm(toHexEncodedString(sha256sum[:]))) } func hmacHelper(operands []*ast.Term, iter func(*ast.Term) error, h func() hash.Hash) error { - a1 := operands[0].Value - message, err := builtins.StringOperand(a1, 1) + message, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - a2 := operands[1].Value - key, err := builtins.StringOperand(a2, 2) + key, err := builtins.StringOperandByteSlice(operands[1].Value, 2) if err != nil { return err } - mac := hmac.New(h, []byte(key)) - mac.Write([]byte(message)) + mac := hmac.New(h, key) + mac.Write(message) messageDigest := mac.Sum(nil) return iter(ast.StringTerm(hex.EncodeToString(messageDigest))) @@ -499,21 +495,17 @@ func builtinCryptoHmacSha512(_ BuiltinContext, operands []*ast.Term, iter func(* } func builtinCryptoHmacEqual(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - a1 := operands[0].Value - mac1, err := builtins.StringOperand(a1, 1) + mac1, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - a2 := operands[1].Value - mac2, err := builtins.StringOperand(a2, 2) + mac2, err := builtins.StringOperandByteSlice(operands[1].Value, 2) if err != nil { return err } - res := hmac.Equal([]byte(mac1), []byte(mac2)) - - return iter(ast.InternedTerm(res)) + return iter(ast.InternedTerm(hmac.Equal(mac1, mac2))) } func init() { @@ -668,7 +660,7 @@ func addCACertsFromFile(pool *x509.CertPool, filePath string) (*x509.CertPool, e pool = x509.NewCertPool() } - caCert, err := readCertFromFile(filePath) + caCert, err := os.ReadFile(filePath) if err != nil { return nil, err } @@ -703,17 +695,7 @@ func addCACertsFromEnv(pool *x509.CertPool, envName string) (*x509.CertPool, err return nil, fmt.Errorf("could not add CA certificates from envvar %q: %w", envName, err) } - return pool, err -} - -// ReadCertFromFile reads a cert from file -func readCertFromFile(localCertFile string) ([]byte, error) { - // Read in the cert file - certPEM, err := os.ReadFile(localCertFile) - if err != nil { - return nil, err - } - return certPEM, nil + return pool, nil } var beginPrefix = []byte("-----BEGIN ") @@ -771,13 +753,3 @@ func getTLSx509KeyPairFromString(certPemBlock []byte, keyPemBlock []byte) (*tls. return &cert, nil } - -// ReadKeyFromFile reads a key from file -func readKeyFromFile(localKeyFile string) ([]byte, error) { - // Read in the cert file - key, err := os.ReadFile(localKeyFile) - if err != nil { - return nil, err - } - return key, nil -} diff --git a/v1/topdown/encoding.go b/v1/topdown/encoding.go index 541b50d0a9..1d11d39981 100644 --- a/v1/topdown/encoding.go +++ b/v1/topdown/encoding.go @@ -5,7 +5,6 @@ package topdown import ( - "bytes" "encoding/base64" "encoding/hex" "encoding/json" @@ -21,7 +20,6 @@ import ( ) func builtinJSONMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - asJSON, err := ast.JSON(operands[0].Value) if err != nil { return err @@ -32,11 +30,10 @@ func builtinJSONMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.T return err } - return iter(ast.StringTerm(string(bs))) + return iter(ast.StringTerm(util.ByteSliceToString(bs))) } func builtinJSONMarshalWithOpts(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - asJSON, err := ast.JSON(operands[0].Value) if err != nil { return err @@ -101,36 +98,34 @@ func builtinJSONMarshalWithOpts(_ BuiltinContext, operands []*ast.Term, iter fun } var bs []byte - if shouldPrettyPrint { bs, err = json.MarshalIndent(asJSON, prefixWith, indentWith) } else { bs, err = json.Marshal(asJSON) } - if err != nil { return err } + s := util.ByteSliceToString(bs) + if shouldPrettyPrint { // json.MarshalIndent() function will not prefix the first line of emitted JSON - return iter(ast.StringTerm(prefixWith + string(bs))) + return iter(ast.StringTerm(prefixWith + s)) } - return iter(ast.StringTerm(string(bs))) + return iter(ast.StringTerm(s)) } func builtinJSONUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } var x any - - if err := util.UnmarshalJSON([]byte(str), &x); err != nil { + if err := util.UnmarshalJSON(bs, &x); err != nil { return err } v, err := ast.InterfaceToValue(x) @@ -141,22 +136,21 @@ func builtinJSONUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast } func builtinJSONIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return iter(ast.InternedTerm(false)) } - return iter(ast.InternedTerm(json.Valid([]byte(str)))) + return iter(ast.InternedTerm(json.Valid(bs))) } func builtinBase64Encode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - return iter(ast.StringTerm(base64.StdEncoding.EncodeToString([]byte(str)))) + return iter(ast.StringTerm(base64.StdEncoding.EncodeToString(bs))) } func builtinBase64Decode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { @@ -183,20 +177,20 @@ func builtinBase64IsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast } func builtinBase64UrlEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - return iter(ast.StringTerm(base64.URLEncoding.EncodeToString([]byte(str)))) + return iter(ast.StringTerm(base64.URLEncoding.EncodeToString(bs))) } func builtinBase64UrlEncodeNoPad(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - return iter(ast.StringTerm(base64.RawURLEncoding.EncodeToString([]byte(str)))) + return iter(ast.StringTerm(base64.RawURLEncoding.EncodeToString(bs))) } func builtinBase64UrlDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { @@ -306,45 +300,39 @@ func builtinURLQueryDecodeObject(_ BuiltinContext, operands []*ast.Term, iter fu } func builtinYAMLMarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - asJSON, err := ast.JSON(operands[0].Value) if err != nil { return err } - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - if err := encoder.Encode(asJSON); err != nil { - return err - } - - bs, err := yaml.JSONToYAML(buf.Bytes()) + bs, err := yaml.Marshal(asJSON) if err != nil { return err } - return iter(ast.StringTerm(string(bs))) + return iter(ast.StringTerm(util.ByteSliceToString(bs))) } func builtinYAMLUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - bs, err := yaml.YAMLToJSON([]byte(str)) + js, err := yaml.YAMLToJSON(bs) if err != nil { return err } - buf := bytes.NewBuffer(bs) - decoder := util.NewJSONDecoder(buf) + reader := ast.BytesReaderPool.Get() + defer ast.BytesReaderPool.Put(reader) + reader.Reset(js) + var val any - err = decoder.Decode(&val) - if err != nil { + if err = util.NewJSONDecoder(reader).Decode(&val); err != nil { return err } + v, err := ast.InterfaceToValue(val) if err != nil { return err @@ -353,22 +341,22 @@ func builtinYAMLUnmarshal(_ BuiltinContext, operands []*ast.Term, iter func(*ast } func builtinYAMLIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return iter(ast.InternedTerm(false)) } var x any - err = yaml.Unmarshal([]byte(str), &x) + err = yaml.Unmarshal(bs, &x) return iter(ast.InternedTerm(err == nil)) } func builtinHexEncode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) + bs, err := builtins.StringOperandByteSlice(operands[0].Value, 1) if err != nil { return err } - return iter(ast.StringTerm(hex.EncodeToString([]byte(str)))) + return iter(ast.StringTerm(hex.EncodeToString(bs))) } func builtinHexDecode(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { diff --git a/v1/topdown/encoding_bench_test.go b/v1/topdown/encoding_bench_test.go new file mode 100644 index 0000000000..edd5fe48ae --- /dev/null +++ b/v1/topdown/encoding_bench_test.go @@ -0,0 +1,49 @@ +package topdown + +import ( + "testing" + + "github.com/open-policy-agent/opa/v1/ast" +) + +var ( + unmarshalled = ast.ObjectTerm( + ast.Item(ast.InternedTerm("foo"), ast.ObjectTerm( + ast.Item(ast.InternedTerm("bar"), ast.ArrayTerm(ast.InternedTerm("baz"), ast.InternedTerm("qux"))), + ast.Item(ast.InternedTerm("num"), ast.InternedTerm(42)), + )), + ) + marshalled = `foo: + bar: + - baz + - qux + num: 42 +` +) + +// 7447 ns/op 22984 B/op 142 allocs/op +// 7343 ns/op 22872 B/op 140 allocs/op +func BenchmarkYAMLMarshal(b *testing.B) { + expect := ast.InternedTerm(marshalled) + operands := []*ast.Term{unmarshalled} + iter := eqIter(expect) + + for b.Loop() { + if err := builtinYAMLMarshal(BuiltinContext{}, operands, iter); err != nil { + b.Fatal(err) + } + } +} + +// 5393 ns/op 11066 B/op 146 allocs/op +// 5210 ns/op 10980 B/op 144 allocs/op +func BenchmarkYAMLUnmarshal(b *testing.B) { + operands := []*ast.Term{ast.InternedTerm(marshalled)} + iter := eqIter(unmarshalled) + + for b.Loop() { + if err := builtinYAMLUnmarshal(BuiltinContext{}, operands, iter); err != nil { + b.Fatal(err) + } + } +} diff --git a/v1/topdown/http_test.go b/v1/topdown/http_test.go index 004e8e7ee3..058489bed3 100644 --- a/v1/topdown/http_test.go +++ b/v1/topdown/http_test.go @@ -2440,13 +2440,13 @@ func TestHTTPSClient(t *testing.T) { } // Set up Environment - clientCert, err := readCertFromFile(localClientCertFile) + clientCert, err := os.ReadFile(localClientCertFile) if err != nil { t.Fatal(err) } t.Setenv("CLIENT_CERT_ENV", string(clientCert)) - clientKey, err := readKeyFromFile(localClientKeyFile) + clientKey, err := os.ReadFile(localClientKeyFile) if err != nil { t.Fatal(err) } diff --git a/v1/topdown/regex.go b/v1/topdown/regex.go index 1d2906ee2e..385117cebc 100644 --- a/v1/topdown/regex.go +++ b/v1/topdown/regex.go @@ -7,6 +7,7 @@ package topdown import ( "fmt" "regexp" + "regexp/syntax" "sync" gintersect "github.com/yashtewari/glob-intersection" @@ -22,18 +23,13 @@ var regexpCacheLock = sync.Mutex{} var regexpCache map[string]*regexp.Regexp func builtinRegexIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - - s, err := builtins.StringOperand(operands[0].Value, 1) - if err != nil { - return iter(ast.InternedTerm(false)) + if s, err := builtins.StringOperand(operands[0].Value, 1); err == nil { + if _, err = syntax.Parse(string(s), syntax.Perl); err == nil { + return iter(ast.InternedTerm(true)) + } } - _, err = regexp.Compile(string(s)) - if err != nil { - return iter(ast.InternedTerm(false)) - } - - return iter(ast.InternedTerm(true)) + return iter(ast.InternedTerm(false)) } func builtinRegexMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { diff --git a/v1/topdown/tokens.go b/v1/topdown/tokens.go index aea15dd26a..8e9b6779d4 100644 --- a/v1/topdown/tokens.go +++ b/v1/topdown/tokens.go @@ -428,7 +428,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash // If a match is found, verify using only that key. Only applicable when a JWKS was provided. if header.kid != "" { if key := getKeyByKid(header.kid, keys); key != nil { - err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature) return done(err == nil) } @@ -440,7 +440,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash if key.alg == "" { // No algorithm provided for the key - this is likely a certificate and not a JWKS, so // we'll need to verify to find out - err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature) if err == nil { return done(true) } @@ -448,7 +448,7 @@ func builtinJWTVerify(bctx BuiltinContext, jwt ast.Value, keyStr ast.Value, hash if header.alg != key.alg { continue } - err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), []byte(signature)) + err = verify(key.key, getInputSHA([]byte(token.header+"."+token.payload), hasher), signature) if err == nil { return done(true) } @@ -509,7 +509,7 @@ func builtinJWTVerifyHS(bctx BuiltinContext, operands []*ast.Term, hashF func() return err } - valid := hmac.Equal([]byte(signature), mac.Sum(nil)) + valid := hmac.Equal(signature, mac.Sum(nil)) putTokenInCache(bctx, jwt, astSecret, nil, nil, valid) @@ -662,7 +662,7 @@ func (constraints *tokenConstraints) validate() error { } // verify verifies a JWT using the constraints and the algorithm from the header -func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature string) error { +func (constraints *tokenConstraints) verify(kid, alg, header, payload string, signature []byte) error { // Construct the payload plaintext := append(append([]byte(header), '.'), []byte(payload)...) @@ -670,7 +670,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature if constraints.keys != nil { if kid != "" { if key := getKeyByKid(kid, constraints.keys); key != nil { - err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature)) + err := jwsbb.Verify(key.key, alg, plaintext, signature) if err != nil { return errSignatureNotVerified } @@ -681,7 +681,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature verified := false for _, key := range constraints.keys { if key.alg == "" { - err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature)) + err := jwsbb.Verify(key.key, alg, plaintext, signature) if err == nil { verified = true break @@ -690,7 +690,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature if alg != key.alg { continue } - err := jwsbb.Verify(key.key, alg, plaintext, []byte(signature)) + err := jwsbb.Verify(key.key, alg, plaintext, signature) if err == nil { verified = true break @@ -704,7 +704,7 @@ func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature return nil } if constraints.secret != "" { - err := jwsbb.Verify([]byte(constraints.secret), alg, plaintext, []byte(signature)) + err := jwsbb.Verify([]byte(constraints.secret), alg, plaintext, signature) if err != nil { return errSignatureNotVerified } @@ -1170,17 +1170,17 @@ func decodeJWT(a ast.Value) (*JSONWebToken, error) { return &JSONWebToken{header: parts[0], payload: parts[1], signature: parts[2]}, nil } -func (token *JSONWebToken) decodeSignature() (string, error) { +func (token *JSONWebToken) decodeSignature() ([]byte, error) { decodedSignature, err := getResult(builtinBase64UrlDecode, ast.StringTerm(token.signature)) if err != nil { - return "", err + return nil, err } - signatureAst, err := builtins.StringOperand(decodedSignature.Value, 1) + signatureBs, err := builtins.StringOperandByteSlice(decodedSignature.Value, 1) if err != nil { - return "", err + return nil, err } - return string(signatureAst), err + return signatureBs, nil } // Extract, validate and return the JWT header as an ast.Object.