Wednesday, November 27, 2013

AeroGear iOS lib - 1.3.0 Release

Today, we are happy to announce the immediate availability of 1.3.0 of the AeroGear iOS library. As with our JS and Android supported platforms, main focus of this release was enabling local data encryption. Further, welcome additions such as multipart upload support and new SQLite adapter for our DataManager interface made onto this release.
Let's dive in..

aerogear-crypto-ios
"Crypto for Humans"

Applying cryptographic techniques is hard. iOS platform makes it even harder, firstly by having the functionality dispersed across different frameworks and secondly (and most important), interacting with those frameworks is not the most easy task in the world.  This became clear evidence, as we started to build local data encryption support for our DataManager.  So, we decided to spin off a new iOS library, aerogear-crypto-ios. As stated in the project's web site, main aim is to provide useful and easy to use API interfaces for performing advanced cryptographic techniques in the iOS platform.

Let's see an example on how you can encrypt/decrypt a simple String object:

NSString *stringToEncrypt = @"I want to keep it secret";
// encode string into data
NSData *dataToEncrypt = [stringToEncrypt dataUsingEncoding:NSUTF8StringEncoding];

// set up crypto params..
// generate symmetric key
NSData *encryptionKey = [pbkdf2 deriveKey:@"password4me" salt:[AGRandomGenerator randomBytes]];
// ..and a random Initialisation Vector (IV)
NSData *IV = [AGRandomGenerator randomBytes;

// init the crypto engine
AGCryptoBox *cryptoBox = [[AGCryptoBox alloc] initWithKey:encryptionKey];

// encrypt
NSData *encryptedData = [cryptoBox encrypt:dataToEncrypt IV:IV];

// decrypt
NSData *decryptedData = [cryptoBox decrypt:encryptedData IV:IV];
 

You will appreciate the 'cleanness' of the code. In four lines (excluding the declarations), we derived a PBKDF2 based encryption key, initialised our encryption engine for Symmetric Encryption, and use it to encrypt/decrypt our data.

That is our major aim for our next items to come. Provide as much as possible cleaner interfaces to the underlying crypto functionality offered by the platform (but not limited to), so the developer can stay focus on what is important, an easy-to-easy API to add crypto in their own applications. If you are interested in Crypto and got frustrated by the number of hoops you needed to jump to implement it in your own iOS applications, that's the perfect time to jump in. We love to hear your ideas and suggestions to help shape the project and move forward.

For detailed information on the API, check our iOS cookbook guide. Further, since Christmas is coming, we built a demo application that utilises the crypto library to encrypt your present wishes! My colleague Corinne has already blogged about it, so head to her blog post for more information.

DataManager

Encrypted Plist/Memory

Built on the security foundation provided by the crypto library, this release adds encrypted variants of the existing in-memory and plist based data stores (with an sqlite variant coming next release).  If you have been using the existing data stores to persist your data, you will be happy to know that you can easily switch to an encrypted variant by simple changing the type and some small (really!) amount of code to provide the necessary crypto params.

Let's see a example of using an encrypted plist:

// randomly generate salt
NSData *salt = [AGRandomGenerator randomBytes];

// set up crypto params configuration object
AGPassphraseCryptoConfig *config = [[AGPassphraseCryptoConfig alloc] init];
[config setSalt:salt];
[config setPassphrase:self.password.text];

// initialize the encryption service passing the config
id<AGEncryptionService> encService = [[AGKeyManager manager] keyService:config];

// access Store Manager
AGDataManager *manager = [AGDataManager manager];

// create store
store = [manager store:^(id<AGStoreConfig> config) {
    [config setName:@"CredentialsStorage"];
    [config setType:@"ENCRYPTED_PLIST"];
    [config setEncryptionService:encService];
}];

// ok time to attempt reading..
NSArray *data = [store readAll])

if (data)
    // decryption succeeded!
 

Detailed description of the API can be found on our iOS cookbook guide. In a nutshell, we introduced a new configuration parameter in the store that accepts an encryption service. The store will then use the service to transparently encrypt/decrypt data as they pass in. Currently a default symmetric encryption service based on PBKDF2 is implemented, but we have more plans in the future so stay tuned!

