siyuan-note/siyuan · error

unsupported encrypted envelope algorithm

Error message

unsupported encrypted envelope algorithm

What it means

EncryptionNonce validates the envelope's algorithm byte (offset 5). Only AES-256-GCM (0x01) is implemented; any other algorithm identifier means the envelope was written with an unsupported cipher and cannot be processed.

Solutions

  1. Use the kernel version that wrote the envelope (matching algorithm support) to decrypt it
  2. Restore the blob from backup if the header is corrupted (compare spec byte at offset 4 — if that is also wrong it is likely corruption)
  3. Do not attempt to force-decrypt; GCM authentication would fail anyway with a wrong-key/wrong-algorithm combination
Defensive patterns

Strategy: validation

Validate before calling

if len(data) >= 6 && data[5] != 0x01 {
    return fmt.Errorf("envelope algorithm %d unsupported (AES-256-GCM=1 expected)", data[5])
}

Type guard

func isAESGCMEnvelope(b []byte) bool {
    return len(b) >= 6 && string(b[:4]) == "SENC" && b[4] == 0x01 && b[5] == 0x01
}

Try / catch

nonce, err := util.EncryptionNonce(ciphertext)
if err != nil {
    return fmt.Errorf("unsupported envelope algorithm: %w", err)
}

Prevention

When it happens

Trigger: Calling EncryptionNonce on an envelope whose byte at offset 5 is not 0x01 — data written by a build supporting a different algorithm (e.g. ChaCha20-Poly1305 marker) or corrupted/tampered header bytes.

Common situations: Reading encrypted data produced by a different or newer kernel build, header corruption during storage/sync, or bit-flip tampering of the stored blob.

Related errors


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

Appendix: source

Thrown at kernel/util/kdf.go:113

// 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 派生用途隔离的子密钥。
// 同一 (dek, purpose) 多次调用结果一致;不同 purpose 派生出相互独立的子密钥,
// 实现用途分离——.sy/assets/AV 各用独立子密钥,互不可替代,限制单点密钥泄漏的影响面。
func DeriveSubKey(dek []byte, purpose string) []byte {
	// HKDF info 用 purpose 字节;salt 为 nil(DEK 本身已是高熵随机密钥,无需额外 salt)
	r := hkdf.New(sha256.New, dek, nil, []byte(purpose))
	out := make([]byte, 32) // AES-256
	if _, err := io.ReadFull(r, out); err != nil {
		// hkdf.Read 不应出错(除非 dek 为空);防御性 panic 避免静默返回弱密钥
		panic("hkdf derive failed: " + err.Error())

View on GitHub (pinned to 9f775e8a12)