Insufficient Key Size

ID

swift.insufficient_key_size

Severity

high

Resource

Predictability

Language

Swift

Tags

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

Description

Inadequate encryption strength due to insufficient key size.

Rationale

In cryptography, the strength of an encryption scheme is significantly driven by the size of the key. Insufficient key sizes can lead to vulnerabilities that render encrypted data susceptible to exposure through brute-force attacks.

In Swift, a common mistake is using cryptographic keys with insufficient length in bits. For instance, RSA keys shorter than 2048 bits are generally considered weak for most usages, such as key wrapping and digital signature:

Vulnerable RSA key generation with 1024 bits
import Security
import Foundation

func generateWeakRSAKeyPair() throws -> (SecKey, SecKey) {
    // VULNERABLE: Weak key size for RSA, 1024 bits
    let attributes: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 1024  // FLAW: Too weak
    ]

    var error: Unmanaged<CFError>?
    guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
          let publicKey = SecKeyCopyPublicKey(privateKey) else {
        throw error!.takeRetainedValue() as Error
    }

    return (privateKey, publicKey)
}

In this example, setting kSecAttrKeySizeInBits to 1024 generates an RSA keypair with a modulus length of 1024 bits, which is below the secure threshold for RSA keys.

Another vulnerable example using CommonCrypto for AES with insufficient key size:

Vulnerable AES encryption with 64-bit key
import CommonCrypto
import Foundation

func encryptWithWeakAES(_ data: Data, key: Data) -> Data? {
    // FLAW: Key is only 64 bits (8 bytes) - way too small for AES
    let keyData = key.prefix(8)  // FLAW: Only 64 bits

    var encrypted = Data(count: data.count + kCCBlockSizeAES128)
    var numBytesEncrypted: size_t = 0

    let status = keyData.withUnsafeBytes { keyBytes in
        data.withUnsafeBytes { dataBytes in
            encrypted.withUnsafeMutableBytes { encryptedBytes in
                CCCrypt(
                    CCOperation(kCCEncrypt),
                    CCAlgorithm(kCCAlgorithmAES),
                    CCOptions(kCCOptionPKCS7Padding),
                    keyBytes.baseAddress, keyData.count,  // FLAW: 64-bit key
                    nil,
                    dataBytes.baseAddress, data.count,
                    encryptedBytes.baseAddress, encrypted.count,
                    &numBytesEncrypted
                )
            }
        }
    }

    guard status == kCCSuccess else { return nil }
    encrypted.count = numBytesEncrypted
    return encrypted
}

Remediation

To remedy insufficient key size vulnerabilities, developers should adhere to well-established cryptographic standards and ensure that cryptographic keys are of adequate length. Here are some practical steps for Java:

  1. Update your cryptographic algorithms and libraries: Always use up-to-date and secure libraries. Java’s built-in cryptographic libraries, or well-known libraries like Bouncy Castle, should be considered.

  2. Choose adequate key sizes: For example, with AES, opt for a minimum of 128 bits, ideally 256 bits, for symmetric encryption keys.

  3. Review and Refactor: Legacy systems often contain outdated encryption; perform a security review to identify areas where cryptography needs strengthening.

  4. Stay Informed: Cryptographic standards evolve. Regularly consult NIST and similar organizations’ guidelines to ensure compliance with current best practices.

Through comprehensive auditing and adherence to these guidelines, an organization can significantly reduce the risk posed by insufficient cryptographic key sizes.

For the RSA example, the following code should be used instead:

Fixed RSA key generation with 2048 bits
import Security
import Foundation

func generateSecureRSAKeyPair() throws -> (SecKey, SecKey) {
    // FIXED: 2048 bit key, the minimum threshold for RSA key length
    let attributes: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 2048  // FIXED: Secure key size
    ]

    var error: Unmanaged<CFError>?
    guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
          let publicKey = SecKeyCopyPublicKey(privateKey) else {
        throw error!.takeRetainedValue() as Error
    }

    return (privateKey, publicKey)
}

For AES, always use proper key sizes (128, 192, or 256 bits):

Fixed AES encryption with proper key size
import CryptoKit
import Foundation

func encryptWithSecureAES(_ data: Data) throws -> (ciphertext: Data, key: SymmetricKey) {
    // FIXED: Generate proper 256-bit AES key
    let key = SymmetricKey(size: .bits256)

    let sealedBox = try AES.GCM.seal(data, using: key)

    return (sealedBox.combined!, key)
}

For elliptic curve cryptography, use approved curves with adequate security levels:

Secure elliptic curve key generation
import CryptoKit

func generateSecureECKeyPair() -> (privateKey: P256.Signing.PrivateKey, publicKey: P256.Signing.PublicKey) {
    // FIXED: Use P-256 curve (256-bit key, provides ~128-bit security)
    let privateKey = P256.Signing.PrivateKey()
    let publicKey = privateKey.publicKey

    return (privateKey, publicKey)
}

func generateHighSecurityECKeyPair() -> (privateKey: P384.Signing.PrivateKey, publicKey: P384.Signing.PublicKey) {
    // Even better: Use P-384 curve (384-bit key, provides ~192-bit security)
    let privateKey = P384.Signing.PrivateKey()
    let publicKey = privateKey.publicKey

    return (privateKey, publicKey)
}
CommonCrypto RSA with secure key size
import Security
import Foundation

func generateRSAKeyPairWithAttributes() throws -> (SecKey, SecKey) {
    // FIXED: Use 2048-bit RSA (or 3072/4096 for higher security)
    let keyAttributes: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits as String: 2048,  // FIXED
        kSecPrivateKeyAttrs as String: [
            kSecAttrIsPermanent as String: false
        ]
    ]

    var error: Unmanaged<CFError>?
    guard let privateKey = SecKeyCreateRandomKey(keyAttributes as CFDictionary, &error),
          let publicKey = SecKeyCopyPublicKey(privateKey) else {
        throw error!.takeRetainedValue() as Error
    }

    return (privateKey, publicKey)
}

Configuration

The rule has the following configurable parameters:

  • minKeySize, that indicates the minimum key size allowed for each algorithm.

  • allowedEllipticCurves, that indicates the elliptic curves allowed for ECDH or ECDSA schemes.

properties:
  minKeySize:
    - AES/128 # Advanced Encryption Standard, block cipher
    - CMAC/128 # Block Cipher Message Authentication Code
    - DiffieHellman/2048 # Diffie-Hellman key agreement
    - DSA/2048 # Digital Signature Standard (DSA)
    - ECDH/256 # Elliptic Curve Diffie-Hellman key agreement
    - ECDSA/256 # Elliptic Curve DSA
    - HMAC/128 # Hash-based Message Authentication Code
    - RSA/2048 # RSA

  # Elliptic curves allowed for ECDH or ECDSA schemes
  allowedEllipticCurves: [
    Curve1174, Curve25519, Curve41417,
    P-256, secp256r1, secp256k1,
    P-384, secp384r1,
    brainpoolP256t1, brainpoolP384t1
  ]