build(deps): bump github.com/open-policy-agent/opa from 1.8.0 to 1.9.0

Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2025-09-26 14:17:55 +00:00
committed by Ralf Haferkamp
parent 703b8dd084
commit d1ebbde760
67 changed files with 7144 additions and 480 deletions

View File

@@ -0,0 +1,32 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
go.work.sum
# env file
.env
# Editor/IDE
# .idea/
# .vscode/

5
vendor/github.com/lestrrat-go/dsig-secp256k1/Changes generated vendored Normal file
View File

@@ -0,0 +1,5 @@
Changes
=======
v1.0.0 18 Aug 2025
* Initial release

21
vendor/github.com/lestrrat-go/dsig-secp256k1/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 lestrrat-go
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,29 @@
package dsigsecp256k1
import (
"crypto"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lestrrat-go/dsig"
)
const ECDSAWithSecp256k1AndSHA256 = "ECDSA_WITH_SECP256K1_AND_SHA256"
// init adds secp256k1 support when the dsig_secp256k1 build tag is used.
func init() {
// Register ES256K (secp256k1 + SHA256) support using the new API
err := dsig.RegisterAlgorithm(ECDSAWithSecp256k1AndSHA256, dsig.AlgorithmInfo{
Family: dsig.ECDSA,
Meta: dsig.ECDSAFamilyMeta{
Hash: crypto.SHA256,
},
})
if err != nil {
panic("failed to register secp256k1 algorithm: " + err.Error())
}
}
// secp256k1Curve returns the secp256k1 curve.
func Curve() *secp256k1.KoblitzCurve {
return secp256k1.S256()
}

32
vendor/github.com/lestrrat-go/dsig/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,32 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
go.work.sum
# env file
.env
# Editor/IDE
# .idea/
# .vscode/

5
vendor/github.com/lestrrat-go/dsig/Changes generated vendored Normal file
View File

@@ -0,0 +1,5 @@
Changes
=======
v1.0.0 - 18 Aug 2025
* Initial release

21
vendor/github.com/lestrrat-go/dsig/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 lestrrat-go
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

163
vendor/github.com/lestrrat-go/dsig/README.md generated vendored Normal file
View File

@@ -0,0 +1,163 @@
# github.com/lestrrat-go/dsig [![CI](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml/badge.svg)](https://github.com/lestrrat-go/dsig/actions/workflows/ci.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/dsig.svg)](https://pkg.go.dev/github.com/lestrrat-go/dsig) [![codecov.io](https://codecov.io/github/lestrrat-go/dsig/coverage.svg?branch=v1)](https://codecov.io/github/lestrrat-go/dsig?branch=v1)
Go module providing low-level digital signature operations.
While there are many standards for generating and verifying digital signatures, the core operations are virtually the same. This module implements the core functionality of digital signature generation / verifications in a framework agnostic way.
# Features
* RSA signatures (PKCS1v15 and PSS)
* ECDSA signatures (P-256, P-384, P-521)
* EdDSA signatures (Ed25519, Ed448)
* HMAC signatures (SHA-256, SHA-384, SHA-512)
* Support for crypto.Signer interface
* Allows for dynamic additions of algorithms in limited cases.
# SYNOPSIS
<!-- INCLUDE(examples/dsig_readme_example_test.go) -->
```go
package examples_test
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"fmt"
"github.com/lestrrat-go/dsig"
)
func Example() {
payload := []byte("hello world")
// RSA signing and verification
{
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Printf("failed to generate RSA key: %s\n", err)
return
}
// Sign with RSA-PSS SHA256
signature, err := dsig.Sign(privKey, dsig.RSAPSSWithSHA256, payload, nil)
if err != nil {
fmt.Printf("failed to sign with RSA: %s\n", err)
return
}
// Verify with RSA-PSS SHA256
err = dsig.Verify(&privKey.PublicKey, dsig.RSAPSSWithSHA256, payload, signature)
if err != nil {
fmt.Printf("failed to verify RSA signature: %s\n", err)
return
}
}
// ECDSA signing and verification
{
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
fmt.Printf("failed to generate ECDSA key: %s\n", err)
return
}
// Sign with ECDSA P-256 SHA256
signature, err := dsig.Sign(privKey, dsig.ECDSAWithP256AndSHA256, payload, nil)
if err != nil {
fmt.Printf("failed to sign with ECDSA: %s\n", err)
return
}
// Verify with ECDSA P-256 SHA256
err = dsig.Verify(&privKey.PublicKey, dsig.ECDSAWithP256AndSHA256, payload, signature)
if err != nil {
fmt.Printf("failed to verify ECDSA signature: %s\n", err)
return
}
}
// EdDSA signing and verification
{
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
fmt.Printf("failed to generate Ed25519 key: %s\n", err)
return
}
// Sign with EdDSA
signature, err := dsig.Sign(privKey, dsig.EdDSA, payload, nil)
if err != nil {
fmt.Printf("failed to sign with EdDSA: %s\n", err)
return
}
// Verify with EdDSA
err = dsig.Verify(pubKey, dsig.EdDSA, payload, signature)
if err != nil {
fmt.Printf("failed to verify EdDSA signature: %s\n", err)
return
}
}
// HMAC signing and verification
{
key := []byte("secret-key")
// Sign with HMAC SHA256
signature, err := dsig.Sign(key, dsig.HMACWithSHA256, payload, nil)
if err != nil {
fmt.Printf("failed to sign with HMAC: %s\n", err)
return
}
// Verify with HMAC SHA256
err = dsig.Verify(key, dsig.HMACWithSHA256, payload, signature)
if err != nil {
fmt.Printf("failed to verify HMAC signature: %s\n", err)
return
}
}
// OUTPUT:
}
```
source: [examples/dsig_readme_example_test.go](https://github.com/lestrrat-go/dsig/blob/v1/examples/dsig_readme_example_test.go)
<!-- END INCLUDE -->
# Supported Algorithms
| Constant | Algorithm | Key Type |
|----------|-----------|----------|
| `HMACWithSHA256` | HMAC using SHA-256 | []byte |
| `HMACWithSHA384` | HMAC using SHA-384 | []byte |
| `HMACWithSHA512` | HMAC using SHA-512 | []byte |
| `RSAPKCS1v15WithSHA256` | RSA PKCS#1 v1.5 using SHA-256 | *rsa.PrivateKey / *rsa.PublicKey |
| `RSAPKCS1v15WithSHA384` | RSA PKCS#1 v1.5 using SHA-384 | *rsa.PrivateKey / *rsa.PublicKey |
| `RSAPKCS1v15WithSHA512` | RSA PKCS#1 v1.5 using SHA-512 | *rsa.PrivateKey / *rsa.PublicKey |
| `RSAPSSWithSHA256` | RSA PSS using SHA-256 | *rsa.PrivateKey / *rsa.PublicKey |
| `RSAPSSWithSHA384` | RSA PSS using SHA-384 | *rsa.PrivateKey / *rsa.PublicKey |
| `RSAPSSWithSHA512` | RSA PSS using SHA-512 | *rsa.PrivateKey / *rsa.PublicKey |
| `ECDSAWithP256AndSHA256` | ECDSA using P-256 and SHA-256 | *ecdsa.PrivateKey / *ecdsa.PublicKey |
| `ECDSAWithP384AndSHA384` | ECDSA using P-384 and SHA-384 | *ecdsa.PrivateKey / *ecdsa.PublicKey |
| `ECDSAWithP521AndSHA512` | ECDSA using P-521 and SHA-512 | *ecdsa.PrivateKey / *ecdsa.PublicKey |
| `EdDSA` | EdDSA using Ed25519 or Ed448 | ed25519.PrivateKey / ed25519.PublicKey |
# Description
This library provides low-level digital signature operations. It does minimal parameter validation for performance, uses strongly typed APIs, and has minimal dependencies.
# Contributions
## Issues
For bug reports and feature requests, please include failing tests when possible.
## Pull Requests
Please include tests that exercise your changes.
# Related Libraries
* [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) - JOSE (JWA/JWE/JWK/JWS/JWT) implementation

37
vendor/github.com/lestrrat-go/dsig/algorithms.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
package dsig
// This file defines verbose algorithm name constants that can be mapped to by
// different standards (RFC7518, FIDO, etc.) for interoperability.
//
// The algorithm names are intentionally verbose to avoid any ambiguity about
// the exact cryptographic operations being performed.
const (
// HMAC signature algorithms
// These use Hash-based Message Authentication Code with specified hash functions
HMACWithSHA256 = "HMAC_WITH_SHA256"
HMACWithSHA384 = "HMAC_WITH_SHA384"
HMACWithSHA512 = "HMAC_WITH_SHA512"
// RSA signature algorithms with PKCS#1 v1.5 padding
// These use RSA signatures with PKCS#1 v1.5 padding and specified hash functions
RSAPKCS1v15WithSHA256 = "RSA_PKCS1v15_WITH_SHA256"
RSAPKCS1v15WithSHA384 = "RSA_PKCS1v15_WITH_SHA384"
RSAPKCS1v15WithSHA512 = "RSA_PKCS1v15_WITH_SHA512"
// RSA signature algorithms with PSS padding
// These use RSA signatures with Probabilistic Signature Scheme (PSS) padding
RSAPSSWithSHA256 = "RSA_PSS_WITH_SHA256"
RSAPSSWithSHA384 = "RSA_PSS_WITH_SHA384"
RSAPSSWithSHA512 = "RSA_PSS_WITH_SHA512"
// ECDSA signature algorithms
// These use Elliptic Curve Digital Signature Algorithm with specified curves and hash functions
ECDSAWithP256AndSHA256 = "ECDSA_WITH_P256_AND_SHA256"
ECDSAWithP384AndSHA384 = "ECDSA_WITH_P384_AND_SHA384"
ECDSAWithP521AndSHA512 = "ECDSA_WITH_P521_AND_SHA512"
// EdDSA signature algorithms
// These use Edwards-curve Digital Signature Algorithm (supports Ed25519 and Ed448)
EdDSA = "EDDSA"
)

45
vendor/github.com/lestrrat-go/dsig/crypto_signer.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package dsig
import (
"crypto"
"crypto/rand"
"fmt"
"io"
)
// cryptosign is a low-level function that signs a payload using a crypto.Signer.
// If hash is crypto.Hash(0), the payload is signed directly without hashing.
// Otherwise, the payload is hashed using the specified hash function before signing.
//
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
func cryptosign(signer crypto.Signer, payload []byte, hash crypto.Hash, opts crypto.SignerOpts, rr io.Reader) ([]byte, error) {
if rr == nil {
rr = rand.Reader
}
var digest []byte
if hash == crypto.Hash(0) {
digest = payload
} else {
h := hash.New()
if _, err := h.Write(payload); err != nil {
return nil, fmt.Errorf(`failed to write payload to hash: %w`, err)
}
digest = h.Sum(nil)
}
return signer.Sign(rr, digest, opts)
}
// SignCryptoSigner generates a signature using a crypto.Signer interface.
// This function can be used for hardware security modules, smart cards,
// and other implementations of the crypto.Signer interface.
//
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
//
// Returns the signature bytes or an error if signing fails.
func SignCryptoSigner(signer crypto.Signer, raw []byte, h crypto.Hash, opts crypto.SignerOpts, rr io.Reader) ([]byte, error) {
if signer == nil {
return nil, fmt.Errorf("dsig.SignCryptoSigner: signer is nil")
}
return cryptosign(signer, raw, h, opts, rr)
}

224
vendor/github.com/lestrrat-go/dsig/dsig.go generated vendored Normal file
View File

@@ -0,0 +1,224 @@
// Package dsig provides digital signature operations for Go.
// It contains low-level signature generation and verification tools that
// can be used by other signing libraries
//
// The package follows these design principles:
// 1. Does minimal checking of input parameters (for performance); callers need to ensure that the parameters are valid.
// 2. All exported functions are strongly typed (i.e. they do not take `any` types unless they absolutely have to).
// 3. Does not rely on other high-level packages (standalone, except for internal packages).
package dsig
import (
"crypto"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"sync"
)
// Family represents the cryptographic algorithm family
type Family int
const (
InvalidFamily Family = iota
HMAC
RSA
ECDSA
EdDSAFamily
maxFamily
)
// String returns the string representation of the Family
func (f Family) String() string {
switch f {
case HMAC:
return "HMAC"
case RSA:
return "RSA"
case ECDSA:
return "ECDSA"
case EdDSAFamily:
return "EdDSA"
default:
return "InvalidFamily"
}
}
// AlgorithmInfo contains metadata about a digital signature algorithm
type AlgorithmInfo struct {
Family Family // The cryptographic family (HMAC, RSA, ECDSA, EdDSA)
Meta any // Family-specific metadata
}
// HMACFamilyMeta contains metadata specific to HMAC algorithms
type HMACFamilyMeta struct {
HashFunc func() hash.Hash // Hash function constructor
}
// RSAFamilyMeta contains metadata specific to RSA algorithms
type RSAFamilyMeta struct {
Hash crypto.Hash // Hash algorithm
PSS bool // Whether to use PSS padding (false = PKCS#1 v1.5)
}
// ECDSAFamilyMeta contains metadata specific to ECDSA algorithms
type ECDSAFamilyMeta struct {
Hash crypto.Hash // Hash algorithm
}
// EdDSAFamilyMeta contains metadata specific to EdDSA algorithms
// Currently EdDSA doesn't need specific metadata, but this provides extensibility
type EdDSAFamilyMeta struct {
// Reserved for future use
}
var algorithms = make(map[string]AlgorithmInfo)
var muAlgorithms sync.RWMutex
// RegisterAlgorithm registers a new digital signature algorithm with the specified family and metadata.
//
// info.Meta should contain extra metadata for some algorithms. Currently HMAC, RSA,
// and ECDSA family of algorithms need their respective metadata (HMACFamilyMeta,
// RSAFamilyMeta, and ECDSAFamilyMeta). Metadata for other families are ignored.
func RegisterAlgorithm(name string, info AlgorithmInfo) error {
muAlgorithms.Lock()
defer muAlgorithms.Unlock()
// Validate the metadata matches the family
switch info.Family {
case HMAC:
if _, ok := info.Meta.(HMACFamilyMeta); !ok {
return fmt.Errorf("invalid HMAC metadata for algorithm %s", name)
}
case RSA:
if _, ok := info.Meta.(RSAFamilyMeta); !ok {
return fmt.Errorf("invalid RSA metadata for algorithm %s", name)
}
case ECDSA:
if _, ok := info.Meta.(ECDSAFamilyMeta); !ok {
return fmt.Errorf("invalid ECDSA metadata for algorithm %s", name)
}
case EdDSAFamily:
// EdDSA metadata is optional for now
default:
return fmt.Errorf("unsupported algorithm family %s for algorithm %s", info.Family, name)
}
algorithms[name] = info
return nil
}
// GetAlgorithmInfo retrieves the algorithm information for a given algorithm name.
// Returns the info and true if found, zero value and false if not found.
func GetAlgorithmInfo(name string) (AlgorithmInfo, bool) {
muAlgorithms.RLock()
defer muAlgorithms.RUnlock()
info, ok := algorithms[name]
return info, ok
}
func init() {
// Register all standard algorithms with their metadata
toRegister := map[string]AlgorithmInfo{
// HMAC algorithms
HMACWithSHA256: {
Family: HMAC,
Meta: HMACFamilyMeta{
HashFunc: sha256.New,
},
},
HMACWithSHA384: {
Family: HMAC,
Meta: HMACFamilyMeta{
HashFunc: sha512.New384,
},
},
HMACWithSHA512: {
Family: HMAC,
Meta: HMACFamilyMeta{
HashFunc: sha512.New,
},
},
// RSA PKCS#1 v1.5 algorithms
RSAPKCS1v15WithSHA256: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA256,
PSS: false,
},
},
RSAPKCS1v15WithSHA384: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA384,
PSS: false,
},
},
RSAPKCS1v15WithSHA512: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA512,
PSS: false,
},
},
// RSA PSS algorithms
RSAPSSWithSHA256: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA256,
PSS: true,
},
},
RSAPSSWithSHA384: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA384,
PSS: true,
},
},
RSAPSSWithSHA512: {
Family: RSA,
Meta: RSAFamilyMeta{
Hash: crypto.SHA512,
PSS: true,
},
},
// ECDSA algorithms
ECDSAWithP256AndSHA256: {
Family: ECDSA,
Meta: ECDSAFamilyMeta{
Hash: crypto.SHA256,
},
},
ECDSAWithP384AndSHA384: {
Family: ECDSA,
Meta: ECDSAFamilyMeta{
Hash: crypto.SHA384,
},
},
ECDSAWithP521AndSHA512: {
Family: ECDSA,
Meta: ECDSAFamilyMeta{
Hash: crypto.SHA512,
},
},
// EdDSA algorithm
EdDSA: {
Family: EdDSAFamily,
Meta: EdDSAFamilyMeta{},
},
}
for name, info := range toRegister {
if err := RegisterAlgorithm(name, info); err != nil {
panic(fmt.Sprintf("failed to register algorithm %s: %v", name, err))
}
}
}

