siyuan-note/siyuan · error

invalid encrypted envelope magic

Error message

invalid encrypted envelope magic

What it means

EncryptionNonce parses SiYuan's SENC envelope format (magic 'SENC' + spec + algorithm + nonce length + nonce). The input does not start with the 4-byte 'SENC' magic, meaning it is not an encrypted envelope produced by Encrypt/EncryptWithAAD. The function refuses to parse arbitrary or foreign bytes.

Solutions

  1. Verify the input is a complete envelope returned by util.Encrypt/EncryptWithAAD, not raw ciphertext or plaintext
  2. Check that the data source actually wrote an encrypted envelope (notebook encryption was enabled when the data was written)
  3. If the data came from another tool, decrypt it with that tool's method instead of SiYuan's EncryptionNonce
  4. If bytes were truncated/reordered in transit, re-fetch or restore the original file

Example fix

// before
nonce, err := util.EncryptionNonce(rawAESCiphertext) // no SENC header

// after
envelope, err := util.Encrypt(key, plaintext)
if err != nil { return err }
nonce, err := util.EncryptionNonce(envelope)
Defensive patterns

Strategy: validation

Validate before calling

func isSENCEnvelope(b []byte) bool {
    return len(b) >= 7 && b[0]=='S' && b[1]=='E' && b[2]=='N' && b[3]=='C'
}
if !isSENCEnvelope(data) { return errors.New("not a SENC envelope") }

Type guard

func hasEncHeader(b []byte) bool {
    return len(b) >= 4 && string(b[:4]) == "SENC"
}

Try / catch

nonce, err := util.EncryptionNonce(ciphertext)
if err != nil {
    return fmt.Errorf("not a SiYuan encrypted envelope: %w", err)
}

Prevention

When it happens

Trigger: Calling EncryptionNonce with ciphertext that was not produced by util.Encrypt/EncryptWithAAD — e.g. raw AES-GCM output from another tool, plaintext bytes, a base64 string decoded incorrectly, or data encrypted by a different format version.

Common situations: Inspecting legacy (pre-encryption-feature) notebook data, decrypting data encrypted by an external tool, passing the wrong slice (e.g. header-stripped ciphertext), or corruption/truncation overwrote the header.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/6fe9c7495c5bda53. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/kdf.go:104

func DeriveKey(password string, salt []byte, p Argon2Params) []byte {
	return argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
}

// Encrypt 用 AES-256-GCM 加密。每次调用生成随机 nonce,因此同一明文多次加密结果不同。
// 返回格式:magic(4B) || spec(1B) || algorithm(1B) || nonceLength(1B) || nonce || ciphertext || GCM tag(16B)。
func Encrypt(key, plaintext []byte) ([]byte, error) {
	return encryptGCM(key, plaintext, nil, "Encrypt")
}

// Decrypt 对应 Encrypt 的解密。密钥错误、格式无效或密文被篡改时返回错误。
func Decrypt(key, ciphertext []byte) ([]byte, error) {
	return decryptGCM(key, ciphertext, nil, "Decrypt")
}

// EncryptionNonce 从 AES-GCM 密文信封中提取 nonce。
func EncryptionNonce(ciphertext []byte) ([]byte, error) {
	if !hasEncryptionMagic(ciphertext) {
		return nil, errors.New("invalid encrypted envelope magic")
	}
	if len(ciphertext) < encryptionEnvelopeHeaderSize {
		return nil, errors.New("encrypted envelope too short")
	}
	if ciphertext[len(encryptionMagic)] != EncryptionSpec {
		return nil, errors.New("unsupported encrypted envelope spec")
	}
	if ciphertext[len(encryptionMagic)+1] != encryptionAlgorithmAES256GCM {
		return nil, errors.New("unsupported encrypted envelope algorithm")
	}
	nonceLength := int(ciphertext[len(encryptionMagic)+2])
	if nonceLength == 0 || len(ciphertext) < encryptionEnvelopeHeaderSize+nonceLength {
		return nil, errors.New("invalid encrypted envelope nonce length")
	}
	return append([]byte(nil), ciphertext[encryptionEnvelopeHeaderSize:encryptionEnvelopeHeaderSize+nonceLength]...), nil
}

// DeriveSubKey 用 HKDF-SHA256 从主 DEK 派生用途隔离的子密钥。

View on GitHub (pinned to 9f775e8a12)