Simple UIScrollView / CATiledLayer PDF Example (MonoTouch version)

Monday, 28 February 2011 04:52 by Krumelur

Searching the web for a simple example how to preview a PDF page using a CATiledLayer, I found some code on Olive Toast's blog: http://www.olivetoast.com/blog/2009/08/simple-uiscrollview-catiledlayer-pdf-example/

A very small and lean solution to demonstrate viewing, zooming and scrolling a page of a PDF. The code can easily be extended to a full PDF viewer which uses only very little memory.

I went and MonoTouched the example. You can download the sample's main file here PdfDemo.cs.zip (1,59 kb) . Also check out the source below:

 

using System;
using MonoTouch.Foundation;
using System.IO;
using MonoTouch.UIKit;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using MonoTouch.CoreGraphics;
using MonoTouch.CoreAnimation;

namespace iOSTest
{
    public class Application
    {
        static void Main ( string[] args )
        {
            UIApplication.Main ( args );
        }
    }

    // The name AppDelegate is referenced in the MainWindow.xib file.
    public partial class AppDelegate : UIApplicationDelegate
    {
        // This method is invoked when the application has loaded its UI and its ready to run
        public override bool FinishedLaunching ( UIApplication app, NSDictionary options )
        {
            NSUrl u = NSUrl.FromString("http://www.tfl.gov.uk/assets/downloads/standard-tube-map.pdf");
            this.o = new AppDelegate.PdfViewController(u);
            this.o.View.Frame = new RectangleF(0, 20, 320, 480 - 20);
            window.AddSubview(this.o.View);
           
            window.MakeKeyAndVisible();       
            return true;
        }
       
        private PdfViewController o;
       
        /// <summary>
        /// Previews first page of a PDF.
        /// </summary>
        public class PdfViewController : UIViewController
        {
            public PdfViewController(NSUrl oUrl) : base()
            {
                this.oUrl = oUrl;
            }
           
            private NSUrl oUrl;
            private UIView oContentView;
            private CGPDFDocument oPdfDoc;
            private CGPDFPage oPdfPage;
           
            public override void ViewDidLoad ()
            {
                base.ViewDidLoad ();
                Console.WriteLine("Loading PDF: {0}", this.oUrl.ToString());
                this.oPdfDoc = CGPDFDocument.FromUrl(this.oUrl.ToString());
               
                // For demo purposes, show first page only.
                this.oPdfPage = this.oPdfDoc.GetPage(1);
               
                RectangleF oPdfPageRect = this.oPdfPage.GetBoxRect(CGPDFBox.Crop);
               
                // Setup tiled layer.
                CATiledLayer oTiledLayer = new CATiledLayer();
                oTiledLayer.Delegate = new TiledLayerDelegate(this);
                oTiledLayer.TileSize = new SizeF(1024f, 1024f);
                oTiledLayer.LevelsOfDetail = 5;
                oTiledLayer.LevelsOfDetailBias = 5;
                oTiledLayer.Frame = oPdfPageRect;
               
                this.oContentView = new UIView(oPdfPageRect);
                this.oContentView.Layer.AddSublayer(oTiledLayer);
               
                this.View = new UIView();
                this.View.AutoresizingMask =
                    UIViewAutoresizing.FlexibleWidth
                    | UIViewAutoresizing.FlexibleHeight
                    | UIViewAutoresizing.FlexibleTopMargin
                    | UIViewAutoresizing.FlexibleBottomMargin
                    | UIViewAutoresizing.FlexibleLeftMargin
                    | UIViewAutoresizing.FlexibleRightMargin;
                this.View.AutosizesSubviews = true;
               
#if DEBUG
                this.View.Layer.BorderColor = UIColor.Red.CGColor;
                this.View.Layer.BorderWidth = 2f;
#endif
               
                // Prepare scroll view.
                UIScrollView oScrollView = new UIScrollView(this.View.Frame);
                oScrollView.AutoresizingMask = this.View.AutoresizingMask;
                oScrollView.Delegate = new ScrollViewDelegate(this);
                oScrollView.ContentSize = oPdfPageRect.Size;
                oScrollView.MaximumZoomScale = 1000f;
                oScrollView.MinimumZoomScale = 0.1f;
                oScrollView.AddSubview(this.oContentView);
               
                this.View.AddSubview(oScrollView);
            }
           
            public override void ViewDidUnload ()
            {
                base.ViewDidUnload ();
                this.oPdfPage.Dispose();
                this.oPdfDoc.Dispose();
                this.oContentView.Dispose();
                this.oPdfPage = null;
                this.oPdfDoc = null;
                this.oContentView = null;
            }
           
