C# on BB10

I recently managed to get a C# application running on a BB10 device, using a port of the mono interpreter – monoberry (https://github.com/roblillack/monoberry). The process was a little painful, mainly due to a lack of documentation, so I thought I’d walk people through the process.

I started from a virgin build of Ubuntu 12.04 – below are the steps I followed…

Prerequisites

Prior to installing monoberry, you need to download and install the BB10 NDK. By default, this will install into /opt/bbndk/

A number of packages are required for the install. After a little trial and error, the below is, I think, sufficient.

$ sudo apt-get install autoconf automake libtool git mono-complete gettext openjdk-7-jre g++ libglib2.0-dev libfontconfig1-dev

Building monoberry

Monoberry is stored on github, and can be downloaded and installed as follows. Note that a number of other libraries are downloaded, via .gitmodules, including a fork of mono – I’m not sure how out of date this fork may be.

$ git clone git://github.com/roblillack/monoberry.git
$ cd monoberry
$ make 
$ sudo make install

Configuring monoberry

You need to setup the rc file (~/.monoberryrc) and copy the debug token bar file.

1) RC file
The init file should be created as ~/.monoberryrc. This is made up of two parts, a [core] sections and a list of [device.xxx] sections. These are documented here: http://burningsoda.com/software/monoberry/

For reference, below is my file with a little obfuscation. I have a simulator (sim) and Dev Alpha (dev):-

[core]
author_name = ABCDEF
author_id = XXXXXX-XXXXXXXXXXXXX
nativesdk = /opt/bbndk/
debug_token = /home/ian/.monoberry/debugtoken.bar

[device.dev]
arch = armle-v7
ip = 10.1.1.169
password = devicepassword
pin = 1234FFFE

[device.sim]
arch = x86
ip = 10.1.1.20 
password = devicepassword
pin = 1234ffff

2) Debug token
In order to do the majority of interactions with devices, using monoberry (wrapping some NDK tools), a debug token needs to be installed. The method I used was as follows – this is not necessarily optimal, but it worked.

Firstly, follow the standard RIM instructions to install the PBDT and RDK files from RIM, and turn them into signing keys, inside the Momentics QNX IDE. Then, within the IDE, go to Window->Preferences->BlackBerry->Signing and click on the debug token you want. If one isn’t present, follow the RIM instructions to create a debug token and deploy it to the devices. Click on “Details” and copy the Author and Author Id fields into the above monoberryrc file.

Next, go to the path given for that debugtoken.bar file, and copy it into the path/location given in the monoberryrc file. If you’ll want to sign files in the future, copy the Author.p12 file (the location can be found in the above Preferences panel) to ~/.rim/author.p12.

Building your first app

The sample apps have .csproj files, rather than makefiles. These can be built using either monodevelop or xbuild. To build:-

$ xbuild paint.csproj /p:Configuration=Release

If you get errors, especially if they’re related to missing references, it’s probably because there’s either a path issue or a missing reference. To fix, I recommend opening the .csproj in monodevelop, and fixing the references. The most likely contender here is a missing libblackberry.dll – right click on References, select Edit References, then browse to the dll and add it.

Deploying the app

To run the app on a target device:-

$ cd APPNAME/bin/Release/
$ monoberry debug APPNAME.exe DEVICE
  [where APPNAME is the name of the built .exe, and DEVICE is the device/sim to run on]

The exe will get packaged into a bar, and uploaded. You should see a “result::success” following by a “Sending Request: Launch” and a couple more steps.

If you see:-

  • result::failure 881: application author does not match debug token author: Most likely the author_name in ~/.monoberryrc doesn’t match that in the debug token (unzip the debug token, and look in the META-INF/MANIFEST.MF file for the line Package-Author: )
  • Other errors involving the debug_token – check that the same debug token is on the device as ~/.monoberry/debugtoken.bar and that the Package-Author in the bar = author_name in the rc, and that Package-Author-Id = author_id.

Additionally/alternately, you can create the .bar file yourself and upload it. This is performed with the following command:-

$ cd APPNAME/bin/Release/
$ monoberry package APPNAME.exe