To showcase the crypto store, we built a "Password Manager" that allows a user to store passwords encrypted. Head over to the demo page to try it out yourself or watch a small video demonstrating it in action!


SQLite Adapter

A new adapter has been added on our DataManager that utilises an SQLite database as its backend. Simply specify 'SQLite' when creating a store and you can start using it immediately:

// create the datamanager
AGDataManager* dm = [AGDataManager manager];
// add a new SQLite store object
id<AGStore> = [dm store:^(id<AGStoreConfig> config) {
   [config setName:@"tasks"];
   [config setType:@"SQLITE"];
}];

The read, reset or remove API behave the same, as on our other store types, but this time objects are persisted in the sqlite database.

Don't forget to checkout our 'Recipe' application on our ios-cookbook, for example usage of the SQLite to store your favourite recipes!


Pipeline

Multipart upload

Support has been added on the Pipe interface to perform a multipart upload. No new API has been introduced, but instead we utilise the existing 'save' operation checking if the dictionary passed in contains instances of NSURL objects that point to local files. If that is the case, we kickstart the upload process.

Here is an example snippet, which uploads two images files on a server:

 // the files to be uploaded
 NSURL *file1 = [[NSBundle mainBundle] URLForResource:@"picture1" withExtension:@"jpg"];
 NSURL *file2 = [[NSBundle mainBundle] URLForResource:@"picture2" withExtension:@"jpg"];

 // construct the data to sent with the files added
 NSDictionary *dict = @{@"somekey": @"somevalue", @"jboss.jpg":file1, @"jboss2.jpg":file2 };

 // set an (optional) progress block
 [pipe setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
    NSLog(@"Sent bytesWritten=%d totalBytesWritten=%qi of totalBytesExpectedToWrite=%qi bytes", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
 }];

