SecTransform API: Sign transform silently produces bogus signatures

Originator:Karoly.Lorentey
Number:rdar://10394759 Date Originated:04-Nov-2011 02:21 AM
Status:Duplicate/10394846 Resolved:
Product:Mac OS X Product Version:10.7.2 (11C73)
Classification:Serious bug Reproducible:Always
 
Summary:

The sign computation transformation created by SecSignTransformCreate does not report an error when the application does not have the credentials to sign data with the supplied SecKeyRef. 

Instead, it runs successfully, but produces corrupt signature data of length 32768. The contents of this signature look like random memory junk. SecVerifyTransform confirms that these signatures are invalid.

The result is the same with either SecTransformExecute and SecTransformExecuteAsync.


Steps to Reproduce:

The attached project includes a silly little command-line tool that retrieves a random identity from your keychain and tries to sign the contents of /etc/passwd using SecSignTransform. It then tries to verify the result with a SecVerifyTransform.

- Create at least one identity in your login keychain.
- Build and run the attached project. It should make the system pop up a standard keychain access confirmation dialog. ("SecTransformTest wants to sign using key "<key>" in your keychain.")
- Click "Allow".  It should produce the following output (indicating correct behavior):

        2011-11-04 01:11:32.103 SecTransformTest[69579:707] Successfully signed, signature length: 256
        2011-11-04 01:11:32.134 SecTransformTest[69579:707] Verified signature with result: 1 (OK)

- Now re-run the project, but click "Deny". It will produce this result:

        2011-11-04 01:23:05.550 SecTransformTest[69703:707] Successfully signed, signature length: 32768
        2011-11-04 01:23:05.557 SecTransformTest[69703:707] Verified signature with result: 0 (FAIL)

Thus, with access denied, the sign transform runs successfully, but its result isn't a signature.


Expected Results:

The sign transformation should either produce a valid signature or report an error.


Actual Results:

The sign transformation produces a bogus signature without reporting an error.


Regression:

None, SecTransform is new in Lion.


Notes:

The source code of the sample project is reproduced below:


//
//  main.c
//  SecTransformTest
//
//  Created by Karoly Lorentey on 2011-11-04.
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Security/Security.h>

CFDataRef CreateSignature (SecKeyRef privateKey, CFDataRef plaintext, CFErrorRef *error);
CFBooleanRef CreateVerificationResult (SecKeyRef publicKey, CFDataRef plaintext, CFDataRef signature, CFErrorRef *error);

CFDataRef 
CreateSignature (SecKeyRef privateKey, CFDataRef plaintext, CFErrorRef *error)
{
    SecTransformRef signingTransform = SecSignTransformCreate(privateKey, error);
    if (signingTransform == NULL)
        return NULL;
    
    Boolean success = SecTransformSetAttribute(signingTransform,
                                               kSecTransformInputAttributeName,
                                               plaintext,
                                               error);
    if (!success) {
        CFRelease(signingTransform);
        return NULL;
    }
    
    CFDataRef signature = SecTransformExecute(signingTransform, error);
    if (signature != NULL)
        CFRetain(signature);
    CFRelease(signingTransform);
    return signature;
}

CFBooleanRef
CreateVerificationResult (SecKeyRef publicKey, CFDataRef plaintext, CFDataRef signature, CFErrorRef *error)
{
    SecTransformRef verifyTransform = SecVerifyTransformCreate(publicKey, signature, error);
    if (verifyTransform == NULL)
        return NULL;
    
    Boolean success = SecTransformSetAttribute(verifyTransform,
                                               kSecTransformInputAttributeName,
                                               plaintext,
                                               error);
    if (!success) {
        CFRelease(verifyTransform);
        return NULL;
    }
    
    CFBooleanRef result = SecTransformExecute(verifyTransform, error);
    if (result != NULL)
        CFRetain(result);
    CFRelease(verifyTransform);
    return result;
}

int main (int argc, const char * argv[])
{
    BOOL success = 0;
    CFErrorRef error = NULL;
    CFDataRef plaintext = (CFDataRef)[[NSData alloc] initWithContentsOfFile:@"/etc/passwd"];

    CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(query, kSecClass, kSecClassIdentity);
    CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
    CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);

    SecIdentityRef identity = NULL;
    SecCertificateRef certificate = NULL;
    SecKeyRef publicKey = NULL;
    SecKeyRef privateKey = NULL;
    CFDataRef signature = NULL;
    CFBooleanRef verified = NULL;

    OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&identity);
    if (status)
        goto error;
    
    status = SecIdentityCopyCertificate(identity, &certificate);
    if (status)
        goto error;
    
    status = SecCertificateCopyPublicKey(certificate, &publicKey);
    if (status)
        goto error;
    
    status = SecIdentityCopyPrivateKey(identity, &privateKey);
    if (status)
        goto error;
    
    signature = CreateSignature(privateKey, plaintext, &error);
    if (signature == NULL)
        goto error;
    NSLog(@"Successfully signed, signature length: %lu", CFDataGetLength(signature));
    
    verified = CreateVerificationResult(publicKey, plaintext, signature, &error);
    if (verified == NULL)
        goto error;
    NSLog(@"Verified signature with result: %@ (%@)", verified, (verified == kCFBooleanTrue ? @"OK" : @"FAIL"));
    
    success = YES;
    
error:
    if (!success) {
        NSLog(@"An error happened.");
        NSLog(@"status: %d", (int)status);
        if (error) {
            CFShow(error);
        }
    }
    CFRelease(plaintext);
    CFRelease(query);
    if (identity) CFRelease(identity);
    if (certificate) CFRelease(certificate);
    if (publicKey) CFRelease(publicKey);
    if (privateKey) CFRelease(privateKey);
    if (signature) CFRelease(signature);
    if (verified) CFRelease(verified);
    return (success ? 0 : 1);
}

Comments

This issue has been fixed in the first seed for OS X Mavericks.

By Karoly.Lorentey at June 19, 2013, 12:17 p.m. (reply...)

Hi,

Indeed, I noticed the same issue (on 10.7.3), do you have any kind of news from apple (I see this is the Apple bug template :))

Thanks, Sébastien.

By sebastien.henaff at April 17, 2012, 11:18 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!