200
vendor/github.com/lestrrat-go/dsig/ecdsa.go generated vendored Normal file
View File

@@ -0,0 +1,200 @@
package dsig
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"encoding/asn1"
"fmt"
"io"
"math/big"
"github.com/lestrrat-go/dsig/internal/ecutil"
)
func ecdsaGetSignerKey(key any) (*ecdsa.PrivateKey, crypto.Signer, bool, error) {
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
if !isValidECDSAKey(key) {
return nil, nil, false, fmt.Errorf(`invalid key type %T for ECDSA algorithm`, key)
}
switch key.(type) {
case ecdsa.PrivateKey, *ecdsa.PrivateKey:
// if it's ecdsa.PrivateKey, it's more efficient to
// go through the non-crypto.Signer route. Set isCryptoSigner to false
isCryptoSigner = false
}
}
if isCryptoSigner {
return nil, cs, true, nil
}
privkey, ok := key.(*ecdsa.PrivateKey)
if !ok {
return nil, nil, false, fmt.Errorf(`invalid key type %T. *ecdsa.PrivateKey is required`, key)
}
return privkey, nil, false, nil
}
// UnpackASN1ECDSASignature unpacks an ASN.1 encoded ECDSA signature into r and s values.
// This is typically used when working with crypto.Signer interfaces that return ASN.1 encoded signatures.
func UnpackASN1ECDSASignature(signed []byte, r, s *big.Int) error {
// Okay, this is silly, but hear me out. When we use the
// crypto.Signer interface, the PrivateKey is hidden.
// But we need some information about the key (its bit size).
//
// So while silly, we're going to have to make another call
// here and fetch the Public key.
// (This probably means that this information should be cached somewhere)
var p struct {
R *big.Int // TODO: get this from a pool?
S *big.Int
}
if _, err := asn1.Unmarshal(signed, &p); err != nil {
return fmt.Errorf(`failed to unmarshal ASN1 encoded signature: %w`, err)
}
r.Set(p.R)
s.Set(p.S)
return nil
}
// UnpackECDSASignature unpacks a JWS-format ECDSA signature into r and s values.
// The signature should be in the format specified by RFC 7515 (r||s as fixed-length byte arrays).
func UnpackECDSASignature(signature []byte, pubkey *ecdsa.PublicKey, r, s *big.Int) error {
keySize := ecutil.CalculateKeySize(pubkey.Curve)
if len(signature) != keySize*2 {
return fmt.Errorf(`invalid signature length for curve %q`, pubkey.Curve.Params().Name)
}
r.SetBytes(signature[:keySize])
s.SetBytes(signature[keySize:])
return nil
}
// PackECDSASignature packs the r and s values from an ECDSA signature into a JWS-format byte slice.
// The output format follows RFC 7515: r||s as fixed-length byte arrays.
func PackECDSASignature(r *big.Int, sbig *big.Int, curveBits int) ([]byte, error) {
keyBytes := curveBits / 8
if curveBits%8 > 0 {
keyBytes++
}
// Serialize r and s into fixed-length bytes
rBytes := r.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := sbig.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
// Output as r||s
return append(rBytesPadded, sBytesPadded...), nil
}
// SignECDSA generates an ECDSA signature for the given payload using the specified private key and hash.
// The raw parameter should be the pre-computed signing input (typically header.payload).
//
// rr is an io.Reader that provides randomness for signing. if rr is nil, it defaults to rand.Reader.
func SignECDSA(key *ecdsa.PrivateKey, payload []byte, h crypto.Hash, rr io.Reader) ([]byte, error) {
if !isValidECDSAKey(key) {
return nil, fmt.Errorf(`invalid key type %T for ECDSA algorithm`, key)
}
hh := h.New()
if _, err := hh.Write(payload); err != nil {
return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err)
}
digest := hh.Sum(nil)
if rr == nil {
rr = rand.Reader
}
// Sign and get r, s values
r, s, err := ecdsa.Sign(rr, key, digest)
if err != nil {
return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err)
}
return PackECDSASignature(r, s, key.Curve.Params().BitSize)
}
// SignECDSACryptoSigner generates an ECDSA signature using a crypto.Signer interface.
// This function works with hardware security modules and other crypto.Signer implementations.
// The signature is converted from ASN.1 format to JWS format (r||s).
//
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
func SignECDSACryptoSigner(signer crypto.Signer, raw []byte, h crypto.Hash, rr io.Reader) ([]byte, error) {
signed, err := SignCryptoSigner(signer, raw, h, h, rr)
if err != nil {
return nil, fmt.Errorf(`failed to sign payload using crypto.Signer: %w`, err)
}
return signECDSACryptoSigner(signer, signed)
}
func signECDSACryptoSigner(signer crypto.Signer, signed []byte) ([]byte, error) {
cpub := signer.Public()
pubkey, ok := cpub.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf(`expected *ecdsa.PublicKey, got %T`, pubkey)
}
curveBits := pubkey.Curve.Params().BitSize
var r, s big.Int
if err := UnpackASN1ECDSASignature(signed, &r, &s); err != nil {
return nil, fmt.Errorf(`failed to unpack ASN1 encoded signature: %w`, err)
}
return PackECDSASignature(&r, &s, curveBits)
}
func ecdsaVerify(key *ecdsa.PublicKey, buf []byte, h crypto.Hash, r, s *big.Int) error {
hasher := h.New()
hasher.Write(buf)
digest := hasher.Sum(nil)
if !ecdsa.Verify(key, digest, r, s) {
return NewVerificationError("invalid ECDSA signature")
}
return nil
}
// VerifyECDSA verifies an ECDSA signature for the given payload.
// This function verifies the signature using the specified public key and hash algorithm.
// The payload parameter should be the pre-computed signing input (typically header.payload).
func VerifyECDSA(key *ecdsa.PublicKey, payload, signature []byte, h crypto.Hash) error {
var r, s big.Int
if err := UnpackECDSASignature(signature, key, &r, &s); err != nil {
return fmt.Errorf("dsig.VerifyECDSA: failed to unpack ECDSA signature: %w", err)
}
return ecdsaVerify(key, payload, h, &r, &s)
}
// VerifyECDSACryptoSigner verifies an ECDSA signature for crypto.Signer implementations.
// This function is useful for verifying signatures created by hardware security modules
// or other implementations of the crypto.Signer interface.
// The payload parameter should be the pre-computed signing input (typically header.payload).
func VerifyECDSACryptoSigner(signer crypto.Signer, payload, signature []byte, h crypto.Hash) error {
var pubkey *ecdsa.PublicKey
switch cpub := signer.Public(); cpub := cpub.(type) {
case ecdsa.PublicKey:
pubkey = &cpub
case *ecdsa.PublicKey:
pubkey = cpub
default:
return fmt.Errorf(`dsig.VerifyECDSACryptoSigner: expected *ecdsa.PublicKey, got %T`, cpub)
}
var r, s big.Int
if err := UnpackECDSASignature(signature, pubkey, &r, &s); err != nil {
return fmt.Errorf("dsig.VerifyECDSACryptoSigner: failed to unpack ASN.1 encoded ECDSA signature: %w", err)
}
return ecdsaVerify(pubkey, payload, h, &r, &s)
}

44
vendor/github.com/lestrrat-go/dsig/eddsa.go generated vendored Normal file
View File

@@ -0,0 +1,44 @@
package dsig
import (
"crypto"
"crypto/ed25519"
"fmt"
)
func eddsaGetSigner(key any) (crypto.Signer, error) {
// The ed25519.PrivateKey object implements crypto.Signer, so we should
// simply accept a crypto.Signer here.
signer, ok := key.(crypto.Signer)
if ok {
if !isValidEDDSAKey(key) {
return nil, fmt.Errorf(`invalid key type %T for EdDSA algorithm`, key)
}
return signer, nil
}
// This fallback exists for cases when users give us a pointer instead of non-pointer, etc.
privkey, ok := key.(ed25519.PrivateKey)
if !ok {
return nil, fmt.Errorf(`failed to retrieve ed25519.PrivateKey out of %T`, key)
}
return privkey, nil
}
// SignEdDSA generates an EdDSA (Ed25519) signature for the given payload.
// The raw parameter should be the pre-computed signing input (typically header.payload).
// EdDSA is deterministic and doesn't require additional hashing of the input.
func SignEdDSA(key ed25519.PrivateKey, payload []byte) ([]byte, error) {
return ed25519.Sign(key, payload), nil
}
// VerifyEdDSA verifies an EdDSA (Ed25519) signature for the given payload.
// This function verifies the signature using Ed25519 verification algorithm.
// The payload parameter should be the pre-computed signing input (typically header.payload).
// EdDSA is deterministic and provides strong security guarantees without requiring hash function selection.
func VerifyEdDSA(key ed25519.PublicKey, payload, signature []byte) error {
if !ed25519.Verify(key, payload, signature) {
return fmt.Errorf("invalid EdDSA signature")
}
return nil
}

45
vendor/github.com/lestrrat-go/dsig/hmac.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package dsig
import (
"crypto/hmac"
"fmt"
"hash"
)
func toHMACKey(dst *[]byte, key any) error {
keyBytes, ok := key.([]byte)
if !ok {
return fmt.Errorf(`dsig.toHMACKey: invalid key type %T. []byte is required`, key)
}
if len(keyBytes) == 0 {
return fmt.Errorf(`dsig.toHMACKey: missing key while signing payload`)
}
*dst = keyBytes
return nil
}
// SignHMAC generates an HMAC signature for the given payload using the specified hash function and key.
// The raw parameter should be the pre-computed signing input (typically header.payload).
func SignHMAC(key, payload []byte, hfunc func() hash.Hash) ([]byte, error) {
h := hmac.New(hfunc, key)
if _, err := h.Write(payload); err != nil {
return nil, fmt.Errorf(`failed to write payload using hmac: %w`, err)
}
return h.Sum(nil), nil
}
// VerifyHMAC verifies an HMAC signature for the given payload.
// This function verifies the signature using the specified key and hash function.
// The payload parameter should be the pre-computed signing input (typically header.payload).
func VerifyHMAC(key, payload, signature []byte, hfunc func() hash.Hash) error {
expected, err := SignHMAC(key, payload, hfunc)
if err != nil {
return fmt.Errorf("failed to sign payload for verification: %w", err)
}
if !hmac.Equal(signature, expected) {
return NewVerificationError("invalid HMAC signature")
}
return nil
}

View File

@@ -0,0 +1,76 @@
// Package ecutil defines tools that help with elliptic curve related
// computation
package ecutil
import (
"crypto/elliptic"
"math/big"
"sync"
)
const (
// size of buffer that needs to be allocated for EC521 curve
ec521BufferSize = 66 // (521 / 8) + 1
)
var ecpointBufferPool = sync.Pool{
New: func() any {
// In most cases the curve bit size will be less than this length
// so allocate the maximum, and keep reusing
buf := make([]byte, 0, ec521BufferSize)
return &buf
},
}
func getCrvFixedBuffer(size int) []byte {
//nolint:forcetypeassert
buf := *(ecpointBufferPool.Get().(*[]byte))
if size > ec521BufferSize && cap(buf) < size {
buf = append(buf, make([]byte, size-cap(buf))...)
}
return buf[:size]
}
// ReleaseECPointBuffer releases the []byte buffer allocated.
func ReleaseECPointBuffer(buf []byte) {
buf = buf[:cap(buf)]
buf[0] = 0x0
for i := 1; i < len(buf); i *= 2 {
copy(buf[i:], buf[:i])
}
buf = buf[:0]
ecpointBufferPool.Put(&buf)
}
func CalculateKeySize(crv elliptic.Curve) int {
// We need to create a buffer that fits the entire curve.
// If the curve size is 66, that fits in 9 bytes. If the curve
// size is 64, it fits in 8 bytes.
bits := crv.Params().BitSize
// For most common cases we know before hand what the byte length
// is going to be. optimize
var inBytes int
switch bits {
case 224, 256, 384: // TODO: use constant?
inBytes = bits / 8
case 521:
inBytes = ec521BufferSize
default:
inBytes = bits / 8
if (bits % 8) != 0 {
inBytes++
}
}
return inBytes
}
// AllocECPointBuffer allocates a buffer for the given point in the given
// curve. This buffer should be released using the ReleaseECPointBuffer
// function.
func AllocECPointBuffer(v *big.Int, crv elliptic.Curve) []byte {
buf := getCrvFixedBuffer(CalculateKeySize(crv))
v.FillBytes(buf)
return buf
}

63
vendor/github.com/lestrrat-go/dsig/rsa.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
package dsig
import (
"crypto"
"crypto/rsa"
"fmt"
"io"
)
func rsaGetSignerCryptoSignerKey(key any) (crypto.Signer, bool, error) {
if !isValidRSAKey(key) {
return nil, false, fmt.Errorf(`invalid key type %T for RSA algorithm`, key)
}
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
return cs, true, nil
}
return nil, false, nil
}
// rsaPSSOptions returns the PSS options for RSA-PSS signatures with the specified hash.
// The salt length is set to equal the hash length as per RFC 7518.
func rsaPSSOptions(h crypto.Hash) rsa.PSSOptions {
return rsa.PSSOptions{
Hash: h,
SaltLength: rsa.PSSSaltLengthEqualsHash,
}
}
// SignRSA generates an RSA signature for the given payload using the specified private key and options.
// The raw parameter should be the pre-computed signing input (typically header.payload).
// If pss is true, RSA-PSS is used; otherwise, PKCS#1 v1.5 is used.
//
// The rr parameter is an optional io.Reader that can be used to provide randomness for signing.
// If rr is nil, it defaults to rand.Reader.
func SignRSA(key *rsa.PrivateKey, payload []byte, h crypto.Hash, pss bool, rr io.Reader) ([]byte, error) {
if !isValidRSAKey(key) {
return nil, fmt.Errorf(`invalid key type %T for RSA algorithm`, key)
}
var opts crypto.SignerOpts = h
if pss {
rsaopts := rsaPSSOptions(h)
opts = &rsaopts
}
return cryptosign(key, payload, h, opts, rr)
}
// VerifyRSA verifies an RSA signature for the given payload and header.
// This function constructs the signing input by encoding the header and payload according to JWS specification,
// then verifies the signature using the specified public key and hash algorithm.
// If pss is true, RSA-PSS verification is used; otherwise, PKCS#1 v1.5 verification is used.
func VerifyRSA(key *rsa.PublicKey, payload, signature []byte, h crypto.Hash, pss bool) error {
if !isValidRSAKey(key) {
return fmt.Errorf(`invalid key type %T for RSA algorithm`, key)
}
hasher := h.New()
hasher.Write(payload)
digest := hasher.Sum(nil)
if pss {
return rsa.VerifyPSS(key, h, digest, signature, &rsa.PSSOptions{Hash: h, SaltLength: rsa.PSSSaltLengthEqualsHash})
}
return rsa.VerifyPKCS1v15(key, h, digest, signature)
}

100
vendor/github.com/lestrrat-go/dsig/sign.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
package dsig
import (
"crypto"
"crypto/rsa"
"fmt"
"io"
)
// Sign generates a digital signature using the specified key and algorithm.
//
// This function loads the signer registered in the dsig package _ONLY_.
// It does not support custom signers that the user might have registered.
//
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
// Not all algorithms require this parameter, but it is included for consistency.
// 99% of the time, you can pass nil for rr, and it will work fine.
func Sign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) {
info, ok := GetAlgorithmInfo(alg)
if !ok {
return nil, fmt.Errorf(`dsig.Sign: unsupported signature algorithm %q`, alg)
}
switch info.Family {
case HMAC:
return dispatchHMACSign(key, info, payload)
case RSA:
return dispatchRSASign(key, info, payload, rr)
case ECDSA:
return dispatchECDSASign(key, info, payload, rr)
case EdDSAFamily:
return dispatchEdDSASign(key, info, payload, rr)
default:
return nil, fmt.Errorf(`dsig.Sign: unsupported signature family %q`, info.Family)
}
}
func dispatchHMACSign(key any, info AlgorithmInfo, payload []byte) ([]byte, error) {
meta, ok := info.Meta.(HMACFamilyMeta)
if !ok {
return nil, fmt.Errorf(`dsig.Sign: invalid HMAC metadata`)
}
var hmackey []byte
if err := toHMACKey(&hmackey, key); err != nil {
return nil, fmt.Errorf(`dsig.Sign: %w`, err)
}
return SignHMAC(hmackey, payload, meta.HashFunc)
}
func dispatchRSASign(key any, info AlgorithmInfo, payload []byte, rr io.Reader) ([]byte, error) {
meta, ok := info.Meta.(RSAFamilyMeta)
if !ok {
return nil, fmt.Errorf(`dsig.Sign: invalid RSA metadata`)
}
cs, isCryptoSigner, err := rsaGetSignerCryptoSignerKey(key)
if err != nil {
return nil, fmt.Errorf(`dsig.Sign: %w`, err)
}
if isCryptoSigner {
var options crypto.SignerOpts = meta.Hash
if meta.PSS {
rsaopts := rsaPSSOptions(meta.Hash)
options = &rsaopts
}
return SignCryptoSigner(cs, payload, meta.Hash, options, rr)
}
privkey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf(`dsig.Sign: invalid key type %T. *rsa.PrivateKey is required`, key)
}
return SignRSA(privkey, payload, meta.Hash, meta.PSS, rr)
}
func dispatchEdDSASign(key any, _ AlgorithmInfo, payload []byte, rr io.Reader) ([]byte, error) {
signer, err := eddsaGetSigner(key)
if err != nil {
return nil, fmt.Errorf(`dsig.Sign: %w`, err)
}
return SignCryptoSigner(signer, payload, crypto.Hash(0), crypto.Hash(0), rr)
}
func dispatchECDSASign(key any, info AlgorithmInfo, payload []byte, rr io.Reader) ([]byte, error) {
meta, ok := info.Meta.(ECDSAFamilyMeta)
if !ok {
return nil, fmt.Errorf(`dsig.Sign: invalid ECDSA metadata`)
}
privkey, cs, isCryptoSigner, err := ecdsaGetSignerKey(key)
if err != nil {
return nil, fmt.Errorf(`dsig.Sign: %w`, err)
}
if isCryptoSigner {
return SignECDSACryptoSigner(cs, payload, meta.Hash, rr)
}
return SignECDSA(privkey, payload, meta.Hash, rr)
}

66
vendor/github.com/lestrrat-go/dsig/validation.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package dsig
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
)
// isValidRSAKey validates that the provided key type is appropriate for RSA algorithms.
// It returns false if the key is clearly incompatible (e.g., ECDSA or EdDSA keys).
func isValidRSAKey(key any) bool {
switch key.(type) {
case
ecdsa.PrivateKey, *ecdsa.PrivateKey,
ed25519.PrivateKey:
// these are NOT ok for RSA algorithms
return false
}
return true
}
// isValidECDSAKey validates that the provided key type is appropriate for ECDSA algorithms.
// It returns false if the key is clearly incompatible (e.g., RSA or EdDSA keys).
func isValidECDSAKey(key any) bool {
switch key.(type) {
case
ed25519.PrivateKey,
rsa.PrivateKey, *rsa.PrivateKey:
// these are NOT ok for ECDSA algorithms
return false
}
return true
}
// isValidEDDSAKey validates that the provided key type is appropriate for EdDSA algorithms.
// It returns false if the key is clearly incompatible (e.g., RSA or ECDSA keys).
func isValidEDDSAKey(key any) bool {
switch key.(type) {
case
ecdsa.PrivateKey, *ecdsa.PrivateKey,
rsa.PrivateKey, *rsa.PrivateKey:
// these are NOT ok for EdDSA algorithms
return false
}
return true
}
// VerificationError represents an error that occurred during signature verification.
type VerificationError struct {
message string
}
func (e *VerificationError) Error() string {
return e.message
}
// NewVerificationError creates a new verification error with the given message.
func NewVerificationError(message string) error {
return &VerificationError{message: message}
}
// IsVerificationError checks if the given error is a verification error.
func IsVerificationError(err error) bool {
_, ok := err.(*VerificationError)
return ok
}

134
vendor/github.com/lestrrat-go/dsig/verify.go generated vendored Normal file
View File