In addition to packaging the .exe and dependencies into a .bar file, this command will also create a simple app-descriptor.xml file (aka the bar descriptor file – more info here). I think you can edit this file if desired, and it will be used if you reissude the package command.

Extending the (limited) API

Currently, monoberry only supports a tiny subset of the available NDK APIs:- Button, Camera, Dialog, Event, Navigator, Platform Services, and a subset of Screen. However, this doesn’t limit your access to the NDK APIs, just makes them a little harder to access.

The NDK is comprised of header files, and libraries. By reviewing the header files, and the online documentation at http://developer.blackberry.com/native/reference/bb10/library_support_at_a_glance.html you can see what functions etc are available and what their prototypes are. You can then use P/Invoke to call native code in the DLLs. You then have the option to either: a) directly access the NDK APIs, or b) wrap them into a cleaner C# class, and then access via that. I recommend the latter approach. It is cleaner, and you have the option to add the code to the monoberry libblackberry.

For an example of this, please see: https://github.com/roblillack/monoberry/blob/master/libblackberry/dialog/Dialog.cs

Summary

In summary, following the above instructions it is relatively trivial to setup a C# build and deployment process for BlackBerry 10 (and, I would imagine, BB PlayBook). Currently there is very limited support for NDK libraries, however there is hope that this will expand as a) the project is still under active development, and b) it’s relatively easy to wrap the libraries with P/Invoke as needed.

There are still a number of questions outstanding though – the key one being ease of debugging. NDK debugging is normally handled via GDB, however this won’t work with an interpreted environment like this. Non-UI and non-BB10-specific components will be able to be run and debugged in native (i.e. Linux/Windows/Mac) environments with IDEs, but when the app is deployed on a device life will get much harder. This is currently an open question, and one to keep an eye on. Options include use of the Console, together with blackberry-connect and ssh, to log text output, but for those of us used to easy debugging using an integrated IDE, development in C# for BB10 will be a shock.

Still, a useful beginning, and I wish the project the best – I know I will be using it for my cross-platform development.

Update 30/1/2013

It turns out, I may have been too pessimistic about debugging. Mono comes with remote debugging support, and using the soft debugger I think I can debug live on the BB10 device. Even better, I can do it from Visual Studio! For info on the mono debugger, see: http://www.mono-project.com/Debugger and for info on debugging from VS there’s http://mono-tools.com/Debug.aspx I’ll update this post when I get a chance to try it out.

Advertisements

VPN on PlayBook

I want to be able to access my files on the move, wherever I am. Whilst cloud services such as dropbox (www.dropbox.com) are fantastic, I have a few too many files to use these exclusively.

Also, many websites don’t use SSL for all their data, only for authentication. Whilst this protects your password, it doesn’t protect your session token after login. Thus anyone can sniff and steal it.

So, I decided to try to get my new toy – a BlackBerry PlayBook – working on my VPN. Until now I have been using my Cisco ASA as a VPN endpoint, but I don’t like how VPNs and ACLs work together on the ASA, and so decided to roll a new VPN in my DMZ. I used strongswan, and it was fantastically easy.

So my network looks like this:-

internet -- ASA -- extranet -- PIX -- dmz
                 |
             internal network

The extranet is a network which allows pretty-much unfettered access through the ASA, whereas the DMZ has a lot of controls put on it by both the PIX and the ASA. Note that the PIX is oriented so the external interface is facing the DMZ. IP address assignments are (all /24 subnets):-

  • ASA: outside=public static IP(outside_ip2), internal=10.1.1.1, extranet=10.1.98.1
  • PIX: extranet=10.1.98.2, dmz=10.1.99.2
  • ipsec server in dmz at: 10.1.99.10
  • pool of virtual IPs for ipsec clients: 10.1.100.0
  • dns server in internal at: 10.1.1.53

The VPN endpoint is on the dmz.

