Question

I am trying to make a Objective-C wrapper class for some cryptographic operations that will use the Crypto++ library. I have a few questions,

This is the code I am using

- (void)testSerpentEncryptonMechanism
{
     byte key[ CryptoPP::Serpent::MAX_KEYLENGTH ], iv[ CryptoPP::Serpent::BLOCKSIZE ];

    ::memset( key, 0x50 , CryptoPP::Serpent::MAX_KEYLENGTH );
    ::memset( iv, 0x10, CryptoPP::Serpent::BLOCKSIZE );

    NSString *andThisShalBeEncrypted = @"Serpentine and black...";

    NSLog(@"will encrypt %@",andThisShalBeEncrypted);

    NSString *encrypted = [self encryptWithSerpentString:andThisShalBeEncrypted withKey:key];

    NSLog(@"encrypted:%@",encrypted);

    NSString *decrypted = [self decryptWithSerpentString:encrypted withKey:key andIV:iv];

    NSLog(@"decrypted: %@",decrypted);


}

- (NSString *)encryptWithSerpentString:(NSString *)plaintext withKey:(byte[])keyArray
{
    std::string ptext = [plaintext UTF8String];
    std::string ciphertext;

    byte key[ CryptoPP::Serpent::MAX_KEYLENGTH ], iv[ CryptoPP::Serpent::BLOCKSIZE ];

    ::memset( key, 0x50 , CryptoPP::Serpent::MAX_KEYLENGTH );
    ::memset( iv, 0x10, CryptoPP::Serpent::BLOCKSIZE );

    CryptoPP::Serpent::Encryption serpentEncryptor (key, CryptoPP::Serpent::MAX_KEYLENGTH);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcSerpentEncryptor (serpentEncryptor, iv);

    CryptoPP::StreamTransformationFilter stfSerpentEncryptor(cbcSerpentEncryptor, new CryptoPP::StringSink (ciphertext));
    stfSerpentEncryptor.Put( reinterpret_cast<const unsigned char*>( ptext.c_str() ), ptext.length() + 1);
    stfSerpentEncryptor.MessageEnd();

    std::string finalCT;
    CryptoPP::StringSource base64Encoder (ciphertext, true, new CryptoPP::Base64Encoder(new CryptoPP::StringSink(finalCT)));



    return @(finalCT.c_str());
}

- (NSString *)decryptWithSerpentString:(NSString *)ciphertext withKey:(byte[])keyArray andIV:(byte[])initializationVector
{
    std::string ctext;
    std::string plaintext;

    byte key[ CryptoPP::Serpent::MAX_KEYLENGTH ], iv[ CryptoPP::Serpent::BLOCKSIZE ];

//    ::memset( key, &keyArray , CryptoPP::Serpent::MAX_KEYLENGTH );
//    ::memset( iv, (byte[])initializationVector, CryptoPP::Serpent::BLOCKSIZE );

    ::memset( key, 0x50 , CryptoPP::Serpent::MAX_KEYLENGTH );
    ::memset( iv, 0x10, CryptoPP::Serpent::BLOCKSIZE );

    // decode from base64
    std::string encoded = [ciphertext UTF8String];
    CryptoPP::StringSource base64Encoder (encoded, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(ctext)));


    CryptoPP::Serpent::Decryption serpentDecryptor (key, CryptoPP::Serpent::MAX_KEYLENGTH);
    CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcSerpentDecryptor (serpentDecryptor, iv);

    CryptoPP::StreamTransformationFilter stfSerpentDecryptor(cbcSerpentDecryptor, new CryptoPP::StringSink (ctext));// crash
    stfSerpentDecryptor.Put( reinterpret_cast<const unsigned char*>( ctext.c_str() ), ctext.length() + 1);
    stfSerpentDecryptor.MessageEnd();


    return @(ctext.c_str());
}

1) How do I pass the key/IV as method parameters? See the commented code, that doesn't work...

2) I get a crash when trying to decrpyt

libc++abi.dylib: terminating with uncaught exception of type CryptoPP::InvalidCiphertext: StreamTransformationFilter: ciphertext length is not a multiple of block size

Why? I thought decryption was automatic and the encryption will pad the plaintext accordingly... I would also like to add a HMAC to the methods... This will be added after encryption and checked before decryption, correct?

Was it helpful?

Solution

To get padding you have to do it yourself or have an API that supports standard paddings such as PKCS7. Base encryption, stream cyphers excepted, are block based, at the lower level there must be full blocks. Some higher level API provide padding.

I pass keys, iv and data as NSData * and access the bytes as data.bytes.
For cypher output data:

NSMutableData  *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
// ...
                   dataOut.mutableBytes,
                   dataOut.length,
                   &cryptBytes
// ...
dataOut.length = cryptBytes;

I would make my API call similar to:

- (NSString *)encryptWithSerpentText:(NSString *)text key:(NSData *)key iv:(NSData *)iv;

The Mac is good but how will you validate it. Best to research best practices for authentication.

For HMAC just use the Apple supplied CommonCrypto library. CommonCrypto also supports: AES128, DES, 3DES, CAST, RC4 and Blowfish but not Serpent.

Finally, to get things working log each step, both in the server and the client, that way the step that is failing with incorrect data can be found. In this case a straight C++ version against the hybrid Objective-C/C++ version.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top