···11+// Copyright 2024 The Forgejo Authors. All rights reserved.
22+// SPDX-License-Identifier: MIT
33+44+// Keying is a module that allows for subkeys to be determistically generated
55+// from the same master key. It allows for domain seperation to take place by
66+// using new keys for new subsystems/domains. These subkeys are provided with
77+// an API to encrypt and decrypt data. The module panics if a bad interaction
88+// happened, the panic should be seen as an non-recoverable error.
99+//
1010+// HKDF (per RFC 5869) is used to derive new subkeys in a safe manner. It
1111+// provides a KDF security property, which is required for Forgejo, as the
1212+// secret key would be an ASCII string and isn't a random uniform bit string.
1313+// XChaCha-Poly1305 (per draft-irtf-cfrg-xchacha-01) is used as AEAD to encrypt
1414+// and decrypt messages. A new fresh random nonce is generated for every
1515+// encryption. The nonce gets prepended to the ciphertext.
1616+package keying
1717+1818+import (
1919+ "crypto/rand"
2020+ "crypto/sha256"
2121+2222+ "golang.org/x/crypto/chacha20poly1305"
2323+ "golang.org/x/crypto/hkdf"
2424+)
2525+2626+var (
2727+ // The hash used for HKDF.
2828+ hash = sha256.New
2929+ // The AEAD used for encryption/decryption.
3030+ aead = chacha20poly1305.NewX
3131+ aeadKeySize = chacha20poly1305.KeySize
3232+ aeadNonceSize = chacha20poly1305.NonceSizeX
3333+ // The pseudorandom key generated by HKDF-Extract.
3434+ prk []byte
3535+)
3636+3737+// Set the main IKM for this module.
3838+func Init(ikm []byte) {
3939+ // Salt is intentionally left empty, it's not useful to Forgejo's use case.
4040+ prk = hkdf.Extract(hash, ikm, nil)
4141+}
4242+4343+// Specifies the context for which a subkey should be derived for.
4444+// This must be a hardcoded string and must not be arbitrarily constructed.
4545+type Context string
4646+4747+// Derive *the* key for a given context, this is a determistic function. The
4848+// same key will be provided for the same context.
4949+func DeriveKey(context Context) *Key {
5050+ if len(prk) == 0 {
5151+ panic("keying: not initialized")
5252+ }
5353+5454+ r := hkdf.Expand(hash, prk, []byte(context))
5555+5656+ key := make([]byte, aeadKeySize)
5757+ // This should never return an error, but if it does, panic.
5858+ if _, err := r.Read(key); err != nil {
5959+ panic(err)
6060+ }
6161+6262+ return &Key{key}
6363+}
6464+6565+type Key struct {
6666+ key []byte
6767+}
6868+6969+// Encrypts the specified plaintext with some additional data that is tied to
7070+// this plaintext. The additional data can be seen as the context in which the
7171+// data is being encrypted for, this is different than the context for which the
7272+// key was derrived this allows for more granuality without deriving new keys.
7373+// Avoid any user-generated data to be passed into the additional data. The most
7474+// common usage of this would be to encrypt a database field, in that case use
7575+// the ID and database column name as additional data. The additional data isn't
7676+// appended to the ciphertext and may be publicly known, it must be available
7777+// when decryping the ciphertext.
7878+func (k *Key) Encrypt(plaintext, additionalData []byte) []byte {
7979+ // Construct a new AEAD with the key.
8080+ e, err := aead(k.key)
8181+ if err != nil {
8282+ panic(err)
8383+ }
8484+8585+ // Generate a random nonce.
8686+ nonce := make([]byte, aeadNonceSize)
8787+ if _, err := rand.Read(nonce); err != nil {
8888+ panic(err)
8989+ }
9090+9191+ // Returns the ciphertext of this plaintext.
9292+ return e.Seal(nonce, nonce, plaintext, additionalData)
9393+}
9494+9595+// Decrypts the ciphertext and authenticates it against the given additional
9696+// data that was given when it was encrypted. It returns an error if the
9797+// authentication failed.
9898+func (k *Key) Decrypt(ciphertext, additionalData []byte) ([]byte, error) {
9999+ if len(ciphertext) <= aeadNonceSize {
100100+ panic("keying: ciphertext is too short")
101101+ }
102102+103103+ e, err := aead(k.key)
104104+ if err != nil {
105105+ panic(err)
106106+ }
107107+108108+ nonce, ciphertext := ciphertext[:aeadNonceSize], ciphertext[aeadNonceSize:]
109109+110110+ return e.Open(nil, nonce, ciphertext, additionalData)
111111+}
···10101111 "code.gitea.io/gitea/modules/auth/password/hash"
1212 "code.gitea.io/gitea/modules/generate"
1313+ "code.gitea.io/gitea/modules/keying"
1314 "code.gitea.io/gitea/modules/log"
1415)
1516···110111 // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
111112 SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
112113 }
114114+ keying.Init([]byte(SecretKey))
113115114116 CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
115117