@@ -0,0 +1,134 @@
package dsig
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"fmt"
)
// Verify verifies a digital signature using the specified key and algorithm.
//
// This function loads the verifier registered in the dsig package _ONLY_.
// It does not support custom verifiers that the user might have registered.
func Verify(key any, alg string, payload, signature []byte) error {
info, ok := GetAlgorithmInfo(alg)
if !ok {
return fmt.Errorf(`dsig.Verify: unsupported signature algorithm %q`, alg)
}
switch info.Family {
case HMAC:
return dispatchHMACVerify(key, info, payload, signature)
case RSA:
return dispatchRSAVerify(key, info, payload, signature)
case ECDSA:
return dispatchECDSAVerify(key, info, payload, signature)
case EdDSAFamily:
return dispatchEdDSAVerify(key, info, payload, signature)
default:
return fmt.Errorf(`dsig.Verify: unsupported signature family %q`, info.Family)
}
}
func dispatchHMACVerify(key any, info AlgorithmInfo, payload, signature []byte) error {
meta, ok := info.Meta.(HMACFamilyMeta)
if !ok {
return fmt.Errorf(`dsig.Verify: invalid HMAC metadata`)
}
var hmackey []byte
if err := toHMACKey(&hmackey, key); err != nil {
return fmt.Errorf(`dsig.Verify: %w`, err)
}
return VerifyHMAC(hmackey, payload, signature, meta.HashFunc)
}
func dispatchRSAVerify(key any, info AlgorithmInfo, payload, signature []byte) error {
meta, ok := info.Meta.(RSAFamilyMeta)
if !ok {
return fmt.Errorf(`dsig.Verify: invalid RSA metadata`)
}
var pubkey *rsa.PublicKey
if cs, ok := key.(crypto.Signer); ok {
cpub := cs.Public()
switch cpub := cpub.(type) {
case rsa.PublicKey:
pubkey = &cpub
case *rsa.PublicKey:
pubkey = cpub
default:
return fmt.Errorf(`dsig.Verify: failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key)
}
} else {
var ok bool
pubkey, ok = key.(*rsa.PublicKey)
if !ok {
return fmt.Errorf(`dsig.Verify: failed to retrieve *rsa.PublicKey out of %T`, key)
}
}
return VerifyRSA(pubkey, payload, signature, meta.Hash, meta.PSS)
}
func dispatchECDSAVerify(key any, info AlgorithmInfo, payload, signature []byte) error {
meta, ok := info.Meta.(ECDSAFamilyMeta)
if !ok {
return fmt.Errorf(`dsig.Verify: invalid ECDSA metadata`)
}
pubkey, cs, isCryptoSigner, err := ecdsaGetVerifierKey(key)
if err != nil {
return fmt.Errorf(`dsig.Verify: %w`, err)
}
if isCryptoSigner {
return VerifyECDSACryptoSigner(cs, payload, signature, meta.Hash)
}
return VerifyECDSA(pubkey, payload, signature, meta.Hash)
}
func dispatchEdDSAVerify(key any, _ AlgorithmInfo, payload, signature []byte) error {
var pubkey ed25519.PublicKey
signer, ok := key.(crypto.Signer)
if ok {
v := signer.Public()
pubkey, ok = v.(ed25519.PublicKey)
if !ok {
return fmt.Errorf(`dsig.Verify: expected crypto.Signer.Public() to return ed25519.PublicKey, but got %T`, v)
}
} else {
var ok bool
pubkey, ok = key.(ed25519.PublicKey)
if !ok {
return fmt.Errorf(`dsig.Verify: failed to retrieve ed25519.PublicKey out of %T`, key)
}
}
return VerifyEdDSA(pubkey, payload, signature)
}
func ecdsaGetVerifierKey(key any) (*ecdsa.PublicKey, crypto.Signer, bool, error) {
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
switch key.(type) {
case ecdsa.PublicKey, *ecdsa.PublicKey:
// if it's ecdsa.PublicKey, it's more efficient to
// go through the non-crypto.Signer route. Set isCryptoSigner to false
isCryptoSigner = false
}
}
if isCryptoSigner {
return nil, cs, true, nil
}
pubkey, ok := key.(*ecdsa.PublicKey)
if !ok {
return nil, nil, false, fmt.Errorf(`invalid key type %T. *ecdsa.PublicKey is required`, key)
}
return pubkey, nil, false, nil
}

View File

@@ -29,6 +29,7 @@ linters:
- nakedret
- nestif
- nlreturn
- noinlineerr
- nonamedreturns
- paralleltest
- tagliatelle
@@ -37,6 +38,7 @@ linters:
- varnamelen
- wrapcheck
- wsl
- wsl_v5
settings:
govet:
disable:

View File

@@ -1,7 +1,10 @@
Changes
=======
v3.0.0 UNRELEASED
v3.0.1 18 Aug 2025
* Refresh() no longer requires the resource to be ready.
v3.0.0 5 Jun 2025
[Breaking Changes]
* The entire API has been re-imagined for Go versions that allow typed parameters

View File

@@ -71,6 +71,7 @@ func (c *ctrlBackend) refreshResource(ctx context.Context, req refreshRequest) {
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: [refresh] START %q", req.u))
defer c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: [refresh] END %q", req.u))
u := req.u
r, ok := c.items[u]
if !ok {
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: [refresh] %s is not registered", req.u))
@@ -78,12 +79,9 @@ func (c *ctrlBackend) refreshResource(ctx context.Context, req refreshRequest) {
return
}
// Make sure it's ready
if err := r.Ready(ctx); err != nil {
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: [refresh] %s did not become ready: %v", req.u, err))
sendReply(ctx, req.reply, struct{}{}, err)
return
}
// Note: We don't wait for r.Ready() here because refresh should work
// regardless of whether the resource has been fetched before. This allows
// refresh to work with resources registered using WithWaitReady(false).
r.SetNext(time.Unix(0, 0))
sendWorkerSynchronous(ctx, c.syncoutgoing, synchronousRequest{

View File

@@ -39,10 +39,12 @@ func (w worker) Run(ctx context.Context, readywg *sync.WaitGroup, donewg *sync.W
case sr := <-w.nextsync:
w.traceSink.Put(ctx, fmt.Sprintf("httprc worker: syncing %q (synchronous)", sr.resource.URL()))
if err := sr.resource.Sync(ctx); err != nil {
w.traceSink.Put(ctx, fmt.Sprintf("httprc worker: FAILED to sync %q (synchronous): %s", sr.resource.URL(), err))
sendReply(ctx, sr.reply, struct{}{}, err)
sr.resource.SetBusy(false)
return
}
w.traceSink.Put(ctx, fmt.Sprintf("httprc worker: SUCCESS syncing %q (synchronous)", sr.resource.URL()))
sr.resource.SetBusy(false)
sendReply(ctx, sr.reply, struct{}{}, nil)
w.sendAdjustIntervalRequest(ctx, sr.resource)

View File

@@ -4,6 +4,20 @@ Changes
v3 has many incompatibilities with v2. To see the full list of differences between
v2 and v3, please read the Changes-v3.md file (https://github.com/lestrrat-go/jwx/blob/develop/v3/Changes-v3.md)
v3.0.11 14 Sep 2025
* [jwk] Add `(jwk.Cache).Shutdown()` method that delegates to the httprc controller
object, to shutdown the cache.
* [jwk] Change timing of `res.Body.Close()` call
* [jwe] Previously, ecdh.PrivateKey/ecdh.PublicKey were not properly handled
when used for encryption, which has been fixed.
* [jws/jwsbb] (EXPERIMENTAL/BREAKS COMPATIBILITY) Convert most functions into
thin wrappers around functions from github.com/lestrrat-go/dsig package.
As a related change, HAMCHashFuncFor/RSAHashFuncFor/ECDSAHashFuncFor/RSAPSSOptions
have been removed or unexported.
Users of this module should be using jwsbb.Sign() and jwsbb.Verify() instead of
algorithm specific jwsbb.SignRSA()/jwsbb.VerifyRSA() and such. If you feel the
need to use these functions, you should use github.com/lestrrat-go/dsig directly.
v3.0.10 04 Aug 2025
* [jws/jwsbb] Add `jwsbb.ErrHeaderNotFound()` to return the same error type as when
a non-existent header is requested. via `HeaderGetXXX()` functions. Previously, this

View File

@@ -9,9 +9,9 @@ bazel_dep(name = "rules_go", version = "0.55.1")
bazel_dep(name = "gazelle", version = "0.44.0")
bazel_dep(name = "aspect_bazel_lib", version = "2.11.0")
# Go SDK setup - using Go 1.23.6 to match the toolchain in go.mod
# Go SDK setup - using Go 1.24.4 to match the toolchain in go.mod
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.23.6")
go_sdk.download(version = "1.24.4")
# Go dependencies from go.mod
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
@@ -23,6 +23,8 @@ use_repo(
"com_github_decred_dcrd_dcrec_secp256k1_v4",
"com_github_goccy_go_json",
"com_github_lestrrat_go_blackmagic",
"com_github_lestrrat_go_dsig",
"com_github_lestrrat_go_dsig_secp256k1",
"com_github_lestrrat_go_httprc_v3",
"com_github_lestrrat_go_option_v2",
"com_github_segmentio_asm",

View File

@@ -5,8 +5,10 @@ import (
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"fmt"
"math/big"
"github.com/lestrrat-go/blackmagic"
"github.com/lestrrat-go/jwx/v3/jwk"
@@ -263,3 +265,90 @@ func ECDHPublicKey(dst, src any) error {
return blackmagic.AssignIfCompatible(dst, pubECDH)
}
// ecdhCurveToElliptic maps ECDH curves to elliptic curves
func ecdhCurveToElliptic(ecdhCurve ecdh.Curve) (elliptic.Curve, error) {
switch ecdhCurve {
case ecdh.P256():
return elliptic.P256(), nil
case ecdh.P384():
return elliptic.P384(), nil
case ecdh.P521():
return elliptic.P521(), nil
default:
return nil, fmt.Errorf(`keyconv: unsupported ECDH curve: %v`, ecdhCurve)
}
}
// ecdhPublicKeyToECDSA converts an ECDH public key to an ECDSA public key
func ecdhPublicKeyToECDSA(ecdhPubKey *ecdh.PublicKey) (*ecdsa.PublicKey, error) {
curve, err := ecdhCurveToElliptic(ecdhPubKey.Curve())
if err != nil {
return nil, err
}
pubBytes := ecdhPubKey.Bytes()
// Parse the uncompressed point format (0x04 prefix + X + Y coordinates)
if len(pubBytes) == 0 || pubBytes[0] != 0x04 {
return nil, fmt.Errorf(`keyconv: invalid ECDH public key format`)
}
keyLen := (len(pubBytes) - 1) / 2
if len(pubBytes) != 1+2*keyLen {
return nil, fmt.Errorf(`keyconv: invalid ECDH public key length`)
}
x := new(big.Int).SetBytes(pubBytes[1 : 1+keyLen])
y := new(big.Int).SetBytes(pubBytes[1+keyLen:])
return &ecdsa.PublicKey{
Curve: curve,
X: x,
Y: y,
}, nil
}
func ECDHToECDSA(dst, src any) error {
// convert ecdh.PublicKey to ecdsa.PublicKey, ecdh.PrivateKey to ecdsa.PrivateKey
// First, handle value types by converting to pointers
switch s := src.(type) {
case ecdh.PrivateKey:
src = &s
case ecdh.PublicKey:
src = &s
}
var privBytes []byte
var pubkey *ecdh.PublicKey
// Now handle the actual conversion with pointer types
switch src := src.(type) {
case *ecdh.PrivateKey:
pubkey = src.PublicKey()
privBytes = src.Bytes()
case *ecdh.PublicKey:
pubkey = src
default:
return fmt.Errorf(`keyconv: expected ecdh.PrivateKey, *ecdh.PrivateKey, ecdh.PublicKey, or *ecdh.PublicKey, got %T`, src)
}
// convert the public key
ecdsaPubKey, err := ecdhPublicKeyToECDSA(pubkey)
if err != nil {
return fmt.Errorf(`keyconv.ECDHToECDSA: failed to convert ECDH public key to ECDSA public key: %w`, err)
}
// return if we were being asked to convert *ecdh.PublicKey
if privBytes == nil {
return blackmagic.AssignIfCompatible(dst, ecdsaPubKey)
}
// Then create the private key with the public key embedded
ecdsaPrivKey := &ecdsa.PrivateKey{
D: new(big.Int).SetBytes(privBytes),
PublicKey: *ecdsaPubKey,
}
return blackmagic.AssignIfCompatible(dst, ecdsaPrivKey)
}

View File

@@ -96,23 +96,48 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
keyToUse = e.pubkey
}
// Handle ecdsa.PublicKey by value - convert to pointer
if pk, ok := keyToUse.(ecdsa.PublicKey); ok {
keyToUse = &pk
switch key := keyToUse.(type) {
case *ecdsa.PublicKey:
// no op
case ecdsa.PublicKey:
keyToUse = &key
case *ecdsa.PrivateKey:
keyToUse = &key.PublicKey
case ecdsa.PrivateKey:
keyToUse = &key.PublicKey
case *ecdh.PublicKey:
// no op
case ecdh.PublicKey:
keyToUse = &key
case ecdh.PrivateKey:
keyToUse = key.PublicKey()
case *ecdh.PrivateKey:
keyToUse = key.PublicKey()
}
// Determine key type and call appropriate function
switch key := keyToUse.(type) {
case *ecdh.PublicKey:
if key.Curve() == ecdh.X25519() {
if !keywrap {
return jwebb.KeyEncryptECDHESX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
}
return jwebb.KeyEncryptECDHESKeyWrapX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
}
var ecdsaKey *ecdsa.PublicKey
if err := keyconv.ECDHToECDSA(&ecdsaKey, key); err != nil {
return nil, fmt.Errorf(`encrypt: failed to convert ECDH public key to ECDSA: %w`, err)
}
keyToUse = ecdsaKey
}
switch key := keyToUse.(type) {
case *ecdsa.PublicKey:
if !keywrap {
return jwebb.KeyEncryptECDHESECDSA(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
}
return jwebb.KeyEncryptECDHESKeyWrapECDSA(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
case *ecdh.PublicKey:
if !keywrap {
return jwebb.KeyEncryptECDHESX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
}
return jwebb.KeyEncryptECDHESKeyWrapX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
default:
return nil, fmt.Errorf(`encrypt: unsupported key type for ECDH-ES: %T`, keyToUse)
}

View File

@@ -223,6 +223,10 @@ func (c *Cache) Unregister(ctx context.Context, u string) error {
return c.ctrl.Remove(ctx, u)
}
func (c *Cache) Shutdown(ctx context.Context) error {
return c.ctrl.ShutdownContext(ctx)
}
// CachedSet is a thin shim over jwk.Cache that allows the user to cloak
// jwk.Cache as if it's a `jwk.Set`. Behind the scenes, the `jwk.Set` is
// retrieved from the `jwk.Cache` for every operation.

View File

@@ -102,13 +102,13 @@ func Fetch(ctx context.Context, u string, options ...FetchOption) (Set, error) {
if err != nil {
return nil, fmt.Errorf(`jwk.Fetch: request failed: %w`, err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf(`jwk.Fetch: request returned status %d, expected 200`, res.StatusCode)
}
buf, err := io.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
return nil, fmt.Errorf(`jwk.Fetch: failed to read response body for %q: %w`, u, err)
}

View File

@@ -8,5 +8,6 @@ import (
)
func init() {
// Register ES256K to EC algorithm family
addAlgorithmForKeyType(jwa.EC(), jwa.ES256K())
}

View File

@@ -19,6 +19,10 @@
// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer
// and verify the result using `algorithm` and `key`. Upon successful
// verification, the original payload is returned, so you can work on it.
//
// As a sidenote, consider using github.com/lestrrat-go/htmsig if you
// looking for HTTP Message Signatures (RFC9421) -- it uses the same
// underlying signing/verification mechanisms as this module.
package jws
import (

View File

@@ -23,6 +23,7 @@ go_library(
"//internal/pool",
"//internal/tokens",
"//jws/internal/keytype",
"@com_github_lestrrat_go_dsig//:dsig",
],
)

View File

@@ -3,59 +3,27 @@ package jwsbb
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"encoding/asn1"
"fmt"
"io"
"math/big"
"github.com/lestrrat-go/dsig"
"github.com/lestrrat-go/jwx/v3/internal/ecutil"
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
)
var ecdsaHashFuncs = map[string]crypto.Hash{
"ES256": crypto.SHA256,
"ES256K": crypto.SHA256,
"ES384": crypto.SHA384,
"ES512": crypto.SHA512,
}
func isSuppotedECDSAAlgorithm(alg string) bool {
_, ok := ecdsaHashFuncs[alg]
return ok
}
func ECDSAHashFuncFor(alg string) (crypto.Hash, error) {
if h, ok := ecdsaHashFuncs[alg]; ok {
return h, nil
// ecdsaHashToDsigAlgorithm maps ECDSA hash functions to dsig algorithm constants
func ecdsaHashToDsigAlgorithm(h crypto.Hash) (string, error) {
switch h {
case crypto.SHA256:
return dsig.ECDSAWithP256AndSHA256, nil
case crypto.SHA384:
return dsig.ECDSAWithP384AndSHA384, nil
case crypto.SHA512:
return dsig.ECDSAWithP521AndSHA512, nil
default:
return "", fmt.Errorf("unsupported ECDSA hash function: %v", h)
}
return 0, fmt.Errorf(`unsupported ECDSA algorithm %s`, alg)
}
func ecdsaGetSignerKey(key any) (*ecdsa.PrivateKey, crypto.Signer, bool, error) {
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
if !keytype.IsValidECDSAKey(key) {
return nil, nil, false, fmt.Errorf(`cannot use key of type %T`, key)
}
switch key.(type) {
case ecdsa.PrivateKey, *ecdsa.PrivateKey:
// if it's ecdsa.PrivateKey, it's more efficient to
// go through the non-crypto.Signer route. Set isCryptoSigner to false
isCryptoSigner = false
}
}
if isCryptoSigner {
return nil, cs, true, nil
}
var privkey *ecdsa.PrivateKey
if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
return nil, nil, false, fmt.Errorf(`invalid key type %T. ecdsa.PrivateKey is required: %w`, key, err)
}
return privkey, nil, false, nil
}
// UnpackASN1ECDSASignature unpacks an ASN.1 encoded ECDSA signature into r and s values.
@@ -120,24 +88,16 @@ func PackECDSASignature(r *big.Int, sbig *big.Int, curveBits int) ([]byte, error
// The raw parameter should be the pre-computed signing input (typically header.payload).
//
// rr is an io.Reader that provides randomness for signing. if rr is nil, it defaults to rand.Reader.
//
// This function is now a thin wrapper around dsig.SignECDSA. For new projects, you should
// consider using dsig instead of this function.
func SignECDSA(key *ecdsa.PrivateKey, payload []byte, h crypto.Hash, rr io.Reader) ([]byte, error) {
hh := h.New()
if _, err := hh.Write(payload); err != nil {
return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err)
}
digest := hh.Sum(nil)
if rr == nil {
rr = rand.Reader
}
// Sign and get r, s values
r, s, err := ecdsa.Sign(rr, key, digest)
dsigAlg, err := ecdsaHashToDsigAlgorithm(h)
if err != nil {
return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err)
return nil, fmt.Errorf("jwsbb.SignECDSA: %w", err)
}
return PackECDSASignature(r, s, key.Curve.Params().BitSize)
return dsig.Sign(key, dsigAlg, payload, rr)
}
// SignECDSACryptoSigner generates an ECDSA signature using a crypto.Signer interface.
@@ -183,13 +143,16 @@ func ecdsaVerify(key *ecdsa.PublicKey, buf []byte, h crypto.Hash, r, s *big.Int)
// VerifyECDSA verifies an ECDSA signature for the given payload.
// This function verifies the signature using the specified public key and hash algorithm.
// The payload parameter should be the pre-computed signing input (typically header.payload).
//
// This function is now a thin wrapper around dsig.VerifyECDSA. For new projects, you should
// consider using dsig instead of this function.
func VerifyECDSA(key *ecdsa.PublicKey, payload, signature []byte, h crypto.Hash) error {
var r, s big.Int
if err := UnpackECDSASignature(signature, key, &r, &s); err != nil {
return fmt.Errorf("jwsbb.ECDSAVerifier: failed to unpack ECDSA signature: %w", err)
dsigAlg, err := ecdsaHashToDsigAlgorithm(h)
if err != nil {
return fmt.Errorf("jwsbb.VerifyECDSA: %w", err)
}
return ecdsaVerify(key, payload, h, &r, &s)
return dsig.Verify(key, dsigAlg, payload, signature)
}
// VerifyECDSACryptoSigner verifies an ECDSA signature for crypto.Signer implementations.

View File

@@ -1,52 +1,30 @@
package jwsbb
import (
"crypto"
"crypto/ed25519"
"fmt"
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
"github.com/lestrrat-go/dsig"
)
func isSupportedEdDSAAlgorithm(alg string) bool {
return alg == "EdDSA"
}
func eddsaGetSigner(key any) (crypto.Signer, error) {
// The ed25519.PrivateKey object implements crypto.Signer, so we should
// simply accept a crypto.Signer here.
signer, ok := key.(crypto.Signer)
if ok {
if !keytype.IsValidEDDSAKey(key) {
return nil, fmt.Errorf(`cannot use key of type %T to generate EdDSA based signatures`, key)
}
return signer, nil
}
// This fallback exists for cases when jwk.Key was passed, or
// users gave us a pointer instead of non-pointer, etc.
var privkey ed25519.PrivateKey
if err := keyconv.Ed25519PrivateKey(&privkey, key); err != nil {
return nil, fmt.Errorf(`failed to retrieve ed25519.PrivateKey out of %T: %w`, key, err)
}
return privkey, nil
}
// SignEdDSA generates an EdDSA (Ed25519) signature for the given payload.
// The raw parameter should be the pre-computed signing input (typically header.payload).
// EdDSA is deterministic and doesn't require additional hashing of the input.
//
// This function is now a thin wrapper around dsig.SignEdDSA. For new projects, you should
// consider using dsig instead of this function.
func SignEdDSA(key ed25519.PrivateKey, payload []byte) ([]byte, error) {
return ed25519.Sign(key, payload), nil
// Use dsig.Sign with EdDSA algorithm constant
return dsig.Sign(key, dsig.EdDSA, payload, nil)
}
// VerifyEdDSA verifies an EdDSA (Ed25519) signature for the given payload.
// This function verifies the signature using Ed25519 verification algorithm.
// The payload parameter should be the pre-computed signing input (typically header.payload).
// EdDSA is deterministic and provides strong security guarantees without requiring hash function selection.
//
// This function is now a thin wrapper around dsig.VerifyEdDSA. For new projects, you should
// consider using dsig instead of this function.
func VerifyEdDSA(key ed25519.PublicKey, payload, signature []byte) error {
if !ed25519.Verify(key, payload, signature) {
return fmt.Errorf("invalid EdDSA signature")
}
return nil
// Use dsig.Verify with EdDSA algorithm constant
return dsig.Verify(key, dsig.EdDSA, payload, signature)
}

View File

@@ -0,0 +1,14 @@
//go:build jwx_es256k
package jwsbb
import (
dsigsecp256k1 "github.com/lestrrat-go/dsig-secp256k1"
)
const es256k = "ES256K"
func init() {
// Add ES256K mapping when this build tag is enabled
jwsToDsigAlgorithm[es256k] = dsigsecp256k1.ECDSAWithSecp256k1AndSHA256
}

View File

@@ -1,67 +1,52 @@
package jwsbb
import (
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
"github.com/lestrrat-go/dsig"
)
var hmacHashFuncs = map[string]func() hash.Hash{
"HS256": sha256.New,
"HS384": sha512.New384,
"HS512": sha512.New,
}
func isSupportedHMACAlgorithm(alg string) bool {
_, ok := hmacHashFuncs[alg]
return ok
}
// HMACHashFuncFor returns the appropriate hash function for the given HMAC algorithm.
// Supported algorithms: HS256 (SHA-256), HS384 (SHA-384), HS512 (SHA-512).
// Returns the hash function constructor and an error if the algorithm is unsupported.
func HMACHashFuncFor(alg string) (func() hash.Hash, error) {
if h, ok := hmacHashFuncs[alg]; ok {
return h, nil
// hmacHashToDsigAlgorithm maps HMAC hash function sizes to dsig algorithm constants
func hmacHashToDsigAlgorithm(hfunc func() hash.Hash) (string, error) {
h := hfunc()
switch h.Size() {
case 32: // SHA256
return dsig.HMACWithSHA256, nil
case 48: // SHA384
return dsig.HMACWithSHA384, nil
case 64: // SHA512
return dsig.HMACWithSHA512, nil
default:
return "", fmt.Errorf("unsupported HMAC hash function: size=%d", h.Size())
}
return nil, fmt.Errorf("unsupported HMAC algorithm %s", alg)
}
func toHMACKey(dst *[]byte, key any) error {
if err := keyconv.ByteSliceKey(dst, key); err != nil {
return fmt.Errorf(`jws.toHMACKey: invalid key type %T. []byte is required: %w`, key, err)
}
if len(*dst) == 0 {
return fmt.Errorf(`jws.toHMACKey: missing key while signing payload`)
}
return nil
}
// SignHMAC generates an HMAC signature for the given payload using the specified hash function and key.
// The raw parameter should be the pre-computed signing input (typically header.payload).
//
// This function is now a thin wrapper around dsig.SignHMAC. For new projects, you should
// consider using dsig instead of this function.
func SignHMAC(key, payload []byte, hfunc func() hash.Hash) ([]byte, error) {
h := hmac.New(hfunc, key)
if _, err := h.Write(payload); err != nil {
return nil, fmt.Errorf(`failed to write payload using hmac: %w`, err)
dsigAlg, err := hmacHashToDsigAlgorithm(hfunc)
if err != nil {
return nil, fmt.Errorf("jwsbb.SignHMAC: %w", err)
}
return h.Sum(nil), nil
return dsig.Sign(key, dsigAlg, payload, nil)
}
// VerifyHMAC verifies an HMAC signature for the given payload.
// This function verifies the signature using the specified key and hash function.
// The payload parameter should be the pre-computed signing input (typically header.payload).
//
// This function is now a thin wrapper around dsig.VerifyHMAC. For new projects, you should
// consider using dsig instead of this function.
func VerifyHMAC(key, payload, signature []byte, hfunc func() hash.Hash) error {
expected, err := SignHMAC(key, payload, hfunc)
dsigAlg, err := hmacHashToDsigAlgorithm(hfunc)
if err != nil {
return fmt.Errorf("failed to sign payload for verification: %w", err)
return fmt.Errorf("jwsbb.VerifyHMAC: %w", err)
}
if !hmac.Equal(signature, expected) {
return fmt.Errorf("invalid HMAC signature")
}
return nil
return dsig.Verify(key, dsigAlg, payload, signature)
}

View File

@@ -12,8 +12,40 @@
// 1. Does minimal checking of input parameters (for performance); callers need to ensure that the parameters are valid.
// 2. All exported functions are strongly typed (i.e. they do not take `any` types unless they absolutely have to).
// 3. Does not rely on other public jwx packages (they are standalone, except for internal packages).
//
// This implementation uses github.com/lestrrat-go/dsig as the underlying signature provider.
package jwsbb
import (
"github.com/lestrrat-go/dsig"
)
// JWS algorithm name constants
const (
// HMAC algorithms
hs256 = "HS256"
hs384 = "HS384"
hs512 = "HS512"
// RSA PKCS#1 v1.5 algorithms
rs256 = "RS256"
rs384 = "RS384"
rs512 = "RS512"
// RSA PSS algorithms
ps256 = "PS256"
ps384 = "PS384"
ps512 = "PS512"
// ECDSA algorithms
es256 = "ES256"
es384 = "ES384"
es512 = "ES512"
// EdDSA algorithm
edDSA = "EdDSA"
)
// Signer is a generic interface that defines the method for signing payloads.
// The type parameter K represents the key type (e.g., []byte for HMAC keys,
// *rsa.PrivateKey for RSA keys, *ecdsa.PrivateKey for ECDSA keys).
@@ -27,3 +59,36 @@ type Signer[K any] interface {
type Verifier[K any] interface {
Verify(key K, buf []byte, signature []byte) error
}
// JWS to dsig algorithm mapping
var jwsToDsigAlgorithm = map[string]string{
// HMAC algorithms
hs256: dsig.HMACWithSHA256,
hs384: dsig.HMACWithSHA384,
hs512: dsig.HMACWithSHA512,
// RSA PKCS#1 v1.5 algorithms
rs256: dsig.RSAPKCS1v15WithSHA256,
rs384: dsig.RSAPKCS1v15WithSHA384,
rs512: dsig.RSAPKCS1v15WithSHA512,
// RSA PSS algorithms
ps256: dsig.RSAPSSWithSHA256,
ps384: dsig.RSAPSSWithSHA384,
ps512: dsig.RSAPSSWithSHA512,
// ECDSA algorithms
es256: dsig.ECDSAWithP256AndSHA256,
es384: dsig.ECDSAWithP384AndSHA384,
es512: dsig.ECDSAWithP521AndSHA512,
// Note: ES256K requires external dependency and is handled separately
// EdDSA algorithm
edDSA: dsig.EdDSA,
}
// getDsigAlgorithm returns the dsig algorithm name for a JWS algorithm
func getDsigAlgorithm(jwsAlg string) (string, bool) {
dsigAlg, ok := jwsToDsigAlgorithm[jwsAlg]
return dsigAlg, ok
}

View File

@@ -6,53 +6,33 @@ import (
"fmt"
"io"
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
"github.com/lestrrat-go/dsig"
)
func rsaGetSignerCryptoSignerKey(key any) (crypto.Signer, bool, error) {
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
if !keytype.IsValidRSAKey(key) {
return nil, false, fmt.Errorf(`cannot use key of type %T`, key)
// rsaHashToDsigAlgorithm maps RSA hash functions to dsig algorithm constants
func rsaHashToDsigAlgorithm(h crypto.Hash, pss bool) (string, error) {
if pss {
switch h {
case crypto.SHA256:
return dsig.RSAPSSWithSHA256, nil
case crypto.SHA384:
return dsig.RSAPSSWithSHA384, nil
case crypto.SHA512:
return dsig.RSAPSSWithSHA512, nil
default:
return "", fmt.Errorf("unsupported hash algorithm for RSA-PSS: %v", h)
}
} else {
switch h {
case crypto.SHA256:
return dsig.RSAPKCS1v15WithSHA256, nil
case crypto.SHA384:
return dsig.RSAPKCS1v15WithSHA384, nil
case crypto.SHA512:
return dsig.RSAPKCS1v15WithSHA512, nil
default:
return "", fmt.Errorf("unsupported hash algorithm for RSA PKCS#1 v1.5: %v", h)
}
return cs, true, nil
}
return nil, false, nil
}
var rsaHashFuncs = map[string]struct {
Hash crypto.Hash
PSS bool // whether to use PSS padding
}{
"RS256": {Hash: crypto.SHA256, PSS: false},
"RS384": {Hash: crypto.SHA384, PSS: false},
"RS512": {Hash: crypto.SHA512, PSS: false},
"PS256": {Hash: crypto.SHA256, PSS: true},
"PS384": {Hash: crypto.SHA384, PSS: true},
"PS512": {Hash: crypto.SHA512, PSS: true},
}
func isSuppotedRSAAlgorithm(alg string) bool {
_, ok := rsaHashFuncs[alg]
return ok
}
// RSAHashFuncFor returns the appropriate hash function and PSS flag for the given RSA algorithm.
// Supported algorithms: RS256, RS384, RS512 (PKCS#1 v1.5) and PS256, PS384, PS512 (PSS).
// Returns the hash function, PSS flag, and an error if the algorithm is unsupported.
func RSAHashFuncFor(alg string) (crypto.Hash, bool, error) {
if h, ok := rsaHashFuncs[alg]; ok {
return h.Hash, h.PSS, nil
}
return 0, false, fmt.Errorf("unsupported RSA algorithm %s", alg)
}
// RSAPSSOptions returns the PSS options for RSA-PSS signatures with the specified hash.
// The salt length is set to equal the hash length as per RFC 7518.
func RSAPSSOptions(h crypto.Hash) rsa.PSSOptions {
return rsa.PSSOptions{
Hash: h,
SaltLength: rsa.PSSSaltLengthEqualsHash,
}
}
@@ -62,25 +42,30 @@ func RSAPSSOptions(h crypto.Hash) rsa.PSSOptions {
//
// The rr parameter is an optional io.Reader that can be used to provide randomness for signing.
// If rr is nil, it defaults to rand.Reader.
//
// This function is now a thin wrapper around dsig.SignRSA. For new projects, you should
// consider using dsig instead of this function.
func SignRSA(key *rsa.PrivateKey, payload []byte, h crypto.Hash, pss bool, rr io.Reader) ([]byte, error) {
var opts crypto.SignerOpts = h
if pss {
rsaopts := RSAPSSOptions(h)
opts = &rsaopts
dsigAlg, err := rsaHashToDsigAlgorithm(h, pss)
if err != nil {
return nil, fmt.Errorf("jwsbb.SignRSA: %w", err)
}
return cryptosign(key, payload, h, opts, rr)
return dsig.Sign(key, dsigAlg, payload, rr)
}
// VerifyRSA verifies an RSA signature for the given payload and header.
// This function constructs the signing input by encoding the header and payload according to JWS specification,
// then verifies the signature using the specified public key and hash algorithm.
// If pss is true, RSA-PSS verification is used; otherwise, PKCS#1 v1.5 verification is used.
//
// This function is now a thin wrapper around dsig.VerifyRSA. For new projects, you should
// consider using dsig instead of this function.
func VerifyRSA(key *rsa.PublicKey, payload, signature []byte, h crypto.Hash, pss bool) error {
hasher := h.New()
hasher.Write(payload)
digest := hasher.Sum(nil)
if pss {
return rsa.VerifyPSS(key, h, digest, signature, &rsa.PSSOptions{Hash: h, SaltLength: rsa.PSSSaltLengthEqualsHash})
dsigAlg, err := rsaHashToDsigAlgorithm(h, pss)
if err != nil {
return fmt.Errorf("jwsbb.VerifyRSA: %w", err)
}
return rsa.VerifyPKCS1v15(key, h, digest, signature)
return dsig.Verify(key, dsigAlg, payload, signature)
}

View File

@@ -2,94 +2,109 @@ package jwsbb
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"fmt"
"io"
"github.com/lestrrat-go/dsig"
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
)
// Sign generates a JWS signature using the specified key and algorithm.
//
// This function loads the signer registered in the hwsbb package _ONLY_.
// This function loads the signer registered in the jwsbb package _ONLY_.
// It does not support custom signers that the user might have registered.
//
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
// Not all algorithms require this parameter, but it is included for consistency.
// 99% of the time, you can pass nil for rr, and it will work fine.
func Sign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) {
switch {
case isSupportedHMACAlgorithm(alg):
return dispatchHMACSign(key, alg, payload)
case isSuppotedRSAAlgorithm(alg):
return dispatchRSASign(key, alg, payload, rr)
case isSuppotedECDSAAlgorithm(alg):
return dispatchECDSASign(key, alg, payload, rr)
case isSupportedEdDSAAlgorithm(alg):
return dispatchEdDSASign(key, alg, payload, rr)
dsigAlg, ok := getDsigAlgorithm(alg)
if !ok {
return nil, fmt.Errorf(`jwsbb.Sign: unsupported signature algorithm %q`, alg)
}
return nil, fmt.Errorf(`jwsbb.Sign: unsupported signature algorithm %q`, alg)
// Get dsig algorithm info to determine key conversion strategy
dsigInfo, ok := dsig.GetAlgorithmInfo(dsigAlg)
if !ok {
return nil, fmt.Errorf(`jwsbb.Sign: dsig algorithm %q not registered`, dsigAlg)
}
switch dsigInfo.Family {
case dsig.HMAC:
return dispatchHMACSign(key, dsigAlg, payload)
case dsig.RSA:
return dispatchRSASign(key, dsigAlg, payload, rr)
case dsig.ECDSA:
return dispatchECDSASign(key, dsigAlg, payload, rr)
case dsig.EdDSAFamily:
return dispatchEdDSASign(key, dsigAlg, payload, rr)
default:
return nil, fmt.Errorf(`jwsbb.Sign: unsupported dsig algorithm family %q`, dsigInfo.Family)
}
}
func dispatchHMACSign(key any, alg string, payload []byte) ([]byte, error) {
h, err := HMACHashFuncFor(alg)
if err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: failed to get hash function for %s: %w`, alg, err)
}
func dispatchHMACSign(key any, dsigAlg string, payload []byte) ([]byte, error) {
var hmackey []byte
if err := toHMACKey(&hmackey, key); err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: %w`, err)
if err := keyconv.ByteSliceKey(&hmackey, key); err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: invalid key type %T. []byte is required: %w`, key, err)
}
return SignHMAC(hmackey, payload, h)
return dsig.Sign(hmackey, dsigAlg, payload, nil)
}
func dispatchRSASign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) {
h, pss, err := RSAHashFuncFor(alg)
if err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: failed to get hash function for %s: %w`, alg, err)
}
cs, isCryptoSigner, err := rsaGetSignerCryptoSignerKey(key)
if err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: %w`, err)
}
if isCryptoSigner {
var options crypto.SignerOpts = h
if pss {
rsaopts := RSAPSSOptions(h)
options = &rsaopts
func dispatchRSASign(key any, dsigAlg string, payload []byte, rr io.Reader) ([]byte, error) {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an RSA key
if _, ok := signer.Public().(*rsa.PublicKey); ok {
return dsig.Sign(signer, dsigAlg, payload, rr)
}
return SignCryptoSigner(cs, payload, h, options, rr)
}
// Fall back to concrete key types
var privkey *rsa.PrivateKey
if err := keyconv.RSAPrivateKey(&privkey, key); err != nil {
return nil, fmt.Errorf(`jws.RSASigner: invalid key type %T. rsa.PrivateKey is required: %w`, key, err)
}
return SignRSA(privkey, payload, h, pss, rr)
}
func dispatchEdDSASign(key any, _ string, payload []byte, rr io.Reader) ([]byte, error) {
signer, err := eddsaGetSigner(key)
if err != nil {
return nil, fmt.Errorf(`jws.EdDSASigner: %w`, err)
return nil, fmt.Errorf(`jwsbb.Sign: invalid key type %T. *rsa.PrivateKey is required: %w`, key, err)
}
return SignCryptoSigner(signer, payload, crypto.Hash(0), crypto.Hash(0), rr)
return dsig.Sign(privkey, dsigAlg, payload, rr)
}
func dispatchECDSASign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) {
h, err := ECDSAHashFuncFor(alg)
if err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: failed to get hash function for %s: %w`, alg, err)
func dispatchECDSASign(key any, dsigAlg string, payload []byte, rr io.Reader) ([]byte, error) {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an ECDSA key
if _, ok := signer.Public().(*ecdsa.PublicKey); ok {
return dsig.Sign(signer, dsigAlg, payload, rr)
}
}
privkey, cs, isCryptoSigner, err := ecdsaGetSignerKey(key)
if err != nil {
return nil, fmt.Errorf(`jws.ECDSASigner: %w`, err)
// Fall back to concrete key types
var privkey *ecdsa.PrivateKey
if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: invalid key type %T. *ecdsa.PrivateKey is required: %w`, key, err)
}
if isCryptoSigner {
return SignECDSACryptoSigner(cs, payload, h, rr)
}
return SignECDSA(privkey, payload, h, rr)
return dsig.Sign(privkey, dsigAlg, payload, rr)
}
func dispatchEdDSASign(key any, dsigAlg string, payload []byte, rr io.Reader) ([]byte, error) {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an EdDSA key
if _, ok := signer.Public().(ed25519.PublicKey); ok {
return dsig.Sign(signer, dsigAlg, payload, rr)
}
}
// Fall back to concrete key types
var privkey ed25519.PrivateKey
if err := keyconv.Ed25519PrivateKey(&privkey, key); err != nil {
return nil, fmt.Errorf(`jwsbb.Sign: invalid key type %T. ed25519.PrivateKey is required: %w`, key, err)
}
return dsig.Sign(privkey, dsigAlg, payload, rr)
}

