mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Add EdDSA JWT signing algorithm (#16786)
* Add EdDSA signing algorithm * Fix typo Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							29b971b6d5
						
					
				
				
					commit
					28ac4a7a87
				
			@@ -392,7 +392,7 @@ INTERNAL_TOKEN=
 | 
			
		||||
;; Enables OAuth2 provider
 | 
			
		||||
ENABLE = true
 | 
			
		||||
;;
 | 
			
		||||
;; Algorithm used to sign OAuth2 tokens. Valid values: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
 | 
			
		||||
;; Algorithm used to sign OAuth2 tokens. Valid values: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, EdDSA
 | 
			
		||||
;JWT_SIGNING_ALGORITHM = RS256
 | 
			
		||||
;;
 | 
			
		||||
;; Private key file path used to sign OAuth2 tokens. The path is relative to APP_DATA_PATH.
 | 
			
		||||
 
 | 
			
		||||
@@ -546,7 +546,7 @@ func AccessTokenOAuth(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
	signingKey := oauth2.DefaultSigningKey
 | 
			
		||||
	if signingKey.IsSymmetric() {
 | 
			
		||||
		clientKey, err := oauth2.CreateJWTSingingKey(signingKey.SigningMethod().Alg(), []byte(form.ClientSecret))
 | 
			
		||||
		clientKey, err := oauth2.CreateJWTSigningKey(signingKey.SigningMethod().Alg(), []byte(form.ClientSecret))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			handleAccessTokenError(ctx, AccessTokenError{
 | 
			
		||||
				ErrorCode:        AccessTokenErrorCodeInvalidRequest,
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func createAndParseToken(t *testing.T, grant *models.OAuth2Grant) *oauth2.OIDCToken {
 | 
			
		||||
	signingKey, err := oauth2.CreateJWTSingingKey("HS256", make([]byte, 32))
 | 
			
		||||
	signingKey, err := oauth2.CreateJWTSigningKey("HS256", make([]byte, 32))
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.NotNil(t, signingKey)
 | 
			
		||||
	oauth2.DefaultSigningKey = signingKey
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ package oauth2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/ed25519"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
@@ -129,6 +130,57 @@ func (key rsaSingingKey) PreProcessToken(token *jwt.Token) {
 | 
			
		||||
	token.Header["kid"] = key.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type eddsaSigningKey struct {
 | 
			
		||||
	signingMethod jwt.SigningMethod
 | 
			
		||||
	key           ed25519.PrivateKey
 | 
			
		||||
	id            string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newEdDSASingingKey(signingMethod jwt.SigningMethod, key ed25519.PrivateKey) (eddsaSigningKey, error) {
 | 
			
		||||
	kid, err := createPublicKeyFingerprint(key.Public().(ed25519.PublicKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return eddsaSigningKey{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return eddsaSigningKey{
 | 
			
		||||
		signingMethod,
 | 
			
		||||
		key,
 | 
			
		||||
		base64.RawURLEncoding.EncodeToString(kid),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) IsSymmetric() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) SigningMethod() jwt.SigningMethod {
 | 
			
		||||
	return key.signingMethod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) SignKey() interface{} {
 | 
			
		||||
	return key.key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) VerifyKey() interface{} {
 | 
			
		||||
	return key.key.Public()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) ToJWK() (map[string]string, error) {
 | 
			
		||||
	pubKey := key.key.Public().(ed25519.PublicKey)
 | 
			
		||||
 | 
			
		||||
	return map[string]string{
 | 
			
		||||
		"alg": key.SigningMethod().Alg(),
 | 
			
		||||
		"kid": key.id,
 | 
			
		||||
		"kty": "OKP",
 | 
			
		||||
		"crv": "Ed25519",
 | 
			
		||||
		"x":   base64.RawURLEncoding.EncodeToString(pubKey),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (key eddsaSigningKey) PreProcessToken(token *jwt.Token) {
 | 
			
		||||
	token.Header["kid"] = key.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ecdsaSingingKey struct {
 | 
			
		||||
	signingMethod jwt.SigningMethod
 | 
			
		||||
	key           *ecdsa.PrivateKey
 | 
			
		||||
@@ -194,8 +246,8 @@ func createPublicKeyFingerprint(key interface{}) ([]byte, error) {
 | 
			
		||||
	return checksum[:], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateJWTSingingKey creates a signing key from an algorithm / key pair.
 | 
			
		||||
func CreateJWTSingingKey(algorithm string, key interface{}) (JWTSigningKey, error) {
 | 
			
		||||
// CreateJWTSigningKey creates a signing key from an algorithm / key pair.
 | 
			
		||||
func CreateJWTSigningKey(algorithm string, key interface{}) (JWTSigningKey, error) {
 | 
			
		||||
	var signingMethod jwt.SigningMethod
 | 
			
		||||
	switch algorithm {
 | 
			
		||||
	case "HS256":
 | 
			
		||||
@@ -218,11 +270,19 @@ func CreateJWTSingingKey(algorithm string, key interface{}) (JWTSigningKey, erro
 | 
			
		||||
		signingMethod = jwt.SigningMethodES384
 | 
			
		||||
	case "ES512":
 | 
			
		||||
		signingMethod = jwt.SigningMethodES512
 | 
			
		||||
	case "EdDSA":
 | 
			
		||||
		signingMethod = jwt.SigningMethodEdDSA
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, ErrInvalidAlgorithmType{algorithm}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch signingMethod.(type) {
 | 
			
		||||
	case *jwt.SigningMethodEd25519:
 | 
			
		||||
		privateKey, ok := key.(ed25519.PrivateKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, jwt.ErrInvalidKeyType
 | 
			
		||||
		}
 | 
			
		||||
		return newEdDSASingingKey(signingMethod, privateKey)
 | 
			
		||||
	case *jwt.SigningMethodECDSA:
 | 
			
		||||
		privateKey, ok := key.(*ecdsa.PrivateKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
@@ -271,6 +331,8 @@ func InitSigningKey() error {
 | 
			
		||||
	case "ES384":
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case "ES512":
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case "EdDSA":
 | 
			
		||||
		key, err = loadOrCreateAsymmetricKey()
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
@@ -278,10 +340,10 @@ func InitSigningKey() error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error while loading or creating symmetric key: %v", err)
 | 
			
		||||
		return fmt.Errorf("Error while loading or creating JWT key: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signingKey, err := CreateJWTSingingKey(setting.OAuth2.JWTSigningAlgorithm, key)
 | 
			
		||||
	signingKey, err := CreateJWTSigningKey(setting.OAuth2.JWTSigningAlgorithm, key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -324,10 +386,15 @@ func loadOrCreateAsymmetricKey() (interface{}, error) {
 | 
			
		||||
	if !isExist {
 | 
			
		||||
		err := func() error {
 | 
			
		||||
			key, err := func() (interface{}, error) {
 | 
			
		||||
				if strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS") {
 | 
			
		||||
				switch {
 | 
			
		||||
				case strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS"):
 | 
			
		||||
					return rsa.GenerateKey(rand.Reader, 4096)
 | 
			
		||||
				case setting.OAuth2.JWTSigningAlgorithm == "EdDSA":
 | 
			
		||||
					_, pk, err := ed25519.GenerateKey(rand.Reader)
 | 
			
		||||
					return pk, err
 | 
			
		||||
				default:
 | 
			
		||||
					return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
				}
 | 
			
		||||
				return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
			}()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user