siyuan-note/siyuan · error

invalid historical notebook encryption key

Error message

invalid historical notebook encryption key

What it means

During decryption of historical key-encryption-keys (KEKs), a KEK successfully authenticated via AAD but the resulting plaintext key was not exactly 32 bytes. This indicates the stored wrapped history key blob is malformed or was produced by an incompatible format, so the derived historical key is rejected instead of being used.

Source

Thrown at kernel/model/crypto_history_keys.go:44

const historyKEKAAD = "siyuan:history-kek:v1"

// decryptHistoryKEKs 只在当前配置已经完成主密码认证后使用,返回值由调用方在使用结束时清零。
func decryptHistoryKEKs(kek []byte, wrappedKeys [][]byte) (keys [][]byte, err error) {
	defer func() {
		if err != nil {
			clearHistoryKEKs(keys)
			keys = nil
		}
	}()
	for _, wrapped := range wrappedKeys {
		key, decryptErr := util.DecryptWithAAD(kek, wrapped, []byte(historyKEKAAD))
		if decryptErr != nil {
			return keys, decryptErr
		}
		if len(key) != 32 {
			zeroAndClear(key)
			return keys, errors.New("invalid historical notebook encryption key")
		}
		keys = append(keys, key)
	}
	return keys, nil
}

func clearHistoryKEKs(keys [][]byte) {
	for _, key := range keys {
		zeroAndClear(key)
	}
}

// rewrapHistoryKEKs 将历次 KEK 和本次旧 KEK 一起封装到新 KEK 下,恢复不依赖历史所在设备。
func rewrapHistoryKEKs(oldKEK, newKEK []byte, wrappedKeys [][]byte) (ret [][]byte, err error) {
	keys, err := decryptHistoryKEKs(oldKEK, wrappedKeys)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Restore the affected encrypted notebook's key/metadata files from a known-good backup or sync snapshot.
  2. Verify the master password is correct — a wrong password should fail AAD authentication before this check, so if you get here the blob itself is likely truncated.
  3. Re-wrap history keys by re-entering the master password through the official migration flow rather than editing key files manually.
  4. If reproducible, report it as data corruption; the 32-byte length invariant must always hold for valid envelopes.
Defensive patterns

Strategy: fallback

Try / catch

keys, err := decryptHistoryKEKs(masterKey, wrappedList)
if err != nil {
    return fmt.Errorf("history keys unusable, restore from backup: %w", err)
}

Prevention

When it happens

Trigger: decryptHistoryKEKs decrypts a wrapped history KEK with util.DecryptWithAAD and the decrypted length differs from 32 bytes; reached via deriveKEK, rewrapHistoryKEKs, decryptWrappedDEKWithHistory, or the history-key tests.

Common situations: Hand-edited or partially truncated key envelope files; data restored from an old backup produced by an earlier envelope format; corruption during sync merging key metadata.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/ec69951a0c43006e. Report an issue: GitHub.