Inadequate Padding

ID

swift.inadequate_padding

Severity

high

Resource

Predictability

Language

Swift

Tags

CWE:325, MASWE:0023, NIST.SP.800-53, OWASP:2021:A2, PCI-DSS:6.5.3, crypto

Description

Inadequate encryption strength due to inadequate padding.

Padding is a crucial process in block cipher encryption, where data is modified to fit the required block size of the encryption algorithm. Inadequate or incorrect padding can lead to vulnerabilities such as padding oracle attacks, where attackers exploit the padding errors to decrypt data without knowing the encryption key.

Rationale

Certain padding schemes are known to have specific attacks, such as padding oracle attacks. For example, with RSA, the PKCS#1 v1.5 padding scheme is vulnerable to chosen ciphertext attack (discovered by D. Bleichenbacher). The Optimal Asymmetric Encryption Padding (OAEP, aka. PKCS#1 v2) padding scheme was designed to be resistant to such attack.

The zero padding scheme sometimes seen with symmetric block ciphers is also known to have vulnerabilities.

Some authenticated cipher modes of operation (e.g., GCM) often run in counter mode, and do not require padding. Other modes require padding with block ciphers.

The following is a summary of the most common padding schemes and their vulnerabilities:

Official Name Standard/RFC Aliases Use Case Known Weaknesses

PKCS#7

RFC 5652

PKCS5 (64-bit blocks)

Symmetric Encryption

Padding oracle attacks if decrypted before MAC validation

PKCS#1 v1.5

PKCS#1, RFC 2313/8017

Asymmetric Encryption (RSA)

Padding oracle and Bleichenbacher attacks

OAEP

PKCS#1 v2, RFC 2437/8017

RSA-OAEP

Asymmetric Encryption (RSA)

Original OAEP had IND-CCA1 security; fixed in RSA-OAEP

PSS

PKCS#1 v2.1, RFC 8017

RSA-PSS

Digital Signature (RSA)

Secure if implemented correctly

X9.23

ANSI X9.23

Symmetric Encryption

Padding oracle attacks (similar to PKCS#7)

TBC

(No standardized doc)

Trailing Bit Complement

Symmetric Encryption (rare)

Not widely analyzed; possible predictability

ISO-10126

ISO/IEC 10126

Symmetric Encryption

Padding oracle attacks (similar to PKCS#7)

ISO-7816

ISO/IEC 7816-4

Smart Card Protocols

Not typically used as general padding; context-specific

ISO-9797-1 Method 1

ISO/IEC 9797-1

Zero padding

MAC

Predictable padding risks if not combined with authentication

ISO-9797-1 Method 2

ISO/IEC 9797-1

Bit padding (1 + 0s)

MAC

More secure than Method 1 but still padding-dependent

ISO-9797-1 Method 3

ISO/IEC 9797-1

Length-prepended padding

MAC

Secure if length data is authenticated

AES-KW

RFC 3394

AES Key Wrap

Key Wrapping/Encryption

No padding; requires data length multiple of 64 bits; secure if implemented correctly

AES-KWP

RFC 5649

AES Key Wrap with Padding

Key Wrapping/Encryption

Allows any data length; uses padding; secure if implemented correctly

PSEP

DFJW04 paper

Signcryption

No major weaknesses reported in the framework

In Swift, inadequate padding can occur with both symmetric and asymmetric encryption:

Vulnerable: RSA with PKCS#1 v1.5 padding
import Security
import Foundation

func encryptRSAPKCS1_Vulnerable(_ plaintext: Data, publicKey: SecKey) throws -> Data {
    // FLAW: PKCS#1 v1.5 padding vulnerable to Bleichenbacher attack
    var error: Unmanaged<CFError>?
    guard let encrypted = SecKeyCreateEncryptedData(
        publicKey,
        .rsaEncryptionPKCS1,  // FLAW: Vulnerable padding
        plaintext as CFData,
        &error
    ) else {
        throw error!.takeRetainedValue() as Error
    }

    return encrypted as Data
}
Vulnerable: No padding with block cipher (CryptoSwift)
import CryptoSwift

func encryptNoPadding_Vulnerable(_ plaintext: String, key: [UInt8], iv: [UInt8]) throws -> [UInt8] {
    // FLAW: No padding specified for block cipher
    let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .noPadding)  // FLAW
    return try aes.encrypt(Array(plaintext.utf8))
}
Vulnerable: Zero padding
import CryptoSwift

func encryptZeroPadding_Vulnerable(_ plaintext: String, key: [UInt8], iv: [UInt8]) throws -> [UInt8] {
    // FLAW: Zero padding has ambiguity issues
    let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .zeroPadding)  // FLAW
    return try aes.encrypt(Array(plaintext.utf8))
}

Remediation