Step 1: Install and setup strongswan
I won’t cover installation, as it’s very distro-specific, however a few pointers:-

  1. Make sure you get the right kernel modules. I had a problem with this for a while. You need the following:-
    • PF_KEY sockets
    • IP: AH transformation
    • IP: ESP transformation
    • IP: IPComp transformation
    • IP: IPsec transport mode
    • IP: IPsec tunnel mode
    • IP: IPsec BEET mode
    • Network packet filtering framework (Netfilter)
      • Advanced netfilter configuration [Note: If you set this to N, you’ll get lots of the below for free – may be an easier way to do things]
      • Core Netfilter Configuration [M]
      • Netfilter connection tracking support [M]
      • (you may want to add all the protocols you’ll want to support) [M]
      • Netfilter Xtables support [M]
      • “esp” match support [M]
      • IPsec “policy” match support [M]
      • “state” match support [M]
    • IP: Netfilter Configuration
      • IPv4 connection tracking support [M]
      • IP tables support [M]
      • Packet filtering [M]
      • REJECT target support [M]
      • LOG target support [M]
  2. Install strongswan. I shall assume the /etc files go in the normal places.
  3. /etc/ipsec.conf
    config setup
    	# Some standard settings. We're not going to use certs, so I don't care about CRLs
    	crlcheckinterval=180
    	strictcrlpolicy=no
    	plutostart=no
    
    conn %default
    	ikelifetime=60m
    	keylife=20m
    	rekeymargin=3m
    	keyingtries=1
    	keyexchange=ikev2
    	dpdaction=clear           #What to do in the event of a dead peer
    	dpddelay=300s
    
    conn rem
    	left=10.1.99.10           #The IP of my IPSec endpoint
    	leftsubnet=0.0.0.0/0    #This will tell the client to send everything through me
    	leftauth=psk               #The server will auth to the client with a PSK
    	#leftfirewall=yes          #This could be set to auto modify iptables rules
    	right=%any
    	rightsourceip=10.1.100.0/24  #A pool of IPs to assign the client
    	rightauth=eap-mschapv2      #The client will auth with EAP-MSCHAPv2
    	righsendcert=never
    	eap_identity=%any
    	auto=start
  4. /etc/strongswan.conf [generally standard, but I added a couple of lines, as shown]
    charon {
    	dns1 = 10.1.1.53
    	dns2 = 87.194.255.154
    ...
  5. /etc/ipsec.secrets The PSK is for the server to auth to the client (but also IIRC needs to be known by the client even if it’s not being used for server auth), the EAP is the plaintext password of the user. They’ll need to supply domain, username, and password. Note: chmod 600 this file to root, as it contains plaintext passwords.
    : PSK longstring1
    domain\user : EAP "longstring2"
  6. A little gotcha here – I tried testing the PB at this point, but there seems to be some issue when it’s sat on the same net as the strongswan, so I moved straight to the next step…
  7. Firewalls. Both the ASA and PIX needed acl changes to allow the IPsec traffic through, and to allow the ipsec clients access to the internet. We are going to nat the ipsec endpoint onto outside_ip2.
    ASA:

    access-list outside_access_in remark Allow IPSec to ipsec server
    access-list outside_access_in extended permit esp any host outside_ip2
    access-list outside_access_in extended permit ah any host outside_ip2
    access-list outside_access_in extended permit udp any eq isakmp host outside_ip2 eq isakmp
    access-list outside_access_in extended permit udp any eq 4500 host outside_ip2
    eq 4500
    ; We want to allow anything from (inside, external) through (outside) to be nat'ed
    ; but traffic between inside and external shouldn't be. nat_exempt has a list of ACEs
    ; which will not be nat'ed
    global (outside) 1 interface
    nat (inside) 0 access-list nat_exempt
    nat (inside) 1 0.0.0.0 0.0.0.0
    nat (external) 0 access-list nat_exempt
    nat (external) 1 0.0.0.0 0.0.0.0
    access-group outside_access_in in interface outside
    ; Both the DMZ and VPN virtual IPs are on the other side of the PIX
    route external 10.1.99.0 255.255.255.0 10.1.98.2 1
    route external 10.1.100.0 255.255.255.0 10.1.98.2 1
    ; Finally, NAT the IPSec server onto outside_ip2
    static (external,outside) outside_ip2 10.1.99.10 netmask 255.255.255.255

    PIX:

    ; acl_int_inbound applies to any traffic going to the DMZ
    access-list acl_int_inbound remark Allow IPSec to ipsec host
    access-list acl_int_inbound permit ah any host 10.1.99.10
    access-list acl_int_inbound permit esp any host 10.1.99.10
    access-list acl_int_inbound permit udp any eq isakmp host 10.1.99.10 eq isakmp
    access-list acl_int_inbound permit udp any eq 4500 host 10.1.99.10 eq 4500
    access-list acl_int_inbound remark Allow remote administration
    access-list acl_int_inbound permit tcp 10.1.1.0 255.255.255.0 any eq ssh
    access-list acl_int_inbound remark Allow inbound useful ICMP
    access-list acl_int_inbound permit icmp any 10.1.99.0 255.255.255.0 echo-reply
    access-list acl_int_inbound permit icmp any 10.1.99.0 255.255.255.0 echo
    access-list acl_int_inbound permit icmp any 10.1.99.0 255.255.255.0 source-quench
    access-list acl_int_inbound permit icmp any 10.1.99.0 255.255.255.0 unreachable
    access-list acl_int_inbound permit icmp any 10.1.99.0 255.255.255.0 time-exceeded
    ; acl_ext_inbound applies to any traffic coming from the DMZ
    access-list acl_ext_inbound remark Allow ipsec back out
    access-list acl_ext_inbound permit ah host 10.1.99.10 any
    access-list acl_ext_inbound permit esp host 10.1.99.10 any
    access-list acl_ext_inbound permit udp host 10.1.99.10 eq isakmp any eq isakmp
    access-list acl_ext_inbound permit udp host 10.1.99.10 eq 4500 any eq 4500
    access-list acl_ext_inbound remark Allow Access to some internal services
    access-list acl_ext_inbound permit tcp 10.1.100.0 255.255.255.0 host 10.1.1.11 eq www
    access-list acl_ext_inbound permit tcp 10.1.100.0 255.255.255.0 host 10.1.1.11 eq ssh
    access-list acl_ext_inbound permit tcp 10.1.100.0 255.255.255.0 host 10.1.1.11 eq https
    access-list acl_ext_inbound permit tcp 10.1.100.0 255.255.255.0 host 10.1.1.11 eq 8022
    access-list acl_ext_inbound remark Reserved to allow streaming in future
    access-list acl_ext_inbound permit tcp 10.1.100.0 255.255.255.0 host 10.1.1.11 range 6000 6500
    access-list acl_ext_inbound remark Allow DNS
    ; note: this could cause problems for large DNS (e.g. DNSSEC) queries, as tcp will be used
    ;     to fix, just add two more rules to add 53/tcp
    access-list acl_ext_inbound permit udp 10.1.100.0 255.255.255.0 host 10.1.1.53 eq domain
    access-list acl_ext_inbound permit udp 10.1.99.0 255.255.255.0 host 10.1.1.53 eq domain
    ; we want to allow the DMZ unfettered access to the internet, but not to our internal network
    ; the easiest way to do this is explicitly deny access to the internal, and then allow everything else
    access-list acl_ext_inbound remark Deny access to any other internal network
    access-list acl_ext_inbound deny ip any 10.1.0.0 255.255.0.0
    access-list acl_ext_inbound remark But allow browsing of internet from ipsec
    access-list acl_ext_inbound permit ip 10.1.100.0 255.255.255.0 any
    access-list acl_ext_inbound permit ip 10.1.99.0 255.255.255.0 any
    ; PIX firewalls are predisposed to NAT from the internal to external interfaces.
    ; The global() and nat() rules stop that - I just want the PIX as a firewall.
    access-list nat_exempt permit ip any any
    global (outside) 1 interface
    global (inside) 2 interface
    nat (outside) 0 access-list nat_exempt outside
    nat (inside) 0 access-list nat_exempt
    access-group acl_ext_inbound in interface outside
    access-group acl_int_inbound in interface inside
    ; And finally, how to get out
    route inside 0.0.0.0 0.0.0.0 10.1.98.1 1
    route outside 10.1.100.0 255.255.255.0 10.1.99.10 1
  8. Pray. And test. This should be working now, once all the services are started etc. A trick for the debugging is follow the packets through your system. If it’s not working, use a packet capture in the ASA and PIX to see if you’re seeing the IKE traffic in _and_out. If you’re not, then it’s either a firewall or routing issue. To check both, use the packet-tracer command (on the ASA, PIX doesn’t have it). Also set up logging to a syslog server from both firewalls (note: make sure you do a “logging trap warnings” else you won’t see much). Finally, have “tail -f /var/log/messages” going to catch any messages from strongswan as well.
  9. On the PlayBook (and these instructions should generally apply to any client):-
    1. Go to “Options”->”Security”
    2. Scroll down to “VPN”
    3. Press “Add New” at the bottom
    4. Populate the fields:
      • Profile Name: [whatever you want]
      • Server Address: outside_ip2 [external IP of server]
      • Gateway Type: Generic IKEv2 VPN Server
      • Authentication Type: EAP-MSCHAPv2
      • Authentication ID Type: IPv4
      • MSCHAPv2 EAP Identity: [whatever you want]
      • MSCHAPv2 Username: domain\user [from above example (ipsec.secrets)]
      • MSCHAPv2 Password: longstring2 [from above example (ipsec.secrets)]
      • Gateway Auth Type: PSK
      • Gateway Auth ID Type: IPv4
      • Gateway Preshared Key: longstring1 [from above example (ipsec.secrets)]
      • Automatically Determine IP: checked
      • Dynamically Determine DNS: checked
      • Perfect Forward Secrecy: empty
      • Manual Algorithm Selection: empty
    5. Click “Save and Connect”, and cross your fingers…
    6. On the VPN page, you should see your new VPN profile added. If you click on the cog button on the bottom left, you should be able to see the nicely populated VPN details.
  10. And finally, enjoy.

BigInt in ActionScript 3.0

Recently I have been working on an ActionScript 3.0 (aka Adobe AIR) implementation of SSH for my PlayBook. Why? you may ask. Well, partly for the hell of it, partly to learn AIR/ActionScript (a bit of a grotesque language, but that’s for another post)), and partly because there isn’t one available at this time and I want to be able to admin some boxen from wherever I happen to be.

Unfortunately, the PlayBook currently doesn’t make its crypto APIs available to the public via ActionScript. Which is annoying. Not to be put off however, I cast around for some alternatives.

There are some ActionScript crypt libraries already out there (notably AS3Crypto: http://code.google.com/p/as3crypto/) however unfortuately that doesn’t support Diffie-Hellman. So, back to first principles…

My maths is veeeery rusty though, and rather than re-invent the wheel regarding “g^x mod p” etc, I looked around for a BigInt library. Again, AS3Crypto has some support, however their version of the above operation only works if x is an int – hardly useful when x is a big-ass secret ideally some 512+ bits long.

Eventually my eyes lighted on http://www.leemon.com. Leemon Baird has implemented BigInt in JavaScript, and it seemed to fit. Fancying one more challenge, I took that code, and ported it to AS3.

The .js implementation handles bigInt’s as arrays. However, as it’s .js, there’s no typesafe-ness. So I added that, wrapping it into a class (BigInt). The internals are a bit disgusting, as I have ultimately created a private property ‘val’ which is an array, which all the private leemon-methods operate on.

Thus far it seems to work – Diffie Hellman completes correctly in my SSH implementation, and 3DES works. It’s entirely possible that I’ve introduced some bugs however – if anyone has any bigInt test cases they’d like me to run against, I’m more than willing.

Anyway, I now release this library to the world. Do (or not) as you see fit. The file is being hosted in my public dropbox (www.dropbox.com) as there’s no way to host .as or .txt files on wordpress (bad wordpress!).

Enjoy! And please let me know if you use it.

The code can be found here: https://www.dropbox.com/s/s5pjey5jaidwcbb/BigInt.as