Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Your code is leaking the first allocation of output.</p> <p>Besides that it looks mostly ok.</p> <p>This is a complete end-to-end implementation using a SHA256 hash of the user's passphrase (in this case 'Salamander') and base64'ing the output. There is a PHP test implementation in the source that reconstructs the key then trims the PKCS7 padding before giving final output. A decryptor in Ruby follows, the PKCS7 padding removal happens automatically by the OpenSSL::Cipher.</p> <p>Here you go:</p> <pre><code>// Crypto categories for iOS #import &lt;CommonCrypto/CommonCryptor.h&gt; #import &lt;CommonCrypto/CommonDigest.h&gt; @interface NSData( Crypto ) - (NSData *) aesEncryptedDataWithKey:(NSData *) key; - (NSString *) base64Encoding; @end @interface NSString( Crypto ) - (NSData *) sha256; @end // -------- @implementation NSData( Crypto ) - (NSData *) aesEncryptedDataWithKey:(NSData *) key { unsigned char *buffer = nil; size_t bufferSize; CCCryptorStatus err; NSUInteger i, keyLength, plainTextLength; // make sure there's data to encrypt err = ( plainTextLength = [self length] ) == 0; // pass the user's passphrase through SHA256 to obtain 32 bytes // of key data. Use all 32 bytes for an AES256 key or just the // first 16 for AES128. if ( ! err ) { switch ( ( keyLength = [key length] ) ) { case kCCKeySizeAES128: case kCCKeySizeAES256: break; // invalid key size default: err = 1; break; } } // create an output buffer with room for pad bytes if ( ! err ) { bufferSize = kCCBlockSizeAES128 + plainTextLength + kCCBlockSizeAES128; // iv + cipher + padding err = ! ( buffer = (unsigned char *) malloc( bufferSize ) ); } // encrypt the data if ( ! err ) { srandomdev(); // generate a random iv and prepend it to the output buffer. the // decryptor needs to be aware of this. for ( i = 0; i &lt; kCCBlockSizeAES128; ++i ) buffer[ i ] = random() &amp; 0xff; err = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, [key bytes], keyLength, buffer, [self bytes], plainTextLength, buffer + kCCBlockSizeAES128, bufferSize - kCCBlockSizeAES128, &amp;bufferSize ); } if ( err ) { if ( buffer ) free( buffer ); return nil; } // dataWithBytesNoCopy takes ownership of buffer and will free() it // when the NSData object that owns it is released. return [NSData dataWithBytesNoCopy: buffer length: bufferSize + kCCBlockSizeAES128]; } - (NSString *) base64Encoding { char *encoded, *r; const char eTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned i, l, n, t; UInt8 *p, pad = '='; NSString *result; p = (UInt8 *) [self bytes]; if ( ! p || ( l = [self length] ) == 0 ) return @""; r = encoded = malloc( 4 * ( ( n = l / 3 ) + ( l % 3 ? 1 : 0 ) ) + 1 ); if ( ! encoded ) return nil; for ( i = 0; i &lt; n; ++i ) { t = *p++ &lt;&lt; 16; t |= *p++ &lt;&lt; 8; t |= *p++; *r++ = eTable[ t &gt;&gt; 18 ]; *r++ = eTable[ t &gt;&gt; 12 &amp; 0x3f ]; *r++ = eTable[ t &gt;&gt; 6 &amp; 0x3f ]; *r++ = eTable[ t &amp; 0x3f ]; } if ( ( i = n * 3 ) &lt; l ) { t = *p++ &lt;&lt; 16; *r++ = eTable[ t &gt;&gt; 18 ]; if ( ++i &lt; l ) { t |= *p++ &lt;&lt; 8; *r++ = eTable[ t &gt;&gt; 12 &amp; 0x3f ]; *r++ = eTable[ t &gt;&gt; 6 &amp; 0x3f ]; } else { *r++ = eTable[ t &gt;&gt; 12 &amp; 0x3f ]; *r++ = pad; } *r++ = pad; } *r = 0; result = [NSString stringWithUTF8String: encoded]; free( encoded ); return result; } @end @implementation NSString( Crypto ) - (NSData *) sha256 { unsigned char *buffer; if ( ! ( buffer = (unsigned char *) malloc( CC_SHA256_DIGEST_LENGTH ) ) ) return nil; CC_SHA256( [self UTF8String], [self lengthOfBytesUsingEncoding: NSUTF8StringEncoding], buffer ); return [NSData dataWithBytesNoCopy: buffer length: CC_SHA256_DIGEST_LENGTH]; } @end // ----------------- @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSData *plain = [@"This is a test of the emergency broadcast system." dataUsingEncoding: NSUTF8StringEncoding]; NSData *key = [NSData dataWithBytes: [[@"Salamander" sha256] bytes] length: kCCKeySizeAES128]; NSData *cipher = [plain aesEncryptedDataWithKey: key]; NSString *base64 = [cipher base64Encoding]; NSLog( @"cipher: %@", base64 ); // stuff the base64'ed cipher into decrypt.php: // http://localhost/~par/decrypt.php?cipher=&lt;base64_output&gt; /* &lt;?php header( "content-type: text/plain" ); if ( ! ( $cipher = $_GET[ 'cipher' ] ) ) { echo "no cipher parameter found"; return; } echo "cipher: $cipher\n"; $cipher = base64_decode( $cipher ); $iv = substr( $cipher, 0, 16 ); $cipher = substr( $cipher, 16 ); // use the full key (all 32 bytes) for aes256 $key = substr( hash( "sha256", "Salamander", true ), 0, 16 ); $plainText = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $cipher, MCRYPT_MODE_CBC, $iv ); $plainTextLength = strlen( $plainText ); // strip pkcs7 padding $padding = ord( $plainText[ $plainTextLength - 1 ] ); $plainText = substr( $plainText, 0, -$padding ); printf( "plaintext: %s\n", $plainText ); ?&gt; */ return YES; } @end </code></pre> <p>Decryption of the output of the above in Ruby:</p> <pre><code>require 'base64' require 'openssl' def decrypt( cipherBase64 ) cipher = Base64.decode64( cipherBase64 ) aes = OpenSSL::Cipher::Cipher.new( "aes-128-cbc" ).decrypt aes.iv = cipher.slice( 0, 16 ) # don't slice the SHA256 output for AES256 aes.key = ( Digest::SHA256.digest( 'Salamander' ) ).slice( 0, 16 ) cipher = cipher.slice( 16..-1 ) return aes.update( cipher ) + aes.final end text = '3o4ARWOxwmLEPgq3SJ3A2ws7sUSxMvWSKbbs+oABsOcywk+9qPBoDjhLAfAW/n28pbnsT2w5QMSye6pz3Lz8xmg5BYL8HdfKwbS9EpTbaUc=' print decrypt( text ) + "\n" </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload