siyuan-note/siyuan · error

invalid encrypted envelope nonce length

Error message

invalid encrypted envelope nonce length

What it means

Raised by EncryptionNonce when the stored nonceLength byte is 0, or when the buffer is too short to contain encryptionEnvelopeHeaderSize+nonceLength bytes. The nonce length is read from the envelope header and must fit within the remaining buffer.

Source

Thrown at kernel/util/kdf.go:117

}

// 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())
	}
	return out
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Restore the ciphertext from a known-good backup.
  2. Validate the full envelope length (header + nonce + at least one GCM tag block) before calling EncryptionNonce.
  3. Ensure writers always use the GCM default nonce size (12 bytes) via encryptGCM.

Example fix

// before
nonce, err := util.EncryptionNonce(blob)

// after: sanity-check the declared nonce length
if len(blob) >= 7 {
    declared := int(blob[6])
    if declared == 0 || len(blob) < 7+declared {
        return errors.New("envelope declares an impossible nonce length")
    }
}
nonce, err := util.EncryptionNonce(blob)
Defensive patterns

Strategy: validation

Validate before calling

if len(blob) >= 7 {
    declared := int(blob[6])
    if declared == 0 || len(blob) < 7+declared {
        return errors.New("envelope declares an impossible nonce length")
    }
}

Prevention

When it happens

Trigger: EncryptionNonce reads nonceLength = blob[6]; if it is zero, or the buffer does not contain at least 7+nonceLength bytes, this error returns. Occurs with corrupted header bytes (nonceLength set to a huge value) or truncated ciphertext.

Common situations: Header byte corruption setting nonceLength to a large value; a truncated blob whose header claims more nonce bytes than remain; an envelope written with a non-standard nonce size read by code assuming GCM's 12-byte nonce.

Related errors


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