// upload data
[[pipe save:dict success:^(id responseObject) {
    NSLog(@"Successfully uploaded!");

} failure:^(NSError *error) {
    NSLog(@"An error has occured during upload! \n%@", error);
}];

Note the ‘key’ in the dictionary is used as the ‘name’ field in the multipart request and is required. Further, you don’t need to specify the ‘Content-type’ or the ‘filename’ fields as they are automatically determined internally by the last path component of the NSURL object passed in. If the mime-type can’t be determined an ‘application-octet-stream’ would be sent instead.


To sum up, we think that you will enjoy this release. We have already laid our foundation for crypto, an area we are particularly interested in to moving forward as it adds significant value in the iOS community. So go ahead and give the libraries a spin. Download and explore our demos, play and have fun! We will be happy to hear your thoughts, suggestions and ways to improve it better. Join our mailing list, hangout on our #aerogear IRC channel and better, file us bugs!


Thursday, January 31, 2013

AeroGear iOS lib - Milestone 3

Today we are happy to announce the immediate availability of Milestone 3 of the AeroGear iOS library. Focus on this release was API overhaul cleanups as we prepare for the final release, but that didn't stop us to provide two neat new features that we hope our users will find them useful.

Pagination


The first feature is Pagination support of the result set returned by the server. You now have the ability to scroll either backwards or forward in the result set with the API trying to be flexible enough to support different pagination strategies supported by a server. Paging metadata located in the server response (either in the header or in the body) are used to identify the next or the previous result set. For example, in Twitter case, paging metadata are located in the body of the response, using next_page or previous_page JSON keys at the root level to identify the next or previous result set. The location of this metadata as well as naming, is fully configurable during the creation of the Pipe, thus enabling greater flexibility in supporting several different paging strategies. By default, if no configuration is applied, the library will choose to use Web Linking as it's paging strategy.

Here is an example of pagination that goes against the AeroGear Controller Server:

    NSURL* baseURL = [NSURL URLWithString:@"https://controller-aerogear.rhcloud.com/aerogear-controller-demo"];
    AGPipeline* pipeline = [AGPipeline pipelineWithBaseURL:baseURL];
    
    id<AGPipe> cars = [pipeline pipe:^(id<AGPipeConfig> config) {
        [config setName:@"cars-custom"];
        [config setNextIdentifier:@"AG-Links-Next"];
        [config setPreviousIdentifier:@"AG-Links-Previous"];
        [config setMetadataLocation:@"header"];
        // --optional config option--
        // you have the option to set default parameters that
        // will be passed along during pagination kickstart.
        // If not specified, a default query parameter provider
        // will be installed, that uses the names "offset" and
        // "limit" (two popular names found in pagination mechanisms)
        // initialized to 0 (first page) and 10 (ten items per page) respectively.
        [config setParameterProvider:@{@"color" : @"black", @"page" : @"2", @"per_page" : @10}];        
    }];     
First we create our pipeline. Notice that in the Pipe configuration object, we explicitely declare the name of the paging identifiers supported by the server, as well as the the location of these identifiers in the response. Here we used header to specify that the paging identifiers are to be found in the headers of the response. We can also specify body if the identifiers are to be found in the body of the response (like with Twitter's search), or webLinking (default) if your server follows the Web Linking standard.

To kick-start pagination, you use the method readWithParams of the underlying AGPipe, passing your desired query parameters to the server. You have the option not to specify query parameters (by passing nil), which in that case the pipe will use the default parameter provider set during the pipe creation (with the setParameterProvider config option). Upon successfully completion, the pagedResultSet (an enhanced category of NSMutableArray) will allow you to scroll through the result set:

 __block NSMutableArray *pagedResultSet;
 // fetch the first page
 [cars readWithParams:@{@"color" : @"black", @"offset" : @"0", @"limit" : @1} success:^(id responseObject) {
     pagedResultSet = responseObject;

     // do something

 } failure:^(NSError *error) {
     //handle error
 }];

To move forward or backwards in the result set, you simple call next or previous on the pagedResultSet, passing on the familiar callbacks to handle the response:

 
    // go forward..
    [pagedResultSet next:^(id responseObject) {
        // do something

   // go backwards..
   [pagedResultSet previous:^(id responseObject) {
         // do something
         
     } failure:^(NSError *error) {
         // handle error
   }];
    } failure:^(NSError *error) {
        // handle error
    }];

Note that exception cases like moving beyond last or first page is left on the behaviour of the specific server implementation, that is the library will not try treat it differently. Some servers can throw an error (like Twitter or AeroGear Controller does) by responding with an http error response, or simply return an empty list. The user is responsible to cater for exception cases like this.

We suggest you have a look here and here, two examples that go against the two most popular servers Twitter and Github. Further, if you want a feel on how paging is implemented on the other platforms we support (Android and JS) here is a page that collectively describes the API under those platforms.


Property List Storage


Drived by the need to support long term storage of objects and as an exercise of the AGStore mechanism, a simple Property List based extension was implemented. Familiar methods of reading, saving and removing objects are provided, this time though persisting the changes on the filesystem. Here is a quick example, that stores an One Time Password (OTP) secret in the filesystem so that OTP tokens can be generated later (check AeroGear OTP iOS library and demo for more information on OTP).

    AGDataManager* manager = [AGDataManager manager];
    id<AGStore> plistStore = [manager store:^(id<AGStoreConfig> config) {
       [config setName:@"secrets"];
       [config setType:@"PLIST"];
    }];

Here we initialize the store using a PLIST as type to identify that we want a property list storage and giving up a name that will be also used as the filename.

Now we can simple call the familiar methods to read and save objects in the store:

    // the object to save (e.g. a dictionary)
    NSDictionary *saveOtp = [NSDictionary dictionaryWithObjectsAndKeys:@"19a01df0281afcdbe", @"otp", @"1", @"id", nil];

    // save it
    [plistStore save:saveOtp success:^(id object) {
        // the object has been saved
    } failure:^(NSError *error) {
        //handle error
    }];

    // read it back (note that we pass the ID)
    [plistStore read:@"1" success:^(id object) {
        id readOtp = object;
    } failure:^(NSError *error) {
        //handle error
    }];  

So go ahead and give them a try. We will be happy to hear your thoughts, suggestions and ways to improve it better. Join our mailing list, hangout on our #aerogear IRC channel and better, file us bugs!

Enjoy!