siyuan-note/siyuan · critical

316

316

Error message

Decryption failed: incorrect key or corrupted data

What it means

Thrown by deriveNotebookCryptoBackupCandidate (crypto.go:1187, i18n code 316) when the KEK passed the verifier magic check but the backup fails integrity authentication: the Spec is not CurrentNotebookCryptoSpec, Checksum is empty, KEKMAC is empty, or verifyKEKMAC fails. A backup that decrypts but does not authenticate is treated as corrupted or tampered, so it is rejected to prevent using untrusted key material.

Source

Thrown at kernel/model/crypto.go:1187

func deriveNotebookCryptoBackupCandidate(password string) (backup *conf.NotebookCrypto, kek []byte, err error) {
	backup, err = loadNotebookCryptoBackup()
	if err != nil || backup == nil || len(backup.MasterSalt) == 0 || len(backup.KEKVerifier) == 0 {
		return nil, nil, errors.New(Conf.Language(310))
	}
	params, validErr := util.ValidateArgon2Params(backup.KDFParams)
	if validErr != nil {
		return nil, nil, errors.New(Conf.Language(317))
	}
	kek = util.DeriveKey(password, backup.MasterSalt, params)
	decrypted, decryptErr := util.DecryptWithAAD(kek, backup.KEKVerifier, []byte("siyuan:kek-verifier"))
	if decryptErr != nil || string(decrypted) != string(kekVerifierMagic) {
		zeroAndClear(kek)
		return nil, nil, errors.New(Conf.Language(311))
	}
	if backup.Spec != conf.CurrentNotebookCryptoSpec || backup.Checksum == "" ||
		len(backup.KEKMAC) == 0 || !verifyKEKMAC(backup, kek) {
		zeroAndClear(kek)
		return nil, nil, errors.New(Conf.Language(316))
	}
	if !verifyKEKAgainstExistingBoxes(kek) || !verifyKEKAgainstEncryptedHistory(kek) {
		zeroAndClear(kek)
		return nil, nil, errors.New(Conf.Language(316))
	}
	backup.KDFParams = params
	return backup, kek, nil
}

// deriveKEK 从主密码派生 KEK 并校验。校验失败返回错误。KEK 仅在函数作用域内有效,调用方负责使用。
func deriveKEK(password string) ([]byte, error) {
	Conf.m.RLock()
	nc := *Conf.NotebookCrypto
	Conf.m.RUnlock()

	if !nc.Enabled {
		// 本机未启用:可能是数据同步到新设备后本机 conf.json 还没有加密配置。
		// 尝试从 DataDir 备份恢复(备份会随 DataDir 同步过来);恢复成功时直接复用其派生的 KEK。

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Restore a complete, spec-compatible backup from a known-good sync snapshot or another device.
  2. Upgrade/downgrade the kernel to a version whose CurrentNotebookCryptoSpec matches the backup, if the backup is intentionally from another spec era.
  3. If the local conf.json authenticates instead (localAuthenticated path in deriveKEK), prefer it and let the model repair the backup automatically.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm backup integrity before recovery.
b, err := loadNotebookCryptoBackup()
if err != nil || b == nil {
    return errors.New("no backup")
}
if b.Spec != conf.CurrentNotebookCryptoSpec || b.Checksum == "" || len(b.KEKMAC) == 0 {
    return errors.New("backup fails spec/checksum/MAC; restore a complete backup")
}

Try / catch

if err := model.UnlockBox(boxID, password, boxCrypt); err != nil {
    if err.Error() == model.Conf.Language(316) {
        respond(c, "key backup failed integrity check; restore a trusted backup")
        return
    }
    respond(c, err.Error())
}

Prevention

When it happens

Trigger: deriveNotebookCryptoBackupCandidate: backup.Spec != conf.CurrentNotebookCryptoSpec, backup.Checksum == "", len(backup.KEKMAC) == 0, or the HMAC computed over the backup does not equal backup.KEKMAC under the derived KEK.

Common situations: Backup from an incompatible spec version (upgrade/downgrade across format changes). Backup truncated or bit-flipped (MAC mismatch). Backup hand-edited. Sync delivered a partially-updated backup.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/1cf9d0e9301cf09c. Report an issue: GitHub.