Files
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

186 lines
5.6 KiB
Go

package jws
import (
"fmt"
"strings"
"sync"
"github.com/lestrrat-go/jwx/v3/jwa"
)
// Signer2 is an interface that represents a per-signature algorithm signing
// operation.
type Signer2 interface {
Algorithm() jwa.SignatureAlgorithm
// Sign takes a key and a payload, and returns the signature for the payload.
// The key type is restricted by the signature algorithm that this
// signer is associated with.
//
// (Note to users of legacy Signer interface: the method signature
// is different from the legacy Signer interface)
Sign(key any, payload []byte) ([]byte, error)
}
var muSigner2DB sync.RWMutex
var signer2DB = make(map[jwa.SignatureAlgorithm]Signer2)
type SignerFactory interface {
Create() (Signer, error)
}
type SignerFactoryFn func() (Signer, error)
func (fn SignerFactoryFn) Create() (Signer, error) {
return fn()
}
func init() {
// register the signers using jwsbb. These will be used by default.
for _, alg := range jwa.SignatureAlgorithms() {
if alg == jwa.NoSignature() {
continue
}
if err := RegisterSigner(alg, defaultSigner{alg: alg}); err != nil {
panic(fmt.Sprintf("RegisterSigner failed: %v", err))
}
}
}
// SignerFor returns a Signer2 for the given signature algorithm.
//
// Currently, this function will never fail. It will always return a
// valid Signer2 object. The heuristic is as follows:
// 1. If a Signer2 is registered for the given algorithm, it will return that.
// 2. If a legacy Signer(Factory) is registered for the given algorithm, it will
// return a Signer2 that wraps the legacy Signer.
// 3. If no Signer2 or legacy Signer(Factory) is registered, it will return a
// default signer that uses jwsbb.Sign.
//
// 1 and 2 will take care of 99% of the cases. The only time 3 will happen is
// when you are using a custom algorithm that is not supported out of the box.
//
// jwsbb.Sign knows how to handle a static set of algorithms, so if the
// algorithm is not supported, it will return an error when you call
// `Sign` on the default signer.
func SignerFor(alg jwa.SignatureAlgorithm) (Signer2, error) {
muSigner2DB.RLock()
defer muSigner2DB.RUnlock()
signer, ok := signer2DB[alg]
if ok {
return signer, nil
}
s1, err := legacySignerFor(alg)
if err == nil {
return signerAdapter{signer: s1}, nil
}
return defaultSigner{alg: alg}, nil
}
var muSignerDB sync.RWMutex
var signerDB = make(map[jwa.SignatureAlgorithm]SignerFactory)
// RegisterSigner is used to register a signer for the given
// algorithm.
//
// Please note that this function is intended to be passed a
// signer object as its second argument, but due to historical
// reasons the function signature is defined as taking `any` type.
//
// You should create a signer object that implements the `Signer2`
// interface to register a signer, unless you have legacy code that
// plugged into the `SignerFactory` interface.
//
// Unlike the `UnregisterSigner` function, this function automatically
// calls `jwa.RegisterSignatureAlgorithm` to register the algorithm
// in this module's algorithm database.
//
// For backwards compatibility, this function also accepts
// `SignerFactory` implementations, but this usage is deprecated.
// You should use `Signer2` implementations instead.
//
// If you want to completely remove an algorithm, you must call
// `jwa.UnregisterSignatureAlgorithm` yourself after calling
// `UnregisterSigner`.
func RegisterSigner(alg jwa.SignatureAlgorithm, f any) error {
jwa.RegisterSignatureAlgorithm(alg)
switch s := f.(type) {
case Signer2:
muSigner2DB.Lock()
signer2DB[alg] = s
muSigner2DB.Unlock()
case SignerFactory:
muSignerDB.Lock()
signerDB[alg] = s
muSignerDB.Unlock()
default:
return fmt.Errorf(`jws.RegisterSigner: unsupported type %T for algorithm %q`, f, alg)
}
return nil
}
// UnregisterSigner removes the signer factory associated with
// the given algorithm, as well as the signer instance created
// by the factory.
//
// Note that when you call this function, the algorithm itself is
// not automatically unregistered from this module's algorithm database.
// This is because the algorithm may still be required for verification or
// some other operation (however unlikely, it is still possible).
// Therefore, in order to completely remove the algorithm, you must
// call `jwa.UnregisterSignatureAlgorithm` yourself.
func UnregisterSigner(alg jwa.SignatureAlgorithm) {
muSigner2DB.Lock()
delete(signer2DB, alg)
muSigner2DB.Unlock()
muSignerDB.Lock()
delete(signerDB, alg)
muSignerDB.Unlock()
// Remove previous signer
removeSigner(alg)
}
// NewSigner creates a signer that signs payloads using the given signature algorithm.
// This function is deprecated, and will either be removed to re-purposed using
// a different signature.
//
// When you want to load a Signer object, you should use `SignerFor()` instead.
func NewSigner(alg jwa.SignatureAlgorithm) (Signer, error) {
s, err := newLegacySigner(alg)
if err == nil {
return s, nil
}
if strings.HasPrefix(err.Error(), `jws.NewSigner: unsupported signature algorithm`) {
// When newLegacySigner fails, automatically trigger to enable signers
enableLegacySignersOnce.Do(enableLegacySigners)
return newLegacySigner(alg)
}
return nil, err
}
func newLegacySigner(alg jwa.SignatureAlgorithm) (Signer, error) {
muSignerDB.RLock()
f, ok := signerDB[alg]
muSignerDB.RUnlock()
if ok {
return f.Create()
}
return nil, fmt.Errorf(`jws.NewSigner: unsupported signature algorithm "%s"`, alg)
}
type noneSigner struct{}
func (noneSigner) Algorithm() jwa.SignatureAlgorithm {
return jwa.NoSignature()
}
func (noneSigner) Sign([]byte, any) ([]byte, error) {
return nil, nil
}