            public class TiledLayerDelegate : CALayerDelegate
            {
                public TiledLayerDelegate(PdfViewController oParentController) : base()
                {
                    this.oParentController = oParentController;
                }
               
                private PdfViewController oParentController;
               
                public override void DrawLayer (CALayer layer, CGContext context)
                {
                    context.SaveState();
                    context.SetRGBFillColor( 1.0f, 1.0f, 1.0f, 1.0f);
                    context.FillRect( context.GetClipBoundingBox());
                    context.TranslateCTM( 0.0f, layer.Bounds.Size.Height);
                    context.ScaleCTM( 1.0f, -1.0f);
                    context.ConcatCTM( this.oParentController.oPdfPage.GetDrawingTransform(CGPDFBox.Crop, layer.Bounds, 0, true));
                    context.DrawPDFPage(this.oParentController.oPdfPage);
                    context.RestoreState();
                }
            }
           
            public class ScrollViewDelegate : UIScrollViewDelegate
            {
                public ScrollViewDelegate(PdfViewController oParentController) : base()
                {
                    this.oParentController = oParentController;
                }
               
                private PdfViewController oParentController;
               
                public override UIView ViewForZoomingInScrollView (UIScrollView scrollView)
                {
                    return this.oParentController.oContentView;
                }
            }
        }
       
    }
}


Lange Schlange an der Kasse? Kein Problem bei REWE!

Friday, 25 February 2011 06:52 by Krumelur

Lange Schlange an der Kasse? Bei REWE ist das ganz einfach, man muss nur die Klingel betätigen. Dazu hat man verschiedene Möglichkeiten:

  • Für Berufstätige: Man ist NBA-Spieler
  • Für Handwerker: Man stapelt 2m Haribo-Schachteln übereinander
  • Für Sportliche: Man versucht, im Sprung von der Kühltruhe, während des Fluges die Klingel zu erwischen
  • Für Schützen: ein gezielter Schuss auf den Druckknopf, wobei die Entfernung (Höhe) nicht zu unterschätzen ist.

 

iOS 4: Data protection, hardware encryption and other insight

Tuesday, 8 February 2011 16:41 by Krumelur

For quite a while I've been trying to figure out the whole truth about hardware encryption, data protection and keychain protection on iOS4 in combination with iPhone 3GS, iPhone 4 or iPad.

Starting with iPhone 3GS a hardware encryption chip is build into the device. Great! But what does it mean to me as a developer? How can I make use of all of this encrypting and masquerading?

First off, one needs to understand how all the encryption business works on iOS devices.

Best thing to do is to watch Episode 209: "Securing Application Data" from Apple's WWDC 2010 conference (http://developer.apple.com/videos/wwdc/2010/) - note that you have to be a registered iOS developer to access the videos.

Next, navigate to http://anthonyvance.com/blog/forensics/ios4_data_protection/ and read the infos there.

Then understand the iOS devices' different folders by going through this document http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/RuntimeEnvironment/RuntimeEnvironment.html#//apple_ref/doc/uid/TP40007072-CH2-SW10

Now you're set.

For me a few questions remained unanswered even after watching the video and reading dozens of articles on the web. I will try to answer them now as good as I can using my findings:

  1. The keychain allows defining a class "available when unlocked, this  device only" which prevents a keychain record from getting transferred  to another device using backup/restore. To my understanding there is  nothing similar for files, or is there? How can I prevent FILE data being restored on another device?
  2. NSData allows storing files with protection and NSFileManager allows changing the security class of an existing file. I wonder if there are any disadvantages if I first store the file unencrypted and the use NSFileManager to change the class?
  3. If the user does not specify a PIN or passcode, there does not seem to be real protection. Does that mean, data is encrypted using the device key only, as introduced with the 3GS?
  4. If I change my PIN, what has to be re-encrypted by the OS? All of the encrypted files?
  5. Is there evidence that a PIN/or password protected device's content  which was protected using the "protect always" has been successfully  hacked?
  6. My device contains files which are stored in encrypted format. If  now I make a backup of my device in iTunes and do not select to encrypt  and password protect that backup, are my backed up files still which were encrypted on the device still secure?

 

1. How can I prevent FILE data being restored on another device?

Easy answer: it is not possible as of iOS 4.2. The feature is available exclusively for the keychain. However it is possible to prevent data being backed up in the first place by putting it into the applications /Library/Caches folder. This folder won't be included in the backup. Of course it won't be restored to the device either. A usecase for this would be if you download sensitive documents from the web in your app, want to keep them local for offline viewing but want to make sure they will not be passed on to anybody else. If a restore is performed, the user will have to download once again.