To remediate inadequate padding vulnerabilities, follow these best practices:

  1. Specify Proper Padding Schemes: Always use encryption algorithms with a specified padding scheme. Prefer standard padding such as PKCS#7 when using block ciphers.

  2. Employ Secure Modes of Encryption: Use secure modes of operation such as CBC (Cipher Block Chaining) along with padding, rather than ECB (Electronic Codebook), which is more predictable and less secure. In particular, authenticated encryption modes eliminate the need for separate padding validation.

  3. Stay Updated with Cryptographic Best Practices: Continuously monitor and adapt to industry best practices for cryptography to ensure encryption implementations remain secure.

  4. Conduct Comprehensive Security Testing: Perform regular cryptographic security assessments and penetration testing to identify and address potential vulnerabilities in encryption processes.

By following these guidelines, developers can ensure that their encryption implementations effectively protect data against padding-related vulnerabilities and other cryptographic threats.

For Swift, use secure padding schemes:

Fixed: RSA with OAEP padding
import Security
import Foundation

func encryptRSAOAEP_Fixed(_ plaintext: Data, publicKey: SecKey) throws -> Data {
    // FIXED: OAEP padding (PKCS#1 v2.0) is secure
    var error: Unmanaged<CFError>?
    guard let encrypted = SecKeyCreateEncryptedData(
        publicKey,
        .rsaEncryptionOAEPSHA256,  // FIXED: Secure padding
        plaintext as CFData,
        &error
    ) else {
        throw error!.takeRetainedValue() as Error
    }

    return encrypted as Data
}

func decryptRSAOAEP_Fixed(_ ciphertext: Data, privateKey: SecKey) throws -> Data {
    var error: Unmanaged<CFError>?
    guard let decrypted = SecKeyCreateDecryptedData(
        privateKey,
        .rsaEncryptionOAEPSHA256,  // FIXED
        ciphertext as CFData,
        &error
    ) else {
        throw error!.takeRetainedValue() as Error
    }

    return decrypted as Data
}
Fixed: Block cipher with PKCS#7 padding
import CryptoSwift

struct EncryptedData {
    let ciphertext: [UInt8]
    let iv: [UInt8]
}

func encryptWithPKCS7_Fixed(_ plaintext: String, key: [UInt8]) throws -> EncryptedData {
    // FIXED: PKCS#7 padding for block cipher
    let iv = (0..<AES.blockSize).map { _ in UInt8.random(in: 0...UInt8.max) }
    let aes = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7)  // FIXED
    let ciphertext = try aes.encrypt(Array(plaintext.utf8))

    return EncryptedData(ciphertext: ciphertext, iv: iv)
}

func decryptWithPKCS7_Fixed(_ encrypted: EncryptedData, key: [UInt8]) throws -> String {
    let aes = try AES(key: key, blockMode: CBC(iv: encrypted.iv), padding: .pkcs7)
    let decrypted = try aes.decrypt(encrypted.ciphertext)

    return String(bytes: decrypted, encoding: .utf8) ?? ""
}
Fixed: Using CryptoKit AES-GCM (no padding needed)
import CryptoKit
import Foundation

func encryptWithAESGCM_Fixed(_ plaintext: Data, key: SymmetricKey) throws -> Data {
    // FIXED: AES-GCM is AEAD mode, doesn't require padding
    let sealedBox = try AES.GCM.seal(plaintext, using: key)

    // Combined data includes nonce, ciphertext, and authentication tag
    return sealedBox.combined!
}

func decryptWithAESGCM_Fixed(_ combined: Data, key: SymmetricKey) throws -> Data {
    let sealedBox = try AES.GCM.SealedBox(combined: combined)
    return try AES.GCM.open(sealedBox, using: key)
}
Fixed: RSA signature with PSS padding
import Security
import CryptoKit
import Foundation

func signWithRSAPSS_Fixed(_ message: Data, privateKey: SecKey) throws -> Data {
    // FIXED: PSS padding for RSA signatures
    let hash = SHA256.hash(data: message)

    var error: Unmanaged<CFError>?
    guard let signature = SecKeyCreateSignature(
        privateKey,
        .rsaSignatureMessagePSSSHA256,  // FIXED: PSS padding
        Data(hash) as CFData,
        &error
    ) else {
        throw error!.takeRetainedValue() as Error
    }

    return signature as Data
}

func verifyRSAPSS_Fixed(_ message: Data, signature: Data, publicKey: SecKey) throws -> Bool {
    let hash = SHA256.hash(data: message)

    var error: Unmanaged<CFError>?
    let result = SecKeyVerifySignature(
        publicKey,
        .rsaSignatureMessagePSSSHA256,  // FIXED
        Data(hash) as CFData,
        signature as CFData,
        &error
    )

    if let error = error {
        throw error.takeRetainedValue() as Error
    }

    return result
}

Configuration

The rule has the following configurable parameters:

  • allowedPaddings, that indicates the list of allowed paddings.

  • forbiddenPaddings, that indicates the list of forbidden paddings.

If the normalized padding scheme is in the allowedPaddings list, the detector considers the padding as acceptable. If the normalized padding scheme is in the forbiddenPaddings list, the detector considers the padding as insecure and emits a vulnerability. If it does not match any of the lists, the detector logs a warning but does not emit a vulnerability.

References