{"record":{"id":"be318cb3b635c5fc","repo":"slackhq/nebula","slug":"invalid-ciphertext-blob-blob-shorter-than-nonce","errorCode":null,"errorMessage":"invalid ciphertext blob - blob shorter than nonce length","messagePattern":"invalid ciphertext blob - blob shorter than nonce length","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/crypto.go","lineNumber":153,"sourceCode":"\t\treturn nil, fmt.Errorf(\"salt must be set in argon2Parameters\")\n\t} else if len(params.salt) < 16 {\n\t\treturn nil, fmt.Errorf(\"salt must be at least 128  bits\")\n\t}\n\n\tkey := argon2.IDKey(passphrase, params.salt, params.Iterations, params.Memory, params.Parallelism, keySize)\n\n\treturn key, nil\n}\n\n// Prepends nonce to ciphertext\nfunc joinNonceCiphertext(nonce []byte, ciphertext []byte) []byte {\n\treturn append(nonce, ciphertext...)\n}\n\n// Splits nonce from ciphertext\nfunc splitNonceCiphertext(blob []byte, nonceSize int) ([]byte, []byte, error) {\n\tif len(blob) <= nonceSize {\n\t\treturn nil, nil, fmt.Errorf(\"invalid ciphertext blob - blob shorter than nonce length\")\n\t}\n\n\treturn blob[:nonceSize], blob[nonceSize:], nil\n}\n\n// EncryptAndMarshalSigningPrivateKey is a simple helper to encrypt and PEM encode a private key\nfunc EncryptAndMarshalSigningPrivateKey(curve Curve, b []byte, passphrase []byte, kdfParams *Argon2Parameters) ([]byte, error) {\n\tciphertext, err := aes256Encrypt(passphrase, kdfParams, b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tb, err = proto.Marshal(&RawNebulaEncryptedData{\n\t\tEncryptionMetadata: &RawNebulaEncryptionMetadata{\n\t\t\tEncryptionAlgorithm: \"AES-256-GCM\",\n\t\t\tArgon2Parameters: &RawNebulaArgon2Parameters{\n\t\t\t\tVersion:     kdfParams.version,\n\t\t\t\tMemory:      kdfParams.Memory,","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/crypto.go#L135-L171","documentation":"splitNonceCiphertext requires the encrypted blob to be strictly longer than the nonce size, because valid AES-GCM output is nonce||ciphertext. A blob at or below nonceSize cannot contain any ciphertext, so aes256Decrypt rejects it as malformed rather than attempting to slice it.","triggerScenarios":"Calling aes256Decrypt (via DecryptAndUnmarshalSigningPrivateKey) with a blob whose length <= nonceSize (12 bytes for AES-GCM): an empty blob, a bare key with no encryption wrapper, or corrupted/truncated PEM-decoded data.","commonSituations":"Attempting to decrypt a key file that was never encrypted; passing a PEM block that is actually a plaintext key or certificate; file truncation during transfer; decrypting with the wrong format/version of encrypted data.","solutions":["Verify the input is genuinely Nebula-encrypted data: decode the PEM block and confirm its Type matches an Encrypted*PrivateKeyBanner and its length exceeds the nonce size (12 bytes for GCM)","Re-export/regenerate the encrypted key file - the existing blob is truncated or not encrypted data","Confirm you are decrypting the right file and not a plaintext key or public certificate"],"exampleFix":"// before\nblock, _ := pem.Decode(data)\nkey, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, block.Bytes) // blob too short\n// after\nblock, _ := pem.Decode(data)\nif block == nil || len(block.Bytes) <= 12 {\n    return fmt.Errorf(\"not encrypted nebula key data\")\n}\nkey, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, block.Bytes)","handlingStrategy":"validation","validationCode":"if len(blob) <= 12 { return fmt.Errorf(\"blob too short to contain nonce+ciphertext: %d\", len(blob)) }","typeGuard":"func isPlausibleEncryptedBlob(b []byte) bool { return len(b) > 12 }","tryCatchPattern":"key, err := cert.DecryptAndUnmarshalSigningPrivateKey(pass, blob)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid ciphertext blob\") { /* wrong/truncated input */ }\n    return err\n}","preventionTips":["Check pem.Decode returned a non-nil block before using block.Bytes","Confirm the PEM block Type is an Encrypted*PrivateKeyBanner","Verify file size/integrity after transfer before decrypting"],"tags":["crypto","aes-gcm","ciphertext","malformed-input"],"backgroundTag":"ciphertext-too-short","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}