mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-31 15:37:51 +01:00
fix(api): use a 256-bit API v1 signing key
The HS256 key was 22 base62 characters, about 128 bits, below the 256 bits RFC 7518 section 3.2 asks for. New keys are 32 bytes from crypto/rand, hex-encoded before being encrypted and stored. Keys already stored keep working unchanged.
This commit is contained in:
parent
b1cfa932be
commit
3e3b73a9cc
2 changed files with 27 additions and 2 deletions
|
|
@ -3,7 +3,9 @@ package apiauth
|
|||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
|
@ -15,7 +17,6 @@ import (
|
|||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/model/id"
|
||||
"github.com/navidrome/navidrome/utils"
|
||||
)
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ func loadSigner(ctx context.Context, ds model.DataStore, now func() time.Time) (
|
|||
}
|
||||
|
||||
func loadKey(ctx context.Context, ds model.DataStore) (string, error) {
|
||||
enc, err := utils.Encrypt(ctx, encryptionKey(), id.NewRandom())
|
||||
enc, err := utils.Encrypt(ctx, encryptionKey(), newKey())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("encrypting API v1 key: %w", err)
|
||||
}
|
||||
|
|
@ -98,6 +99,13 @@ func loadKey(ctx context.Context, ds model.DataStore) (string, error) {
|
|||
return key, nil
|
||||
}
|
||||
|
||||
// newKey returns 256 random bits, the minimum RFC 7518 asks of an HS256 key.
|
||||
func newKey() string {
|
||||
b := make([]byte, 32)
|
||||
_, _ = rand.Read(b) // never fails since Go 1.24
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
func encryptionKey() []byte {
|
||||
sum := sha256.Sum256([]byte(cmp.Or(conf.Server.PasswordEncryptionKey, consts.DefaultEncryptionKey)))
|
||||
return sum[:]
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ package apiauth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/jwt"
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
|
@ -23,6 +25,21 @@ var _ = Describe("signer", func() {
|
|||
Expect(realDS.Property().Delete(ctx, consts.JWTAPIv1SecretKey)).To(Or(Succeed(), MatchError(model.ErrNotFound)))
|
||||
})
|
||||
|
||||
It("creates a 256-bit key", func() {
|
||||
key, err := loadKey(ctx, realDS)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
raw, err := hex.DecodeString(key)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(raw).To(HaveLen(32))
|
||||
})
|
||||
|
||||
It("keeps using a stored key created in the older format", func() {
|
||||
enc, err := utils.Encrypt(ctx, encryptionKey(), "legacy22charskeyABCDEF")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(realDS.Property().Put(ctx, consts.JWTAPIv1SecretKey, enc)).To(Succeed())
|
||||
Expect(loadKey(ctx, realDS)).To(Equal("legacy22charskeyABCDEF"))
|
||||
})
|
||||
|
||||
It("round-trips claims", func() {
|
||||
s, err := loadSigner(ctx, realDS, clock)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue