cosign

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2024 License: MIT Imports: 5 Imported by: 0

README

COSIGN

cosign-25.png

A go-lang library that allows multiple signers of a single JWT. Done in a way that maintains backward compatibility with standard single signer verification.

Usage

Take any existing JWT, parse it, and add one or more additional signers.

jwtString := "ey...===="
token, err := cosign.parse(jwtString
doubleSignedToken, err := token.AddSigner("kid1234", secondSignerPrivateKey, jwt.SigningMethodES256)
doubleSignedTokenString := doubleSignedToken.Token()
fmt.Printf("The cosigned token: %s", doubleSignedTokenString)

The "signing method" must match the private key usage (RS256 for SHA245 + RSA, ES256 for SHA256+P256, etc.).

Verification can be performed in a variety of ways. The last signer can always be verified using standard JWT verification. The cosigners can be verified by first parsing the JWT with cosign.Parse(token) and then using one of the verify functions:

  • Verify(pubKeyProvider) - Verify the current token is signed by a known public key.
  • VerifyOne(pubKeyProvider) - Verify that the token was signed by at least one known public key.
  • VerifyAll(pubKeyProvider) - Verify that the toke was signed by only known public keys.
token, err := cosign.Parse(doubleSignedTokenString)
pubKeyProvider := cosign.MapPubKeyProvider{
	"kid1234": &secondSignerPrivateKey.PublicKey
}
result := token.VerifyOne(pubKeyProvider)

if result.Error {
	fmt.Printf("Error: %s\n", result.Error)
} else {
	fmt.Printf("Is Valid: %b\n", result.IsValid)
}

Each single cosigned token is logically multiple individual tokens signed with different private keys. Cosign "unpacks" these JWTs during parsing. The logical tokens can be accessed via: token.Previous() or token.Tokens().

When using token.VerifyOne() or token.VerifyAll() the referenced token and all previous tokens are verified, but any tokens higher up, are not. For example, if an arbitrary token named myToken was signed three times, using cosign.Parse(myToken) will return a single Token instance that has references to two other token instances.

topToken, _ := cosign.Parse(myToken)
middleToken := topToken.Previous()
bottomToken := middleToken.Previous()
thisWillBeNil := bottomToken.Previous()

If middleToken.VerifyOne() is run, it will verify against middleToken and bottomToken; as verification is down through the token, not up.

Why?

I worked on a project where I needed to be able to have older clients verify a JWT from a single signer, but newer clients could verify from any number of signers. Issuing multiple JWTs was too much to handle, and nesting JWTs inside each other caused the JWT size to grow too large for my use case. For the initial application, the claims were identical, no matter how many signers there were. Cosign requires that the claim set not change between signers. Said another way, all cosigners sign the same claims.

Documentation

Index

Constants

View Source
const (
	HeaderAttr         = "hdr"
	SignatureAttr      = "sig"
	VersionAttr        = "cos"
	PrevsiouSignerAttr = "pre"
)
View Source
const CoSignerV1 = "v1"

Variables

This section is empty.

Functions

This section is empty.

Types

type Header struct {
	KeyId           string  `json:"kid,omitempty"`
	Algorithm       string  `json:"alg,omitempty"`
	CoSignerVersion string  `json:"cos,omitempty"`
	PreviousSigner  *Signer `json:"pre,omitempty"`
}

func (*Header) Encode

func (h *Header) Encode() ([]byte, []byte, error)

type MapKeyProvider

type MapKeyProvider map[string]crypto.PublicKey

func (MapKeyProvider) GetPublicKey

func (m MapKeyProvider) GetPublicKey(kid string) (crypto.PublicKey, error)

type PublicKeyProvider

type PublicKeyProvider interface {
	GetPublicKey(kid string) (crypto.PublicKey, error)
}

type Signer

type Signer struct {
	HeaderEncoded    string `json:"hdr,omitempty"`
	SignatureEncoded string `json:"sig,omitempty"`
}

type StringToken

type StringToken struct {
	HeaderEncoded string
	HeaderBytes   []byte
	Header        *Header

	ClaimsEncoded string
	ClaimsBytes   []byte

	SignatureEncoded string
	SignatureBytes   []byte

	TokenEncoded string

	PreviousToken Token
}

func (*StringToken) AddSigner

func (s *StringToken) AddSigner(kid string, privateKey crypto.PrivateKey, signingMethod jwt.SigningMethod) (Token, error)

func (*StringToken) AssemblePreviousToken

func (s *StringToken) AssemblePreviousToken() (Token, error)

func (*StringToken) Claims added in v1.0.2

func (s *StringToken) Claims() []byte

func (*StringToken) Kid

func (s *StringToken) Kid() string

func (*StringToken) Kids

func (s *StringToken) Kids() []string

func (*StringToken) Previous

func (s *StringToken) Previous() Token

func (*StringToken) Token

func (s *StringToken) Token() string

func (*StringToken) Tokens

func (s *StringToken) Tokens() ([]string, []Token)

func (*StringToken) Verify

func (s *StringToken) Verify(publicKeyProvider PublicKeyProvider) *VerifyResult

func (*StringToken) VerifyAll

func (s *StringToken) VerifyAll(publicKeyProvider PublicKeyProvider) (bool, []*VerifyResult)

func (*StringToken) VerifyOne

func (s *StringToken) VerifyOne(publicKeyProvider PublicKeyProvider) *VerifyResult

type Token

type Token interface {
	AddSigner(kid string, privateKey crypto.PrivateKey, signingMethod jwt.SigningMethod) (Token, error)
	Kids() []string
	Kid() string
	Previous() Token
	Verify(publicKeyProvider PublicKeyProvider) *VerifyResult
	VerifyAll(publicKeyProvider PublicKeyProvider) (bool, []*VerifyResult)
	VerifyOne(publicKeyProvider PublicKeyProvider) *VerifyResult
	Tokens() ([]string, []Token)
	Token() string
	AssemblePreviousToken() (Token, error)
	Claims() []byte
}

func Parse

func Parse(token string) (Token, error)

type VerifyResult

type VerifyResult struct {
	Token   Token
	IsValid bool
	Error   error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL