Allow specifying old signing keys with the public key and key ID only
If the private key file is lost, it's often possible to retrieve the
public key from another server elsewhere, so we should make it possible
to configure it in that way.
MODIFY
dendrite-sample.monolith.yaml
dendrite-sample.monolith.yaml
@@ -18,12 +18,17 @@ global:
private_key: matrix_key.pem
# The paths and expiry timestamps (as a UNIX timestamp in millisecond precision)
- # to old signing private keys that were formerly in use on this domain. These
+ # to old signing keys that were formerly in use on this domain name. These
# keys will not be used for federation request or event signing, but will be
# provided to any other homeserver that asks when trying to verify old events.
old_private_keys:
+ # If the old private key file is available:
# - private_key: old_matrix_key.pem
# expired_at: 1601024554498
+ # If only the public key (in base64 format) and key ID are known:
+ # - public_key: mn59Kxfdq9VziYHSBzI7+EDPDcBS2Xl7jeUdiiQcOnM=
+ # key_id: ed25519:mykeyid
+ # expired_at: 1601024554498
# How long a remote server can cache our server signing key before requesting it
# again. Increasing this number will reduce the number of requests made by other

MODIFY
dendrite-sample.polylith.yaml
dendrite-sample.polylith.yaml
@@ -18,12 +18,17 @@ global:
private_key: matrix_key.pem
# The paths and expiry timestamps (as a UNIX timestamp in millisecond precision)
- # to old signing private keys that were formerly in use on this domain. These
+ # to old signing keys that were formerly in use on this domain name. These
# keys will not be used for federation request or event signing, but will be
# provided to any other homeserver that asks when trying to verify old events.
old_private_keys:
+ # If the old private key file is available:
# - private_key: old_matrix_key.pem
# expired_at: 1601024554498
+ # If only the public key (in base64 format) and key ID are known:
+ # - public_key: mn59Kxfdq9VziYHSBzI7+EDPDcBS2Xl7jeUdiiQcOnM=
+ # key_id: ed25519:mykeyid
+ # expired_at: 1601024554498
# How long a remote server can cache our server signing key before requesting it
# again. Increasing this number will reduce the number of requests made by other

MODIFY
federationapi/routing/keys.go
federationapi/routing/keys.go
@@ -160,7 +160,7 @@ func localKeys(cfg *config.FederationAPI, validUntil time.Time) (*gomatrixserver
for _, oldVerifyKey := range cfg.Matrix.OldVerifyKeys {
keys.OldVerifyKeys[oldVerifyKey.KeyID] = gomatrixserverlib.OldVerifyKey{
VerifyKey: gomatrixserverlib.VerifyKey{
- Key: gomatrixserverlib.Base64Bytes(oldVerifyKey.PrivateKey.Public().(ed25519.PublicKey)),
+ Key: oldVerifyKey.PublicKey,
},
ExpiredTS: oldVerifyKey.ExpiredAt,
}

MODIFY
setup/config/config.go
setup/config/config.go
@@ -231,13 +231,14 @@ func loadConfig(
return nil, err
}
- for i, oldPrivateKey := range c.Global.OldVerifyKeys {
+ for _, key := range c.Global.OldVerifyKeys {
+ switch {
+ case key.PrivateKeyPath != "":
var oldPrivateKeyData []byte
-
- oldPrivateKeyPath := absPath(basePath, oldPrivateKey.PrivateKeyPath)
+ oldPrivateKeyPath := absPath(basePath, key.PrivateKeyPath)
oldPrivateKeyData, err = readFile(oldPrivateKeyPath)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to read %q: %w", oldPrivateKeyPath, err)
}
// NOTSPEC: Ordinarily we should enforce key ID formatting, but since there are
@@ -245,10 +246,25 @@ func loadConfig(
// to lack of validation in Synapse, we won't enforce that for old verify keys.
keyID, privateKey, perr := readKeyPEM(oldPrivateKeyPath, oldPrivateKeyData, false)
if perr != nil {
- return nil, perr
+ return nil, fmt.Errorf("failed to parse %q: %w", oldPrivateKeyPath, perr)
}
- c.Global.OldVerifyKeys[i].KeyID, c.Global.OldVerifyKeys[i].PrivateKey = keyID, privateKey
+ key.KeyID = keyID
+ key.PrivateKey = privateKey
+ key.PublicKey = gomatrixserverlib.Base64Bytes(privateKey.Public().(ed25519.PublicKey))
+
+ case key.KeyID == "":
+ return nil, fmt.Errorf("'key_id' must be specified if 'public_key' is specified")
+
+ case len(key.PublicKey) == ed25519.PublicKeySize:
+ continue
+
+ case len(key.PublicKey) > 0:
+ return nil, fmt.Errorf("the supplied 'public_key' is the wrong length")
+
+ default:
+ return nil, fmt.Errorf("either specify a 'private_key' path or supply both 'public_key' and 'key_id'")
+ }
}
c.MediaAPI.AbsBasePath = Path(absPath(basePath, c.MediaAPI.BasePath))

MODIFY
setup/config/config_global.go
setup/config/config_global.go
@@ -27,7 +27,7 @@ type Global struct {
// Information about old private keys that used to be used to sign requests and
// events on this domain. They will not be used but will be advertised to other
// servers that ask for them to help verify old events.
- OldVerifyKeys []OldVerifyKeys `yaml:"old_private_keys"`
+ OldVerifyKeys []*OldVerifyKeys `yaml:"old_private_keys"`
// How long a remote server can cache our server key for before requesting it again.
// Increasing this number will reduce the number of requests made by remote servers
@@ -127,8 +127,11 @@ type OldVerifyKeys struct {
// The private key itself.
PrivateKey ed25519.PrivateKey `yaml:"-"`
+ // The public key, in case only that part is known.
+ PublicKey gomatrixserverlib.Base64Bytes `yaml:"public_key"`
+
// The key ID of the private key.
- KeyID gomatrixserverlib.KeyID `yaml:"-"`
+ KeyID gomatrixserverlib.KeyID `yaml:"key_id"`
// When the private key was designed as "expired", as a UNIX timestamp
// in millisecond precision.

Allow specifying old signing keys with the public key and key ID only (#2770)

If the private key file is lost, it's often possible to retrieve the
public key from another server elsewhere, so we should make it possible
to configure it in that way.