github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/pkiverifier/pkiverifier.go (about)

     1  package pkiverifier
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/x509"
     7  	"errors"
     8  	"math/big"
     9  	"time"
    10  
    11  	jwt "github.com/dgrijalva/jwt-go"
    12  	"go.aporeto.io/enforcerd/trireme-lib/utils/cache"
    13  	"go.uber.org/zap"
    14  )
    15  
    16  const (
    17  	// defaultValidity is the default cache validity in seconds
    18  	defaultValidity = 1
    19  )
    20  
    21  // PKITokenIssuer is the interface of an object that can issue a PKI token.
    22  type PKITokenIssuer interface {
    23  	CreateTokenFromCertificate(*x509.Certificate, []string) ([]byte, error)
    24  }
    25  
    26  // PKITokenVerifier is the interface of an object that can verify a PKI token.
    27  type PKITokenVerifier interface {
    28  	Verify([]byte) (*DatapathKey, error)
    29  }
    30  
    31  type verifierClaims struct {
    32  	X    *big.Int
    33  	Y    *big.Int
    34  	Tags []string `json:"tags,omitempty"`
    35  	jwt.StandardClaims
    36  }
    37  
    38  // PKIControllerInfo holds the controller information about public keys
    39  type PKIControllerInfo struct {
    40  	Namespace      string // The namespace of the public key.
    41  	Controller     string // The controller or control plane of the public key.
    42  	SameController bool   // Does the public key come from the same controller
    43  }
    44  
    45  // PKIPublicKey holds information about public keys
    46  type PKIPublicKey struct {
    47  	PublicKey  *ecdsa.PublicKey
    48  	Controller *PKIControllerInfo
    49  }
    50  
    51  type tokenManager struct {
    52  	publicKeys []*PKIPublicKey
    53  	privateKey *ecdsa.PrivateKey
    54  	signMethod jwt.SigningMethod
    55  	keycache   cache.DataStore
    56  	validity   time.Duration
    57  }
    58  
    59  // DatapathKey holds the data path key with the corresponding claims.
    60  type DatapathKey struct {
    61  	PublicKey  *ecdsa.PublicKey
    62  	Tags       []string
    63  	Expiration time.Time
    64  	Controller *PKIControllerInfo
    65  }
    66  
    67  // NewPKIIssuer initializes a new signer structure
    68  func NewPKIIssuer(privateKey *ecdsa.PrivateKey) PKITokenIssuer {
    69  
    70  	return &tokenManager{
    71  		privateKey: privateKey,
    72  		signMethod: jwt.SigningMethodES256,
    73  	}
    74  }
    75  
    76  // NewPKIVerifier returns a new PKIConfiguration.
    77  func NewPKIVerifier(publicKeys []*PKIPublicKey, cacheValidity time.Duration) PKITokenVerifier {
    78  
    79  	validity := defaultValidity * time.Second
    80  	if cacheValidity > 0 {
    81  		validity = cacheValidity
    82  	}
    83  
    84  	return &tokenManager{
    85  		publicKeys: publicKeys,
    86  		signMethod: jwt.SigningMethodES256,
    87  		keycache:   cache.NewCacheWithExpiration("PKIVerifierKey", validity),
    88  		validity:   validity,
    89  	}
    90  }
    91  
    92  // Verify verifies a token and returns the public key
    93  func (p *tokenManager) Verify(token []byte) (*DatapathKey, error) {
    94  
    95  	tokenString := string(token)
    96  	if pk, err := p.keycache.Get(tokenString); err == nil {
    97  		return pk.(*DatapathKey), err
    98  	}
    99  
   100  	claims := &verifierClaims{}
   101  	var t *jwt.Token
   102  	var err error
   103  	for _, pk := range p.publicKeys {
   104  
   105  		if pk == nil || pk.PublicKey == nil {
   106  			continue
   107  		}
   108  
   109  		t, err = jwt.ParseWithClaims(tokenString, claims, func(*jwt.Token) (interface{}, error) { // nolint
   110  			return pk.PublicKey, nil
   111  		})
   112  		if err != nil || !t.Valid {
   113  			continue
   114  		}
   115  
   116  		expTime := time.Unix(claims.ExpiresAt, 0)
   117  		dp := &DatapathKey{
   118  			PublicKey: &ecdsa.PublicKey{
   119  				Curve: elliptic.P256(),
   120  				X:     claims.X,
   121  				Y:     claims.Y,
   122  			},
   123  			Tags:       claims.Tags,
   124  			Expiration: expTime,
   125  			Controller: pk.Controller,
   126  		}
   127  
   128  		p.keycache.AddOrUpdate(tokenString, dp)
   129  
   130  		// if the token expires before our default validity, update the timer
   131  		// so that we expire it no longer than its validity.
   132  		if time.Now().Add(p.validity).After(expTime) {
   133  			if err := p.keycache.SetTimeOut(tokenString, time.Until(expTime)); err != nil {
   134  				zap.L().Warn("Failed to update cache validity for token", zap.Error(err))
   135  			}
   136  		}
   137  
   138  		return dp, nil
   139  	}
   140  
   141  	return nil, errors.New("unable to verify token against any available public key")
   142  }
   143  
   144  // CreateTokenFromCertificate creates and signs a token
   145  func (p *tokenManager) CreateTokenFromCertificate(cert *x509.Certificate, tags []string) ([]byte, error) {
   146  
   147  	// Combine the application claims with the standard claims
   148  	claims := &verifierClaims{
   149  		X:    cert.PublicKey.(*ecdsa.PublicKey).X,
   150  		Y:    cert.PublicKey.(*ecdsa.PublicKey).Y,
   151  		Tags: tags,
   152  	}
   153  	claims.ExpiresAt = cert.NotAfter.Unix()
   154  
   155  	// Create the token and sign with our key
   156  	strtoken, err := jwt.NewWithClaims(p.signMethod, claims).SignedString(p.privateKey)
   157  	if err != nil {
   158  		return []byte{}, err
   159  	}
   160  
   161  	return []byte(strtoken), nil
   162  }