View File

@@ -7,6 +7,7 @@ import (
"crypto/rsa"
"fmt"
"github.com/lestrrat-go/dsig"
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
)
@@ -15,113 +16,90 @@ import (
// This function loads the verifier registered in the jwsbb package _ONLY_.
// It does not support custom verifiers that the user might have registered.
func Verify(key any, alg string, payload, signature []byte) error {
switch {
case isSupportedHMACAlgorithm(alg):
return dispatchHMACVerify(key, alg, payload, signature)
case isSuppotedRSAAlgorithm(alg):
return dispatchRSAVerify(key, alg, payload, signature)
case isSuppotedECDSAAlgorithm(alg):
return dispatchECDSAVerify(key, alg, payload, signature)
case isSupportedEdDSAAlgorithm(alg):
return dispatchEdDSAVerify(key, alg, payload, signature)
dsigAlg, ok := getDsigAlgorithm(alg)
if !ok {
return fmt.Errorf(`jwsbb.Verify: unsupported signature algorithm %q`, alg)
}
return fmt.Errorf(`jwsbb.Verify: unsupported signature algorithm %q`, alg)
// Get dsig algorithm info to determine key conversion strategy
dsigInfo, ok := dsig.GetAlgorithmInfo(dsigAlg)
if !ok {
return fmt.Errorf(`jwsbb.Verify: dsig algorithm %q not registered`, dsigAlg)
}
switch dsigInfo.Family {
case dsig.HMAC:
return dispatchHMACVerify(key, dsigAlg, payload, signature)
case dsig.RSA:
return dispatchRSAVerify(key, dsigAlg, payload, signature)
case dsig.ECDSA:
return dispatchECDSAVerify(key, dsigAlg, payload, signature)
case dsig.EdDSAFamily:
return dispatchEdDSAVerify(key, dsigAlg, payload, signature)
default:
return fmt.Errorf(`jwsbb.Verify: unsupported dsig algorithm family %q`, dsigInfo.Family)
}
}
func dispatchHMACVerify(key any, alg string, payload, signature []byte) error {
h, err := HMACHashFuncFor(alg)
if err != nil {
return fmt.Errorf(`jwsbb.Verify: failed to get hash function for %s: %w`, alg, err)
}
func dispatchHMACVerify(key any, dsigAlg string, payload, signature []byte) error {
var hmackey []byte
if err := toHMACKey(&hmackey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: %w`, err)
if err := keyconv.ByteSliceKey(&hmackey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: invalid key type %T. []byte is required: %w`, key, err)
}
return VerifyHMAC(hmackey, payload, signature, h)
return dsig.Verify(hmackey, dsigAlg, payload, signature)
}
func dispatchRSAVerify(key any, alg string, payload, signature []byte) error {
h, pss, err := RSAHashFuncFor(alg)
if err != nil {
return fmt.Errorf(`jwsbb.Verify: failed to get hash function for %s: %w`, alg, err)
func dispatchRSAVerify(key any, dsigAlg string, payload, signature []byte) error {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an RSA key
if _, ok := signer.Public().(*rsa.PublicKey); ok {
return dsig.Verify(signer, dsigAlg, payload, signature)
}
}
// Fall back to concrete key types
var pubkey *rsa.PublicKey
if cs, ok := key.(crypto.Signer); ok {
cpub := cs.Public()
switch cpub := cpub.(type) {
case rsa.PublicKey:
pubkey = &cpub
case *rsa.PublicKey:
pubkey = cpub
default:
return fmt.Errorf(`jwsbb.Verify: failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key)
}
} else {
if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: failed to retrieve rsa.PublicKey out of %T: %w`, key, err)
}
if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: invalid key type %T. *rsa.PublicKey is required: %w`, key, err)
}
return VerifyRSA(pubkey, payload, signature, h, pss)
return dsig.Verify(pubkey, dsigAlg, payload, signature)
}
func dispatchECDSAVerify(key any, alg string, payload, signature []byte) error {
h, err := ECDSAHashFuncFor(alg)
if err != nil {
return fmt.Errorf(`jwsbb.Verify: failed to get hash function for %s: %w`, alg, err)
}
pubkey, cs, isCryptoSigner, err := ecdsaGetVerifierKey(key)
if err != nil {
return fmt.Errorf(`jwsbb.Verify: %w`, err)
}
if isCryptoSigner {
return VerifyECDSACryptoSigner(cs, payload, signature, h)
}
return VerifyECDSA(pubkey, payload, signature, h)
}
func dispatchEdDSAVerify(key any, _ string, payload, signature []byte) error {
var pubkey ed25519.PublicKey
signer, ok := key.(crypto.Signer)
if ok {
v := signer.Public()
pubkey, ok = v.(ed25519.PublicKey)
if !ok {
return fmt.Errorf(`jwsbb.Verify: expected crypto.Signer.Public() to return ed25519.PublicKey, but got %T`, v)
}
} else {
if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: failed to retrieve ed25519.PublicKey out of %T: %w`, key, err)
func dispatchECDSAVerify(key any, dsigAlg string, payload, signature []byte) error {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an ECDSA key
if _, ok := signer.Public().(*ecdsa.PublicKey); ok {
return dsig.Verify(signer, dsigAlg, payload, signature)
}
}
return VerifyEdDSA(pubkey, payload, signature)
}
func ecdsaGetVerifierKey(key any) (*ecdsa.PublicKey, crypto.Signer, bool, error) {
cs, isCryptoSigner := key.(crypto.Signer)
if isCryptoSigner {
switch key.(type) {
case ecdsa.PublicKey, *ecdsa.PublicKey:
// if it's ecdsa.PublicKey, it's more efficient to
// go through the non-crypto.Signer route. Set isCryptoSigner to false
isCryptoSigner = false
}
}
if isCryptoSigner {
return nil, cs, true, nil
}
// Fall back to concrete key types
var pubkey *ecdsa.PublicKey
if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
return nil, nil, false, fmt.Errorf(`invalid key type %T. ecdsa.PublicKey is required: %w`, key, err)
return fmt.Errorf(`jwsbb.Verify: invalid key type %T. *ecdsa.PublicKey is required: %w`, key, err)
}
return pubkey, nil, false, nil
return dsig.Verify(pubkey, dsigAlg, payload, signature)
}
func dispatchEdDSAVerify(key any, dsigAlg string, payload, signature []byte) error {
// Try crypto.Signer first (dsig can handle it directly)
if signer, ok := key.(crypto.Signer); ok {
// Verify it's an EdDSA key
if _, ok := signer.Public().(ed25519.PublicKey); ok {
return dsig.Verify(signer, dsigAlg, payload, signature)
}
}
// Fall back to concrete key types
var pubkey ed25519.PublicKey
if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil {
return fmt.Errorf(`jwsbb.Verify: invalid key type %T. ed25519.PublicKey is required: %w`, key, err)
}
return dsig.Verify(pubkey, dsigAlg, payload, signature)
}

File diff suppressed because it is too large Load Diff

View File

@@ -176,7 +176,7 @@ func LoadPathsForRegoVersion(regoVersion ast.RegoVersion,
}
}
if len(nonBundlePaths) == 0 {
if asBundle {
return &result, nil
}

View File

@@ -34,6 +34,7 @@ type (
RelatedResources []*RelatedResourceAnnotation `json:"related_resources,omitempty"`
Authors []*AuthorAnnotation `json:"authors,omitempty"`
Schemas []*SchemaAnnotation `json:"schemas,omitempty"`
Compile *CompileAnnotation `json:"compile,omitempty"`
Custom map[string]any `json:"custom,omitempty"`
Location *Location `json:"location,omitempty"`
@@ -48,6 +49,11 @@ type (
Definition *any `json:"definition,omitempty"`
}
CompileAnnotation struct {
Unknowns []Ref `json:"unknowns,omitempty"`
MaskRule Ref `json:"mask_rule,omitempty"` // NOTE: This doesn't need to start with "data.package", it can be relative
}
AuthorAnnotation struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
@@ -151,6 +157,10 @@ func (a *Annotations) Compare(other *Annotations) int {
return cmp
}
if cmp := a.Compile.Compare(other.Compile); cmp != 0 {
return cmp
}
if a.Entrypoint != other.Entrypoint {
if a.Entrypoint {
return 1
@@ -403,6 +413,8 @@ func (a *Annotations) Copy(node Node) *Annotations {
cpy.Schemas[i] = a.Schemas[i].Copy()
}
cpy.Compile = a.Compile.Copy()
if a.Custom != nil {
cpy.Custom = deepcopy.Map(a.Custom)
}
@@ -716,6 +728,44 @@ func (s *SchemaAnnotation) String() string {
return string(bs)
}
// Copy returns a deep copy of s.
func (c *CompileAnnotation) Copy() *CompileAnnotation {
if c == nil {
return nil
}
cpy := *c
for i := range c.Unknowns {
cpy.Unknowns[i] = c.Unknowns[i].Copy()
}
return &cpy
}
// Compare returns an integer indicating if s is less than, equal to, or greater
// than other.
func (c *CompileAnnotation) Compare(other *CompileAnnotation) int {
switch {
case c == nil && other == nil:
return 0
case c != nil && other == nil:
return 1
case c == nil && other != nil:
return -1
}
if cmp := slices.CompareFunc(c.Unknowns, other.Unknowns,
func(x, y Ref) int {
return x.Compare(y)
}); cmp != 0 {
return cmp
}
return c.MaskRule.Compare(other.MaskRule)
}
func (c *CompileAnnotation) String() string {
bs, _ := json.Marshal(c)
return string(bs)
}
func newAnnotationSet() *AnnotationSet {
return &AnnotationSet{
byRule: map[*Rule][]*Annotations{},

View File

@@ -213,7 +213,7 @@ func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) {
return result, nil
}
func (i *baseDocEqIndex) AllRules(_ ValueResolver) (*IndexResult, error) {
func (i *baseDocEqIndex) AllRules(ValueResolver) (*IndexResult, error) {
tr := newTrieTraversalResult()
// Walk over the rule trie and accumulate _all_ rules
@@ -285,33 +285,51 @@ func newrefindices(isVirtual func(Ref) bool) *refindices {
}
}
// anyValue is a fake variable we used to put "naked ref" expressions
// into the rule index
var anyValue = Var("__any__")
// Update attempts to update the refindices for the given expression in the
// given rule. If the expression cannot be indexed the update does not affect
// the indices.
func (i *refindices) Update(rule *Rule, expr *Expr) {
if expr.Negated {
return
}
if len(expr.With) > 0 {
// NOTE(tsandall): In the future, we may need to consider expressions
// that have with statements applied to them.
return
}
op := expr.Operator()
if expr.Negated {
// NOTE(sr): We could try to cover simple expressions, like
// not input.funky => input.funky == false or undefined (two refindex?)
return
}
op := expr.Operator()
if op == nil {
if ts, ok := expr.Terms.(*Term); ok {
// NOTE(sr): If we wanted to cover function args, we'd need to also
// check for type "Var" here. But since it's impossible to call a
// function with a undefined argument, there's no point to recording
// "needs to be anything" for function args
if ref, ok := ts.Value.(Ref); ok { // "naked ref"
i.updateEq(rule, ref, anyValue)
}
}
}
a, b := expr.Operand(0), expr.Operand(1)
switch {
case op.Equal(equalityRef):
i.updateEq(rule, expr)
i.updateEq(rule, a.Value, b.Value)
case op.Equal(equalRef) && len(expr.Operands()) == 2:
// NOTE(tsandall): if equal() is called with more than two arguments the
// output value is being captured in which case the indexer cannot
// exclude the rule if the equal() call would return false (because the
// false value must still be produced.)
i.updateEq(rule, expr)
i.updateEq(rule, a.Value, b.Value)
case op.Equal(globMatchRef) && len(expr.Operands()) == 3:
// NOTE(sr): Same as with equal() above -- 4 operands means the output
@@ -366,8 +384,7 @@ func (i *refindices) Mapper(rule *Rule, ref Ref) *valueMapper {
return nil
}
func (i *refindices) updateEq(rule *Rule, expr *Expr) {
a, b := expr.Operand(0), expr.Operand(1)
func (i *refindices) updateEq(rule *Rule, a, b Value) {
args := rule.Head.Args
if idx, ok := eqOperandsToRefAndValue(i.isVirtual, args, a, b); ok {
i.insert(rule, idx)
@@ -426,12 +443,7 @@ func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) {
}
func (i *refindices) insert(rule *Rule, index *refindex) {
count, ok := i.frequency.Get(index.Ref)
if !ok {
count = 0
}
count, _ := i.frequency.Get(index.Ref)
i.frequency.Put(index.Ref, count+1)
for pos, other := range i.rules[rule] {
@@ -454,7 +466,7 @@ func (i *refindices) index(rule *Rule, ref Ref) *refindex {
}
type trieWalker interface {
Do(x any) trieWalker
Do(any) trieWalker
}
type trieTraversalResult struct {
@@ -816,11 +828,11 @@ func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalR
// for the argument number. So for `f(x, y) { x = 10; y = 12 }`, we'll
// bind `args[0]` and `args[1]` to this rule when called for (x=10) and
// (y=12) respectively.
func eqOperandsToRefAndValue(isVirtual func(Ref) bool, args []*Term, a, b *Term) (*refindex, bool) {
switch v := a.Value.(type) {
func eqOperandsToRefAndValue(isVirtual func(Ref) bool, args []*Term, a, b Value) (*refindex, bool) {
switch v := a.(type) {
case Var:
for i, arg := range args {
if arg.Value.Compare(a.Value) == 0 {
if arg.Value.Compare(a) == 0 {
if bval, ok := indexValue(b); ok {
return &refindex{Ref: Ref{FunctionArgRootDocument, InternedTerm(i)}, Value: bval}, true
}
@@ -843,8 +855,8 @@ func eqOperandsToRefAndValue(isVirtual func(Ref) bool, args []*Term, a, b *Term)
return nil, false
}
func indexValue(b *Term) (Value, bool) {
switch b := b.Value.(type) {
func indexValue(b Value) (Value, bool) {
switch b := b.(type) {
case Null, Boolean, Number, String, Var:
return b, true
case *Array:

View File

@@ -1259,23 +1259,25 @@ func (p *Parser) parseLiteralExpr(negated bool) *Expr {
return nil
}
}
// If we find a plain `every` identifier, attempt to parse an every expression,
// add hint if it succeeds.
if term, ok := expr.Terms.(*Term); ok && Var("every").Equal(term.Value) {
var hint bool
t := p.save()
p.restore(s)
if expr := p.futureParser().parseEvery(); expr != nil {
_, hint = expr.Terms.(*Every)
}
p.restore(t)
if hint {
p.hint("`import future.keywords.every` for `every x in xs { ... }` expressions")
if p.isFutureKeyword("every") {
// If we find a plain `every` identifier, attempt to parse an every expression,
// add hint if it succeeds.
if term, ok := expr.Terms.(*Term); ok && Var("every").Equal(term.Value) {
var hint bool
t := p.save()
p.restore(s)
if expr := p.futureParser().parseEvery(); expr != nil {
_, hint = expr.Terms.(*Every)
}
p.restore(t)
if hint {
p.hint("`import future.keywords.every` for `every x in xs { ... }` expressions")
}
}
}
return expr
}
return nil
return expr
}
func (p *Parser) parseWith() []*With {
@@ -1368,26 +1370,28 @@ func (p *Parser) parseSome() *Expr {
}
p.restore(s)
s = p.save() // new copy for later
var hint bool
p.scan()
if term := p.futureParser().parseTermInfixCall(); term != nil {
if call, ok := term.Value.(Call); ok {
switch call[0].String() {
case Member.Name, MemberWithKey.Name:
hint = true
if p.isFutureKeyword("in") {
s = p.save() // new copy for later
var hint bool
p.scan()
if term := p.futureParser().parseTermInfixCall(); term != nil {
if call, ok := term.Value.(Call); ok {
switch call[0].String() {
case Member.Name, MemberWithKey.Name:
hint = true
}
}
}
// go on as before, it's `some x[...]` or illegal
p.restore(s)
if hint {
p.hint("`import future.keywords.in` for `some x in xs` expressions")
}
}
// go on as before, it's `some x[...]` or illegal
p.restore(s)
if hint {
p.hint("`import future.keywords.in` for `some x in xs` expressions")
}
for { // collecting var args
p.scan()
if p.s.tok != tokens.Ident {
@@ -2566,6 +2570,7 @@ type rawAnnotation struct {
RelatedResources []any `yaml:"related_resources"`
Authors []any `yaml:"authors"`
Schemas []map[string]any `yaml:"schemas"`
Compile map[string]any `yaml:"compile"`
Custom map[string]any `yaml:"custom"`
}
@@ -2633,6 +2638,40 @@ func (b *metadataParser) Parse() (*Annotations, error) {
result.RelatedResources = append(result.RelatedResources, rr)
}
if raw.Compile != nil {
result.Compile = &CompileAnnotation{}
if unknowns, ok := raw.Compile["unknowns"]; ok {
if unknowns, ok := unknowns.([]any); ok {
result.Compile.Unknowns = make([]Ref, len(unknowns))
for i := range unknowns {
if unknown, ok := unknowns[i].(string); ok {
ref, err := ParseRef(unknown)
if err != nil {
return nil, fmt.Errorf("invalid unknowns element %q: %w", unknown, err)
}
result.Compile.Unknowns[i] = ref
}
}
}
}
if mask, ok := raw.Compile["mask_rule"]; ok {
if mask, ok := mask.(string); ok {
maskTerm, err := ParseTerm(mask)
if err != nil {
return nil, fmt.Errorf("invalid mask_rule annotation %q: %w", mask, err)
}
switch v := maskTerm.Value.(type) {
case Var, String:
result.Compile.MaskRule = Ref{maskTerm}
case Ref:
result.Compile.MaskRule = v
default:
return nil, fmt.Errorf("invalid mask_rule annotation type %q: %[1]T", mask)
}
}
}
}
for _, pair := range raw.Schemas {
k, v := unwrapPair(pair)
@@ -2916,6 +2955,11 @@ func IsFutureKeywordForRegoVersion(s string, v RegoVersion) bool {
return yes
}
// isFutureKeyword answers if keyword is from the "future" with the parser options set.
func (p *Parser) isFutureKeyword(s string) bool {
return IsFutureKeywordForRegoVersion(s, p.po.RegoVersion)
}
func (p *Parser) futureImport(imp *Import, allowedFutureKeywords map[string]tokens.Token) {
path := imp.Path.Value.(Ref)

View File

@@ -462,7 +462,7 @@ func (it *iterator) Next() (*storage.Update, error) {
f := it.files[it.idx]
it.idx++
isPolicy := false
var isPolicy bool
if strings.HasSuffix(f.name, RegoExt) {
isPolicy = true
}

View File

@@ -19,21 +19,27 @@ import (
// Well-known metric names.
const (
BundleRequest = "bundle_request"
ServerHandler = "server_handler"
ServerQueryCacheHit = "server_query_cache_hit"
SDKDecisionEval = "sdk_decision_eval"
RegoQueryCompile = "rego_query_compile"
RegoQueryEval = "rego_query_eval"
RegoQueryParse = "rego_query_parse"
RegoModuleParse = "rego_module_parse"
RegoDataParse = "rego_data_parse"
RegoModuleCompile = "rego_module_compile"
RegoPartialEval = "rego_partial_eval"
RegoInputParse = "rego_input_parse"
RegoLoadFiles = "rego_load_files"
RegoLoadBundles = "rego_load_bundles"
RegoExternalResolve = "rego_external_resolve"
BundleRequest = "bundle_request"
ServerHandler = "server_handler"
ServerQueryCacheHit = "server_query_cache_hit"
SDKDecisionEval = "sdk_decision_eval"
RegoQueryCompile = "rego_query_compile"
RegoQueryEval = "rego_query_eval"
RegoQueryParse = "rego_query_parse"
RegoModuleParse = "rego_module_parse"
RegoDataParse = "rego_data_parse"
RegoModuleCompile = "rego_module_compile"
RegoPartialEval = "rego_partial_eval"
RegoInputParse = "rego_input_parse"
RegoLoadFiles = "rego_load_files"
RegoLoadBundles = "rego_load_bundles"
RegoExternalResolve = "rego_external_resolve"
CompilePrepPartial = "compile_prep_partial"
CompileEvalConstraints = "compile_eval_constraints"
CompileTranslateQueries = "compile_translate_queries"
CompileExtractAnnotationsUnknowns = "compile_extract_annotations_unknowns"
CompileExtractAnnotationsMask = "compile_extract_annotations_mask"
CompileEvalMaskRule = "compile_eval_mask_rule"
)
// Info contains attributes describing the underlying metrics provider.

View File

@@ -1646,12 +1646,11 @@ func (e *eval) getRules(ref ast.Ref, args []*ast.Term) (*ast.IndexResult, error)
var result *ast.IndexResult
var err error
resolver.e = e
if e.indexing {
resolver.e = e
resolver.args = args
result, err = index.Lookup(resolver)
} else {
resolver.e = e
result, err = index.AllRules(resolver)
}
if err != nil {

View File

@@ -675,7 +675,7 @@ const gqlCacheName = "graphql"
func init() {
var defaultCacheEntries int = 10
var defaultCacheEntries = 10
var graphqlCacheConfig = cache.NamedValueCacheConfig{
MaxNumEntries: &defaultCacheEntries,
}

View File

@@ -99,6 +99,7 @@ var (
requiredKeys = ast.NewSet(ast.InternedTerm("method"), ast.InternedTerm("url"))
httpSendLatencyMetricKey = "rego_builtin_http_send"
httpSendInterQueryCacheHits = httpSendLatencyMetricKey + "_interquery_cache_hits"
httpSendNetworkRequests = httpSendLatencyMetricKey + "_network_requests"
)
type httpSendKey string
@@ -1535,6 +1536,9 @@ func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) {
return nil, handleHTTPSendErr(c.bctx, err)
}
// Increment counter for actual network requests
c.bctx.Metrics.Counter(httpSendNetworkRequests).Incr()
return executeHTTPRequest(c.httpReq, c.httpClient, c.req)
}
@@ -1586,6 +1590,10 @@ func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) {
if err != nil {
return nil, handleHTTPSendErr(c.bctx, err)
}
// Increment counter for actual network requests
c.bctx.Metrics.Counter(httpSendNetworkRequests).Incr()
return executeHTTPRequest(httpReq, httpClient, c.req)
}

View File

@@ -15,8 +15,10 @@ import (
type randIntCachingKey string
var zero = big.NewInt(0)
var one = big.NewInt(1)
var (
zero = big.NewInt(0)
one = big.NewInt(1)
)
func builtinNumbersRange(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
if canGenerateCheapRange(operands) {
@@ -45,8 +47,9 @@ func builtinNumbersRangeStep(bctx BuiltinContext, operands []*ast.Term, iter fun
if canGenerateCheapRangeStep(operands) {
step, _ := builtins.IntOperand(operands[2].Value, 3)
if step <= 0 {
return errors.New("numbers.range_step: step must be a positive number above zero")
return errors.New("numbers.range_step: step must be a positive integer")
}
return generateCheapRange(operands, step, iter)
}
@@ -66,7 +69,7 @@ func builtinNumbersRangeStep(bctx BuiltinContext, operands []*ast.Term, iter fun
}
if step.Cmp(zero) <= 0 {
return errors.New("numbers.range_step: step must be a positive number above zero")
return errors.New("numbers.range_step: step must be a positive integer")
}
ast, err := generateRange(bctx, x, y, step, "numbers.range_step")
@@ -158,11 +161,9 @@ func generateRange(bctx BuiltinContext, x *big.Int, y *big.Int, step *big.Int, f
}
func builtinRandIntn(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
strOp, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}
n, err := builtins.IntOperand(operands[1].Value, 2)
@@ -178,7 +179,7 @@ func builtinRandIntn(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.T
n = -n
}
var key = randIntCachingKey(fmt.Sprintf("%s-%d", strOp, n))
key := randIntCachingKey(fmt.Sprintf("%s-%d", strOp, n))
if val, ok := bctx.Cache.Get(key); ok {
return iter(val.(*ast.Term))

View File

@@ -10,7 +10,7 @@ import (
"runtime/debug"
)
var Version = "1.8.0"
var Version = "1.9.0"
// GoVersion is the version of Go this was built with
var GoVersion = runtime.Version()

View File

@@ -33,7 +33,7 @@ GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
GO_VERSION ?= $(shell $(GO) version)
GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))Error Parsing File
GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
PROMU := $(FIRST_GOPATH)/bin/promu
@@ -61,7 +61,8 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_
SKIP_GOLANGCI_LINT :=
GOLANGCI_LINT :=
GOLANGCI_LINT_OPTS ?=
GOLANGCI_LINT_VERSION ?= v2.0.2
GOLANGCI_LINT_VERSION ?= v2.1.5
GOLANGCI_FMT_OPTS ?=
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64.
# windows isn't included here because of the path separator being different.
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
@@ -156,9 +157,13 @@ $(GOTEST_DIR):
@mkdir -p $@
.PHONY: common-format
common-format:
common-format: $(GOLANGCI_LINT)
@echo ">> formatting code"
$(GO) fmt $(pkgs)
ifdef GOLANGCI_LINT
@echo ">> formatting code with golangci-lint"
$(GOLANGCI_LINT) fmt $(GOLANGCI_FMT_OPTS)
endif
.PHONY: common-vet
common-vet:
@@ -248,8 +253,8 @@ $(PROMU):
cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
rm -r $(PROMU_TMP)
.PHONY: proto
proto:
.PHONY: common-proto
common-proto:
@echo ">> generating code from proto files"
@./scripts/genproto.sh

View File

@@ -123,13 +123,16 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
finish := float64(0)
pct := float64(0)
recovering := strings.Contains(lines[syncLineIdx], "recovery")
reshaping := strings.Contains(lines[syncLineIdx], "reshape")
resyncing := strings.Contains(lines[syncLineIdx], "resync")
checking := strings.Contains(lines[syncLineIdx], "check")
// Append recovery and resyncing state info.
if recovering || resyncing || checking {
if recovering || resyncing || checking || reshaping {
if recovering {
state = "recovering"
} else if reshaping {
state = "reshaping"
} else if checking {
state = "checking"
} else {

View File

@@ -66,6 +66,10 @@ type Meminfo struct {
// Memory which has been evicted from RAM, and is temporarily
// on the disk
SwapFree *uint64
// Memory consumed by the zswap backend (compressed size)
Zswap *uint64
// Amount of anonymous memory stored in zswap (original size)
Zswapped *uint64
// Memory which is waiting to get written back to the disk
Dirty *uint64
// Memory which is actively being written back to the disk
@@ -85,6 +89,8 @@ type Meminfo struct {
// amount of memory dedicated to the lowest level of page
// tables.
PageTables *uint64
// secondary page tables.
SecPageTables *uint64
// NFS pages sent to the server, but not yet committed to
// stable storage
NFSUnstable *uint64
@@ -129,15 +135,18 @@ type Meminfo struct {
Percpu *uint64
HardwareCorrupted *uint64
AnonHugePages *uint64
FileHugePages *uint64
ShmemHugePages *uint64
ShmemPmdMapped *uint64
CmaTotal *uint64
CmaFree *uint64
Unaccepted *uint64
HugePagesTotal *uint64
HugePagesFree *uint64
HugePagesRsvd *uint64
HugePagesSurp *uint64
Hugepagesize *uint64
Hugetlb *uint64
DirectMap4k *uint64
DirectMap2M *uint64
DirectMap1G *uint64
@@ -161,6 +170,8 @@ type Meminfo struct {
MlockedBytes *uint64
SwapTotalBytes *uint64
SwapFreeBytes *uint64
ZswapBytes *uint64
ZswappedBytes *uint64
DirtyBytes *uint64
WritebackBytes *uint64
AnonPagesBytes *uint64
@@ -171,6 +182,7 @@ type Meminfo struct {
SUnreclaimBytes *uint64
KernelStackBytes *uint64
PageTablesBytes *uint64
SecPageTablesBytes *uint64
NFSUnstableBytes *uint64
BounceBytes *uint64
WritebackTmpBytes *uint64
@@ -182,11 +194,14 @@ type Meminfo struct {
PercpuBytes *uint64
HardwareCorruptedBytes *uint64
AnonHugePagesBytes *uint64
FileHugePagesBytes *uint64
ShmemHugePagesBytes *uint64
ShmemPmdMappedBytes *uint64
CmaTotalBytes *uint64
CmaFreeBytes *uint64
UnacceptedBytes *uint64
HugepagesizeBytes *uint64
HugetlbBytes *uint64
DirectMap4kBytes *uint64
DirectMap2MBytes *uint64
DirectMap1GBytes *uint64
@@ -287,6 +302,12 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "SwapFree:":
m.SwapFree = &val
m.SwapFreeBytes = &valBytes
case "Zswap:":
m.Zswap = &val
m.ZswapBytes = &valBytes
case "Zswapped:":
m.Zswapped = &val
m.ZswapBytes = &valBytes
case "Dirty:":
m.Dirty = &val
m.DirtyBytes = &valBytes
@@ -317,6 +338,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "PageTables:":
m.PageTables = &val
m.PageTablesBytes = &valBytes
case "SecPageTables:":
m.SecPageTables = &val
m.SecPageTablesBytes = &valBytes
case "NFS_Unstable:":
m.NFSUnstable = &val
m.NFSUnstableBytes = &valBytes
@@ -350,6 +374,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "AnonHugePages:":
m.AnonHugePages = &val
m.AnonHugePagesBytes = &valBytes
case "FileHugePages:":
m.FileHugePages = &val
m.FileHugePagesBytes = &valBytes
case "ShmemHugePages:":
m.ShmemHugePages = &val
m.ShmemHugePagesBytes = &valBytes
@@ -362,6 +389,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "CmaFree:":
m.CmaFree = &val
m.CmaFreeBytes = &valBytes
case "Unaccepted:":
m.Unaccepted = &val
m.UnacceptedBytes = &valBytes
case "HugePages_Total:":
m.HugePagesTotal = &val
case "HugePages_Free:":
@@ -373,6 +403,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
case "Hugepagesize:":
m.Hugepagesize = &val
m.HugepagesizeBytes = &valBytes
case "Hugetlb:":
m.Hugetlb = &val
m.HugetlbBytes = &valBytes
case "DirectMap4k:":
m.DirectMap4k = &val
m.DirectMap4kBytes = &valBytes

View File

@@ -101,6 +101,12 @@ type ProcStat struct {
RSS int
// Soft limit in bytes on the rss of the process.
RSSLimit uint64
// The address above which program text can run.
StartCode uint64
// The address below which program text can run.
EndCode uint64
// The address of the start (i.e., bottom) of the stack.
StartStack uint64
// CPU number last executed on.
Processor uint
// Real-time scheduling priority, a number in the range 1 to 99 for processes
@@ -177,9 +183,9 @@ func (p Proc) Stat() (ProcStat, error) {
&s.VSize,
&s.RSS,
&s.RSSLimit,
&ignoreUint64,
&ignoreUint64,
&ignoreUint64,
&s.StartCode,
&s.EndCode,
&s.StartStack,
&ignoreUint64,
&ignoreUint64,
&ignoreUint64,

116
vendor/github.com/prometheus/procfs/proc_statm.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
// Copyright 2025 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package procfs
import (
"os"
"strconv"
"strings"
"github.com/prometheus/procfs/internal/util"
)
// - https://man7.org/linux/man-pages/man5/proc_pid_statm.5.html
// ProcStatm Provides memory usage information for a process, measured in memory pages.
// Read from /proc/[pid]/statm.
type ProcStatm struct {
// The process ID.
PID int
// total program size (same as VmSize in status)
Size uint64
// resident set size (same as VmRSS in status)
Resident uint64
// number of resident shared pages (i.e., backed by a file)
Shared uint64
// text (code)
Text uint64
// library (unused since Linux 2.6; always 0)
Lib uint64
// data + stack
Data uint64
// dirty pages (unused since Linux 2.6; always 0)
Dt uint64
}
// NewStatm returns the current status information of the process.
// Deprecated: Use p.Statm() instead.
func (p Proc) NewStatm() (ProcStatm, error) {
return p.Statm()
}
// Statm returns the current memory usage information of the process.
func (p Proc) Statm() (ProcStatm, error) {
data, err := util.ReadFileNoStat(p.path("statm"))
if err != nil {
return ProcStatm{}, err
}
statmSlice, err := parseStatm(data)
if err != nil {
return ProcStatm{}, err
}
procStatm := ProcStatm{
PID: p.PID,
Size: statmSlice[0],
Resident: statmSlice[1],
Shared: statmSlice[2],
Text: statmSlice[3],
Lib: statmSlice[4],
Data: statmSlice[5],
Dt: statmSlice[6],
}
return procStatm, nil
}
// parseStatm return /proc/[pid]/statm data to uint64 slice.
func parseStatm(data []byte) ([]uint64, error) {
var statmSlice []uint64
statmItems := strings.Fields(string(data))
for i := 0; i < len(statmItems); i++ {
statmItem, err := strconv.ParseUint(statmItems[i], 10, 64)
if err != nil {
return nil, err
}
statmSlice = append(statmSlice, statmItem)
}
return statmSlice, nil
}
// SizeBytes returns the process of total program size in bytes.
func (s ProcStatm) SizeBytes() uint64 {
return s.Size * uint64(os.Getpagesize())
}
// ResidentBytes returns the process of resident set size in bytes.
func (s ProcStatm) ResidentBytes() uint64 {
return s.Resident * uint64(os.Getpagesize())
}
// SHRBytes returns the process of share memory size in bytes.
func (s ProcStatm) SHRBytes() uint64 {
return s.Shared * uint64(os.Getpagesize())
}
// TextBytes returns the process of text (code) size in bytes.
func (s ProcStatm) TextBytes() uint64 {
return s.Text * uint64(os.Getpagesize())
}
// DataBytes returns the process of data + stack size in bytes.
func (s ProcStatm) DataBytes() uint64 {
return s.Data * uint64(os.Getpagesize())
}

View File

@@ -13,6 +13,7 @@ go:
- "1.12"
- "1.13"
- "1.14"
- "1.15"
script:
- ./validate.sh

View File

@@ -7,6 +7,15 @@ Go port of Coda Hale's Metrics library: <https://github.com/dropwizard/metrics>.
Documentation: <http://godoc.org/github.com/rcrowley/go-metrics>.
Archived as of April 1 2025
-----
This repository is no longer maintained. The authors recommend you explore the
following newer, more widely adopted libraries for your Go instrumentation
needs:
* [OpenTelemetry Go SDK](https://opentelemetry.io/docs/languages/go/instrumentation/#metrics)
* [Prometheus Go Client Library](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus)
Usage
-----

View File

@@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd netbsd openbsd
// +build darwin dragonfly freebsd netbsd openbsd hurd
// +build !js
package logrus

View File

@@ -1,5 +1,7 @@
//go:build (linux || aix || zos) && !js && !wasi
// +build linux aix zos
// +build !js
// +build !wasi
package logrus

View File

@@ -0,0 +1,8 @@
//go:build wasi
// +build wasi
package logrus
func isTerminal(fd int) bool {
return false
}

View File

@@ -0,0 +1,8 @@
//go:build wasip1
// +build wasip1
package logrus
func isTerminal(fd int) bool {
return false
}

View File

@@ -143,8 +143,9 @@ type ParseErrorsAllowlist struct {
UnknownFlags bool
}
// DEPRECATED: please use ParseErrorsAllowlist instead
// This type will be removed in a future release
// ParseErrorsWhitelist defines the parsing errors that can be ignored.
//
// Deprecated: use [ParseErrorsAllowlist] instead. This type will be removed in a future release.
type ParseErrorsWhitelist = ParseErrorsAllowlist
// NormalizedName is a flag name that has been normalized according to rules
@@ -165,8 +166,9 @@ type FlagSet struct {
// ParseErrorsAllowlist is used to configure an allowlist of errors
ParseErrorsAllowlist ParseErrorsAllowlist
// DEPRECATED: please use ParseErrorsAllowlist instead
// This field will be removed in a future release
// ParseErrorsAllowlist is used to configure an allowlist of errors.
//
// Deprecated: use [FlagSet.ParseErrorsAllowlist] instead. This field will be removed in a future release.
ParseErrorsWhitelist ParseErrorsAllowlist
name string
@@ -1185,7 +1187,7 @@ func (f *FlagSet) Parse(arguments []string) error {
case ContinueOnError:
return err
case ExitOnError:
if errors.Is(err, ErrHelp) {
if err == ErrHelp {
os.Exit(0)
}
fmt.Fprintln(f.Output(), err)
@@ -1214,7 +1216,7 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string)
case ContinueOnError:
return err
case ExitOnError:
if errors.Is(err, ErrHelp) {
if err == ErrHelp {
os.Exit(0)
}
fmt.Fprintln(f.Output(), err)