Skip to content

MASTG-DEMO-0011: Uses of Weak Key Size in SecKeyCreateRandomKey with r2

Content in BETA

This content is in beta and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Sample

MastgTest.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import Foundation
import Security

struct MastgTest {
    static func mastgTest(completion: @escaping (String) -> Void) {

        // Step 1: Generate an RSA key pair with a 1024-bit key size
        let tag = "org.owasp.mas.rsa-1014".data(using: .utf8)!
        let keyAttributes: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: 1024,  // Using 1024-bit RSA key
            kSecPrivateKeyAttrs as String:
               [kSecAttrIsPermanent as String:    true,     // to store it in the Keychain
                kSecAttrApplicationTag as String: tag]      // to find and retrieve it from the Keychain later
        ]

        var error: Unmanaged<CFError>?
        guard let privateKey = SecKeyCreateRandomKey(keyAttributes as CFDictionary, &error) else {
            completion("Failed to generate private key: \(String(describing: error))")
            return
        }

        guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
            completion("Failed to generate public key")
            return
        }

        // Convert the private key to data (DER format)
        guard let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, &error) as Data? else {
            completion("Failed to extract private key: \(String(describing: error))")
            return
        }

        // Encode the private key for display
        //let privateKeyBase64 = privateKeyData.base64EncodedString()
        let privateKeyHex = privateKeyData.map { String(format: "%02hhx", $0) }.joined()

        // Convert the public key to data (DER format)
        guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
            completion("Failed to extract public key: \(String(describing: error))")
            return
        }

        // Encode the public key for display
        // let publicKeyBase64 = publicKeyData.base64EncodedString()
        let publicKeyHex = publicKeyData.map { String(format: "%02hhx", $0) }.joined()

        // Data to sign
        let dataToSign = "This is a sample text".data(using: .utf8)!

        // Step 2: Sign the data with the private key
        guard let signature = SecKeyCreateSignature(
            privateKey,
            SecKeyAlgorithm.rsaSignatureMessagePKCS1v15SHA256,
            dataToSign as CFData,
            &error
        ) else {
            completion("Signing failed: \(String(describing: error))")
            return
        }

        // Convert signature to hex string for display
        let signatureHex = (signature as Data).map { String(format: "%02hhx", $0) }.joined()

        // Step 3: Verify the signature with the public key
        let verificationStatus = SecKeyVerifySignature(
            publicKey,
            SecKeyAlgorithm.rsaSignatureMessagePKCS1v15SHA256,
            dataToSign as CFData,
            signature as CFData,
            &error
        )

        let verificationResult = verificationStatus ? "Signature is valid." : "Signature is invalid."

        let value = """
        Original: \(String(data: dataToSign, encoding: .utf8)!)

        Private Key (Hex): \(privateKeyHex)

        Public Key (Hex): \(publicKeyHex)

        Signature (Hex): \(signatureHex)

        Verification: \(verificationResult)
        """

        completion(value)
    }
}

Steps

When calling SecKeyCreateRandomKey the key size is specified in the kSecAttrKeySizeInBits attribute within the parameters dictionary. See Key Generation Attributes for details.

  1. Unzip the app package and locate the main binary file ( Exploring the App Package), which in this case is ./Payload/MASTestApp.app/MASTestApp.
  2. Open the app binary with radare2 for iOS with the -i option to run this script.
security_keysize.r2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
?e;?e

?e Uses of SecKeyCreateRandomKey:
afl~SecKeyCreateRandomKey

?e

?e xrefs to SecKeyCreateRandomKey:
axt @ 0x1000078ac

?e

?e Use of reloc.kSecAttrKeySizeInBits as input for SecKeyCreateRandomKey:
pd 1 @ sym.func.1000046f8

?e ...

pd 9 @ 0x10000484c

?e ...

pd-- 2 @ 0x1000049a0
run.sh
1
r2 -q -i security_keysize.r2 -A MASTestApp

Observation

The output contains the disassembled code of the function using SecKeyCreateRandomKey.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Uses of SecKeyCreateRandomKey:
0x1000078ac    1     12 sym.imp.SecKeyCreateRandomKey

xrefs to SecKeyCreateRandomKey:
sym.func.1000046f8 0x1000049a0 [CALL:--x] bl sym.imp.SecKeyCreateRandomKey

Use of reloc.kSecAttrKeySizeInBits as input for SecKeyCreateRandomKey:
            ; CALL XREF from sym.func.1000063a0 @ 0x1000063d8(x)
 2920: sym.func.1000046f8 (int64_t arg1, void *arg2, void *arg3);
           ; arg int64_t arg1 @ x0
           ; arg void *arg2 @ x1
           ; arg void *arg3 @ x2
           ; var void *var_0h @ sp+0x0
           ; var int64_t var_0h_14 @ sp+0x8
           ; var int64_t var_10h @ sp+0x10
           ; var int64_t var_10h_4 @ sp+0x18
           ; var void *var_20h @ sp+0x20
           ; var int64_t var_28h @ sp+0x28
           ; var int64_t var_30h @ sp+0x30
           ; var int64_t var_0h_12 @ sp+0x38
           ; var int64_t var_40h @ sp+0x40
           ; var int64_t var_0h_13 @ sp+0x48
           ; var void *var_50h_2 @ sp+0x50
           ; var int64_t var_0h_10 @ sp+0x58
           ; var int64_t var_0h_9 @ sp+0x60
           ; var void *var_0h_11 @ sp+0x68
           ; var int64_t var_0h_5 @ sp+0x70
           ; var int64_t var_0h_4 @ sp+0x78
           ; var int64_t var_80h @ sp+0x80
           ; var int64_t var_80h_2 @ sp+0x88
           ; var int64_t var_0h_2 @ sp+0x90
           ; var void *var_98h @ sp+0x98
           ; var void *var_98h_2 @ sp+0xa0
           ; var int64_t var_0h_7 @ sp+0xb0
           ; var int64_t var_b0h @ sp+0xb8
           ; var void *var_c0h @ sp+0xc0
           ; var void *arg0 @ sp+0xc8
           ; var int64_t var_0h_8 @ sp+0xd0
           ; var int64_t var_0h_6 @ sp+0xd8
           ; var void *var_e0h @ sp+0xe0
           ; var void *var_160h @ sp+0x160
           ; var int64_t var_0h_3 @ sp+0x210
           ; var int64_t var_60h @ sp+0x220
           ; var int64_t var_60h_2 @ sp+0x228
           ; var int64_t var_10h_2 @ sp+0x230
           ; var int64_t var_10h_3 @ sp+0x238
           ; var int64_t var_20h_2 @ sp+0x240
           ; var int64_t var_20h_3 @ sp+0x248
           ; var int64_t var_30h_2 @ sp+0x250
           ; var int64_t var_30h_3 @ sp+0x258
           ; var int64_t var_40h_2 @ sp+0x260
           ; var int64_t var_40h_3 @ sp+0x268
           ; var int64_t var_50h @ sp+0x270
           ; var int64_t var_50h_3 @ sp+0x278
           0x1000046f8      fc6fbaa9       stp x28, x27, [sp, -0x60]!
...
           0x10000484c      080942f9       ldr x8, reloc.kSecAttrKeySizeInBits ; 0x10000c410
           0x100004850      000140f9       ldr x0, [x8]
           0x100004854      e30b0094       bl fcn.1000077e0
           0x100004858      800605a9       stp x0, x1, [x20, 0x50]
           0x10000485c      48000090       adrp x8, reloc.Foundation.__DataStorage._bytes.allocator__UnsafeMutableRawPointer______ ; 0x10000c000
           0x100004860      089d41f9       ldr x8, reloc.Swift.Int    ; 0x10000c338
           0x100004864      883e00f9       str x8, [x20, 0x78]
           0x100004868      08808052       mov w8, 0x400
           0x10000486c      883200f9       str x8, [x20, 0x60]
...
           0x100004998      f40300aa       mov x20, x0
           0x10000499c      61620391       add x1, x19, 0xd8
           0x1000049a0      c30b0094       bl sym.imp.SecKeyCreateRandomKey
           0x1000049a4      fb0300aa       mov x27, x0

This function is pretty big so we just included the relevant part of the code that's right before the call to SecKeyCreateRandomKey. Note that we can see attributes being set in the parameters dictionary such as kSecAttrKeySizeInBits as reloc.kSecAttrKeySizeInBits. In radare2, this means that the symbol kSecAttrKeySizeInBits is not directly referenced by an absolute address but rather through a relocation entry. This entry will be resolved by the dynamic linker at runtime to the actual address where kSecAttrKeySizeInBits is located in memory.

Evaluation

In the output we can see how the kSecAttrKeySizeInBits attribute is set to 1024 bits (0x400 in hexadecimal) using the x8 register. This is later used to call SecKeyCreateRandomKey.

evaluation.txt
1
2
3
4
5
6
7
8
9
          0x10000484c      080942f9       ldr x8, reloc.kSecAttrKeySizeInBits ; 0x10000c410   ->  Load the address of kSecAttrKeySizeInBits into x8
          0x100004850      000140f9       ldr x0, [x8]
          0x100004854      e30b0094       bl fcn.1000077e0
          0x100004858      800605a9       stp x0, x1, [x20, 0x50]
          0x10000485c      48000090       adrp x8, reloc.Foundation.__DataStorage._bytes.allocator__UnsafeMutableRawPointer______ ; 0x10000c000
          0x100004860      089d41f9       ldr x8, reloc.Swift.Int    ; 0x10000c338
          0x100004864      883e00f9       str x8, [x20, 0x78]
          0x100004868      08808052       mov w8, 0x400                                       ->  Move 0x400 (1024 in decimal) into w8, the lower 32 bits of x8
          0x10000486c      883200f9       str x8, [x20, 0x60]                                 ->  Store the final value (1024-bit key size) into memory

The test fails because the key size is set to 1024 bits, which is considered weak for RSA encryption. The key size should be increased to 2048 bits or higher to provide adequate security against modern cryptographic attacks.