An alternative option is to store the device's unique ID in the filesystem and after restore compare it to the current device ID. If they don't match, data cannot be accessed.

Or: store a flag in the keychain and set the security flag to allow restoring to "this device only". If the flag is not there, deny access to files (or delete them). If it is there, you can be sure it is the device the files were written to originally.

2. Change security class after file has been created.

Apple's document linked above says, one should set the encryption once the file has been created but does not have content yet. They do not tell if it is a performance issue or if it is a recommendation to keep the file protected all the time. So I cannot give a clear answer on that one. But if you don't change classes constantly it should not matter. Looking at Episode 209, I would assume that only the file key is reencrypted using the new class which should not have a performance impact.

3. Is data secure if no passcode is defined?

Well, security is relative. Data will be encrypted using the device key only. According to this article (http://www.wired.com/gadgetlab/2009/07/iphone-encryption/), hacking it was easy back then with the 3GS and I think this is still true. So to keep data really secure, encourage your users to have a PIN on the phone or a passcode.

4. Change PIN - what will happen?

Only the keybag will have to be recreated. All files and file keys remain untouched. So changing the PIN is a really fast operation.

5. Has it been hacked?

iOS4 is using a variation of PBKDF2 (http://en.wikipedia.org/wiki/PBKDF2) with 50.000 (not 10.000 as Wikipedia states) iterations. As the device key is part of the encryption, decrypting can only be performed on the device itself which results in a significant low number of attack attempts per second. In addition the hacker has to break the device's PIN/passcode first which is protected by increasing delays between each input trial. I have not found evidence that it has been hacked.

6. Backup & Restore

We only take encrypted files in account. Keychain has similar mechanism and even allows restore to "this device only" (see 1.). Assuming we have an encrypted file which is password/PIN protected on the device. A backup is made and the user specifies a password for the backup. Instead of encrypting using a combination of device ID and PIN, the backup is encrypted using a combination of device ID and backup password. To make it short: your data is secure. It can only go to another device if the password is entered. If however, no backup password is specified, the backup is encrypted using the device key only. Which seems to be easily hackable. So this is not a good thing to do. Encourage your users to pwd protect their backups.

 

One last note on the "Escrow" keybag: the desktop PC/Mac the iOS device is syncing with contains an Escrow keybag which allows syncing the device without the user having to unlock it. Yikes...not good. This was introduced to improve usability. Apple says: if an attacker has the syncing desktop machine under control, he has probably all the valuable files already, so shit has already happened beforehand...well...In my opinion it should be an option. I would better like it to input my PIN everytime.

iOS: Store passwords in the keychain using MonoTouch

Tuesday, 1 February 2011 05:20 by Krumelur

EDIT: I have just updated this post a bit. Storing a password now supports data encryption. This means you can specify when the stored password is accessible (e.g. only if the device is unlocked).

After searching the web a lot I could not find a resource providing examples on how to store a password securely on an iOS device. StackOverflow.com pointed me to the iOS's KeyChain and I found this example which does the magic using ObjectiveC: https://github.com/ldandersen/scifihifi-iphone/

As I want to give the community something back I offer a MonoTouch implementation inspired by to code referenced above for download here.

My code contains three static methods:

/// <summary>
/// Deletes a username/password record.
/// </summary>
/// <param name="sUsername">the username to query. May not be NULL.</param>
/// <param name="sService">the service description to query. May not be NULL.</param>
/// <returns>SecStatusCode.Success if everything went fine, otherwise some other status</returns>
public static SecStatusCode DeletePasswordForUsername ( string sUsername, string sService )

/// <summary>
/// Sets a password for a specific username.
/// </summary>
/// <param name="sUsername">the username to add the password for. May not be NULL.</param>
/// <param name="sPassword">the password to associate with the record. May not be NULL.</param>
/// <param name="sService">the service description to use. May not be NULL.</param>
/// <param name="eSecAccessible">defines how the keychain record is protected</param>
/// <returns>SecStatusCode.Success if everything went fine, otherwise some other status</returns>
public static SecStatusCode SetPasswordForUsername ( string sUsername, string sPassword, string sService, SecAccessible eSecAccessible )

/// <summary>
/// Gets a password for a specific username.
/// </summary>
/// <param name="sUsername">the username to query. May not be NULL.</param>
/// <param name="sService">the service description to use. May not be NULL.</param>
/// <returns>
/// The password or NULL if no matching record was found.
/// </returns>
public static string GetPasswordForUsername ( string sUsername, string sService )

Find the MonoTouch C# file attached for download with this post.

2011_2_KeyChain_MT.cs (4,69 kb)