Archive

Posts Tagged ‘security’

SSH Proxy: Server and Client Side of Using an SSH User Without Shell as Proxy Server

April 30, 2016 Leave a comment

Accessing services on an internal network over a proxy reachable from the Internet.

Imagine you want one of your machines to become a proxy for external users to be able to access local resources or the internet as if they were on that machine. You could run a dedicated proxy server for that, but if the machine provides SSH and you want an easy solution, you can use SSH as well – without risking any shell-related issues (like this user also having access to the local file system).

Why would you want stuff like that in the first place? That’s what proxy servers (and most frequently VPNs) are made for: to access country-, company-, or university-internal resources from outside of that network just as you would from the inside.

Server side

The server side is rather easy. Let’s assume we call that user “sshproxy”. The important thing is to not give your proxy user a shell, which we do here during creation of the user:

sudo useradd -m -d /home/sshproxy -s /bin/false sshproxy # create user and home directory, disable shell

The user’s password is disabled by default (you can check that there’s a ! in /etc/shadow for the sshproxy user). If you don’t want to use passwords but private-public keys (which I would recommend): in /home/sshproxy create the .ssh/ folder and the .ssh/authorized_keys file and ensure they’re readable for the sshproxy user. There you need to add the ssh public keys of people that should be allowed to use the ssh proxy. The cool thing about it is that different real life users can make use of the same sshproxy user: you just need to add to manage the keys of real life users for the sshproxy user. Further, you likely don’t want the sshproxy user to be able to change the authorized keys, therefore make the file read only for that user (e.g. root owned + writeable by owner only). If you have an AllowUsers section in your /etc/ssh/sshd_config, you need to add the sshproxy user there and restart ssh. That’s it on the server side.

User side

If you’re one of the users that need to generate their ssh keypair, you can have a look at:

https://help.ubuntu.com/community/SSH/OpenSSH/Keys#Generating_RSA_Keys
(very easy to understand)

https://stribika.github.io/2015/01/04/secure-secure-shell.html
(details that are good to know if you want to have more secure keys = not so easy to crack)

If you use RSA then use at least 2048 bit key length (4096 can’t hurt…), e.g. with ssh-keygen with the “-b 4096” parameter. If you’re on a Windows machine: you can do the same using e.g. Putty. Don’t forget to use a good password to protect your private key file (usually the “id_rsa” file) and never disclose it – servers only need your public key (usually “id_rsa.pub”).

Using the proxy via Linux shell

Everything’s built in, just use the following command.

ssh -D 8080 -N sshproxy@YOUR-IP-OR-URL -p YOUR-PORT

This command opens the local port 8080 (on the client machine) for proxy tasks. YOUR-PORT-URL and YOUR-PORT correspond to the SSH server running on the server machine. If everything worked fine you won’t get any response in the shell – just leave the terminal open. Make sure your private key file is in ~/.ssh/id_rsa or provide it explicitly with “-i”. If you can’t access the server try removing the “-N”. Then you should see that the server logs you in and out immediately (this means everything works fine from the SSH, proxy and tunnel side – if you add -N again, you should be fine therefore). You can check your local opened ports with nmap (“nmap localhost”). If port 8080 is not in the list before login and opened after login your tunnel works.

Using the proxy via Putty

  • address: YOUR-IP-OR-URL (where SSH server is running)
  • port: YOUR-PORT (where SSH server is running)
  • user: sshproxy
  • Enable the check box “Don’t start a shell or command at all” in “Connection-SSH”
  • Specify your private key in “Auth”
  • Tunnels: add a “dynamic tunnel” on “local port 8080”, leave destination open (should state “D8080” in Putty after you apply)

Sending data to your local port

As you now have a tunnel on port 8080 on your local machine open, you need to send request to this port, instead of your standard gateway. E.g. for your browser you can achieve that configuring the browser to use a proxy (e.g. Firefox: FoxyProxy AddOn: set address “localhost” and port “8080”).

Btw: you can do other cool things with that proxy as well, such as reverse port tunnelling, circumventing firewalls etc. But you should ensure that this is allowed in the company/country you’re in before you get yourself into troubles.

Git security: enabe fsckobjects in ~/.gitconfig:

February 3, 2016 Leave a comment

In order to prevent possible tampering with code in git repositories you work with (e.g. malicious manipulation of objects during clone, fetch, push…), check if these lines exist in your ~/.gitconfig and add them, if they don’t:

[transfer]
fsckobjects = true
[fetch]
fsckobjects = true
[receive]
fsckObjects = true

These enable git checking transferred objects for their integrity using their computed hashes.

Original idea from here: https://groups.google.com/forum/#!topic/binary-transparency/f-BI4o8HZW0
(and the corresponding bug on Debian here: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=813157)

Cracking RC4 messages that use weak RC4 reinitialization for each message part

April 12, 2015 Leave a comment

This post deals with a short excerpt of a security and hacking related exercise at the University of Applied Sciences Upper Austria, Campus Hagenberg: we obtain the plaintext from an intercepted RC4 encoded message, from which we know that it uses an easy to crack re-initialization of the RC4 cipher for each message part.

The problem

Imagine we have intercepted a secret message split into 11 message parts, each (but the last) consisting of 40 1-byte characters:

0xd9, 0xef, 0x7b, 0x6e, 0xca, 0xb5, 0x12, 0xa0, 0x4f, 0x4b, 0x56, 0xb4, 0x94, 0xdf, 0x27, 0xed, 0xf8, 0xcc, 0x64, 0x94, 0xeb, 0x5a, 0x77, 0xea, 0x9b, 0x76, 0xdf, 0xe9, 0x18, 0x02, 0xc6, 0x36, 0x19, 0x1f, 0xc8, 0xf2, 0x5a, 0xa8, 0xda, 0xe7,
0xb7, 0xd3, 0x5f, 0x4e, 0x89, 0x9f, 0x01, 0xa9, 0x0e, 0x4c, 0x5c, 0xb6, 0xc0, 0xdc, 0x2e, 0xfa, 0xbd, 0xbf, 0x63, 0xb4, 0xa4, 0x42, 0x77, 0xb3, 0x98, 0x62, 0xd5, 0xf0, 0x5d, 0x07, 0xdc, 0x62, 0x17, 0x4d, 0xca, 0xe6, 0x5a, 0xa8, 0xdd, 0xec,
0xf9, 0xd3, 0x5c, 0x47, 0x85, 0x86, 0x05, 0xab, 0x5d, 0x4c, 0x5b, 0xf0, 0xc7, 0xc2, 0x22, 0xf8, 0xb4, 0xe6, 0x17, 0xb8, 0xa4, 0x5f, 0x66, 0xb3, 0x80, 0x79, 0xd9, 0xec, 0x18, 0x1d, 0xc0, 0x35, 0x51, 0x4b, 0xca, 0xab, 0x19, 0xaf, 0xc2, 0xe6,
0xe4, 0xc5, 0x0f, 0x5f, 0x82, 0x93, 0x40, 0xb7, 0x47, 0x58, 0x5b, 0xa4, 0x94, 0xdb, 0x2e, 0xfa, 0xb9, 0xf2, 0x52, 0xa8, 0xae, 0x43, 0x61, 0xb3, 0xbc, 0x7e, 0xc2, 0xf3, 0x18, 0x18, 0xd6, 0x62, 0x1c, 0x50, 0xc1, 0xe2, 0x1c, 0xae, 0xce, 0xe8,
0xe3, 0xc9, 0x40, 0x45, 0x99, 0xd6, 0x32, 0x96, 0x6f, 0x1f, 0x56, 0xbe, 0xd7, 0xd9, 0x36, 0xf8, 0xac, 0xf6, 0x58, 0xb2, 0xeb, 0x46, 0x7d, 0xe1, 0x80, 0x64, 0x96, 0xec, 0x59, 0x0c, 0x8f, 0x24, 0x10, 0x4c, 0xd1, 0xee, 0x08, 0xe7, 0x8d, 0xa9,
0xc3, 0xef, 0x6b, 0x64, 0xca, 0xb3, 0x0e, 0xa6, 0x5c, 0x46, 0x43, 0xa4, 0x94, 0xc9, 0x26, 0xfc, 0xbb, 0xf0, 0x5e, 0xb2, 0xeb, 0x41, 0x60, 0xfa, 0x9d, 0x76, 0xc2, 0xfe, 0x18, 0x1e, 0xca, 0x3b, 0x02, 0x1f, 0xd2, 0xe2, 0x0e, 0xaf, 0x8d, 0xce,
0xc7, 0xe7, 0x0f, 0x41, 0x9f, 0x85, 0x14, 0xe5, 0x4c, 0x5a, 0x13, 0xb2, 0xd1, 0x8b, 0x3c, 0xfd, 0xaa, 0xfa, 0x17, 0xbe, 0xae, 0x52, 0x73, 0xe6, 0x98, 0x72, 0x96, 0xef, 0x50, 0x10, 0xd6, 0x62, 0x19, 0x5e, 0xd3, 0xee, 0x5a, 0xa5, 0xc8, 0xec,
0xf9, 0x80, 0x4a, 0x53, 0x9a, 0x99, 0x12, 0xb1, 0x4b, 0x5b, 0x13, 0xa5, 0xda, 0xdb, 0x3d, 0xe7, 0xac, 0xfa, 0x54, 0xa8, 0xae, 0x55, 0x32, 0xc4, 0x82, 0x7b, 0xda, 0xbb, 0x4b, 0x10, 0xc1, 0x26, 0x51, 0x4b, 0xcd, 0xee, 0x17, 0xe7, 0xf9, 0xc5,
0xc4, 0x80, 0x5f, 0x59, 0x85, 0x82, 0x05, 0xa6, 0x5a, 0x5a, 0x57, 0xf0, 0xd6, 0xde, 0x3b, 0xa8, 0xbb, 0xfe, 0x59, 0xfc, 0xb2, 0x5e, 0x67, 0xb3, 0x9f, 0x65, 0xc3, 0xe8, 0x4c, 0x55, 0xfb, 0x0e, 0x22, 0x1f, 0xf5, 0xea, 0x09, 0xb4, 0xda, 0xe6,
0xe5, 0xc4, 0x0f, 0x4d, 0x85, 0x84, 0x40, 0xa1, 0x4f, 0x49, 0x5a, 0xb4, 0xd0, 0xde, 0x21, 0xe6, 0xb6, 0xfa, 0x43, 0xfc, 0xa0, 0x54, 0x6b, 0xb3, 0x82, 0x64, 0x96, 0xc2, 0x48, 0x33, 0xcd, 0x26, 0x25, 0x73, 0xce, 0xe8, 0x2e, 0x8a, 0xc8, 0xcb,
0xff, 0xf6, 0x5a, 0x0b, 0xca, 0xb2, 0x01, 0xb3, 0x47, 0x5b

We know all these message parts have been encrypted using RC4 with a known to be easy to crack reinitialization of the RC4 stream for each message part. We also know the message plaintext only contains letters and whitespaces. The task is: obtain the plaintext. How to do so?

The solution

We know all 11 message parts have been encrypted using the same key stream (as the RC4 cipher has been re-initialized for each message using the same secret key). To avoid confusion here: as stream cipher, RC4 gets initialized from a secret key (e.g. the user’s password) and generates a continuous key stream from it. Each time a RC4 cipher is initialized this way, the generated key stream is exactly the same. Therefore, the RC4 reinitialization for each package causes the key stream to be exactly the same for all messages. RC4 does an XOR on the key stream and the plaintext to obtain the ciphertext. Hence, an XOR with the key stream used for all 11 message ciphertexts causes these to become correct plaintext. We further know that valid plaintext only allows certain characters. Therefore it’s possible to try through all 255 possibilities per keystream character: XOR each one with all corresponding characters of all ciphertexts and keep only those, where all resulting plaintext characters are actually allowed ones. By doing so we generate a list of valid keystreams in an effective way (which by doing an XOR on the ciphertext all result in valid plaintext). Trying through these keys (XOR with plaintext) until we see a message that “makes sense” is now very easy – and could even be done by hand (not posting the actual solution here).

# Cracking RC4 messages that use weak RC4 reinitialization for each message part
#
# Rainhard Findling
# University of Applied Sciences Upper Austria, Campus Hagenberg
# 2015/03
# 
# all ciphertext message parts
c1 = [0xd9, 0xef, 0x7b, 0x6e, 0xca, 0xb5, 0x12, 0xa0, 0x4f, 0x4b, 0x56, 0xb4, 0x94, 0xdf, 0x27, 0xed, 0xf8, 0xcc, 0x64, 0x94, 0xeb, 0x5a, 0x77, 0xea, 0x9b, 0x76, 0xdf, 0xe9, 0x18, 0x02, 0xc6, 0x36, 0x19, 0x1f, 0xc8, 0xf2, 0x5a, 0xa8, 0xda, 0xe7]

c2 = [0xb7, 0xd3, 0x5f, 0x4e, 0x89, 0x9f, 0x01, 0xa9, 0x0e, 0x4c, 0x5c, 0xb6, 0xc0, 0xdc, 0x2e, 0xfa, 0xbd, 0xbf, 0x63, 0xb4, 0xa4, 0x42, 0x77, 0xb3, 0x98, 0x62, 0xd5, 0xf0, 0x5d, 0x07, 0xdc, 0x62, 0x17, 0x4d, 0xca, 0xe6, 0x5a, 0xa8, 0xdd, 0xec]

c3 = [0xf9, 0xd3, 0x5c, 0x47, 0x85, 0x86, 0x05, 0xab, 0x5d, 0x4c, 0x5b, 0xf0, 0xc7, 0xc2, 0x22, 0xf8, 0xb4, 0xe6, 0x17, 0xb8, 0xa4, 0x5f, 0x66, 0xb3, 0x80, 0x79, 0xd9, 0xec, 0x18, 0x1d, 0xc0, 0x35, 0x51, 0x4b, 0xca, 0xab, 0x19, 0xaf, 0xc2, 0xe6]

c4 = [0xe4, 0xc5, 0x0f, 0x5f, 0x82, 0x93, 0x40, 0xb7, 0x47, 0x58, 0x5b, 0xa4, 0x94, 0xdb, 0x2e, 0xfa, 0xb9, 0xf2, 0x52, 0xa8, 0xae, 0x43, 0x61, 0xb3, 0xbc, 0x7e, 0xc2, 0xf3, 0x18, 0x18, 0xd6, 0x62, 0x1c, 0x50, 0xc1, 0xe2, 0x1c, 0xae, 0xce, 0xe8]

c5 = [0xe3, 0xc9, 0x40, 0x45, 0x99, 0xd6, 0x32, 0x96, 0x6f, 0x1f, 0x56, 0xbe, 0xd7, 0xd9, 0x36, 0xf8, 0xac, 0xf6, 0x58, 0xb2, 0xeb, 0x46, 0x7d, 0xe1, 0x80, 0x64, 0x96, 0xec, 0x59, 0x0c, 0x8f, 0x24, 0x10, 0x4c, 0xd1, 0xee, 0x08, 0xe7, 0x8d, 0xa9]

c6 = [0xc3, 0xef, 0x6b, 0x64, 0xca, 0xb3, 0x0e, 0xa6, 0x5c, 0x46, 0x43, 0xa4, 0x94, 0xc9, 0x26, 0xfc, 0xbb, 0xf0, 0x5e, 0xb2, 0xeb, 0x41, 0x60, 0xfa, 0x9d, 0x76, 0xc2, 0xfe, 0x18, 0x1e, 0xca, 0x3b, 0x02, 0x1f, 0xd2, 0xe2, 0x0e, 0xaf, 0x8d, 0xce]

c7 = [0xc7, 0xe7, 0x0f, 0x41, 0x9f, 0x85, 0x14, 0xe5, 0x4c, 0x5a, 0x13, 0xb2, 0xd1, 0x8b, 0x3c, 0xfd, 0xaa, 0xfa, 0x17, 0xbe, 0xae, 0x52, 0x73, 0xe6, 0x98, 0x72, 0x96, 0xef, 0x50, 0x10, 0xd6, 0x62, 0x19, 0x5e, 0xd3, 0xee, 0x5a, 0xa5, 0xc8, 0xec]

c8 = [0xf9, 0x80, 0x4a, 0x53, 0x9a, 0x99, 0x12, 0xb1, 0x4b, 0x5b, 0x13, 0xa5, 0xda, 0xdb, 0x3d, 0xe7, 0xac, 0xfa, 0x54, 0xa8, 0xae, 0x55, 0x32, 0xc4, 0x82, 0x7b, 0xda, 0xbb, 0x4b, 0x10, 0xc1, 0x26, 0x51, 0x4b, 0xcd, 0xee, 0x17, 0xe7, 0xf9, 0xc5]

c9 = [0xc4, 0x80, 0x5f, 0x59, 0x85, 0x82, 0x05, 0xa6, 0x5a, 0x5a, 0x57, 0xf0, 0xd6, 0xde, 0x3b, 0xa8, 0xbb, 0xfe, 0x59, 0xfc, 0xb2, 0x5e, 0x67, 0xb3, 0x9f, 0x65, 0xc3, 0xe8, 0x4c, 0x55, 0xfb, 0x0e, 0x22, 0x1f, 0xf5, 0xea, 0x09, 0xb4, 0xda, 0xe6]

c10 = [0xe5, 0xc4, 0x0f, 0x4d, 0x85, 0x84, 0x40, 0xa1, 0x4f, 0x49, 0x5a, 0xb4, 0xd0, 0xde, 0x21, 0xe6, 0xb6, 0xfa, 0x43, 0xfc, 0xa0, 0x54, 0x6b, 0xb3, 0x82, 0x64, 0x96, 0xc2, 0x48, 0x33, 0xcd, 0x26, 0x25, 0x73, 0xce, 0xe8, 0x2e, 0x8a, 0xc8, 0xcb]

c11 = [0xff, 0xf6, 0x5a, 0x0b, 0xca, 0xb2, 0x01, 0xb3, 0x47, 0x5b]

# concatenate all ciphertext parts (except last) 
ciphertext = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10]

# our brute forcing method
def try_keys(c):
    # each key is one byte, so 255 possibilities here
    k_ok = []
    for k in range(255):
        plaintext = [x^k for x in c] 
        # all chars of one position have to translate to chars allowed in plaintext: 41-5a, 61-7a
        ok = [p_chr == 0x20 or p_chr >= 0x41 and p_chr <= 0x5a or p_chr >= 0x61 and p_chr <= 0x7a for p_chr in plaintext]
        if(all(ok)):
            k_ok += [k]
    return k_ok

# brute force all keystream positions
xor = []
for i in range(40):
    c = [x[i] for x in ciphertext]
    xor += [try_keys(c)]

# see amount of possibilities left per keystream char position
print filter(lambda x:x[1] > 1, [(i, len(xor[i])) for i in range(len(xor))])

# use "first" key
key = [x[0] for x in xor]
# adapt specific keystream positions per hand so that all plaintext is correct (could be automated)
key[3] = xor[3][7]
key[14] = xor[14][1]
key[21] = xor[21][1]
key[24] = xor[24][3]
key[25] = xor[25][5]
key[34] = xor[34][14]

# decode (now include last message part)
plaintext = []
for c in (ciphertext + [c11]):
    t = []
    for i in range(len(c)):
        t += [chr(c[i]^key[i])]
    plaintext += t

print ''.join(plaintext)

Note: the more plaintext messages encoded by the same, re-initialized RC4 cipher we have, the less possible keystreams will remain, and the faster we obtain the plaintext. Btw: this is pretty much the problem WiFi WEP had – this is the core problem of why it can be cracked so easily, and why it is considered very insecure by now.

Facedetection with JavaCV and different haarcascades on Android

November 5, 2011 2 comments

UPDATE (2015)

The pan shot face recognition prototype from 2013 (see below) has been embedded in the prototypical face module of the mobilesec android authentication framework. The face module uses 2D frontal-only face detection and authentication, but additionally showcases pan shot face detection and authentication. It currently uses Android 4.4 and OpenCV 2.4.10 for Android. Additionally to the functionality provided in the old prototype the module features (beside others) KNN/SVM classification, with training and classification both done on the device, more detail settings that can be changed/played with and direct access to authentication data stored on the FS in order to manage it (as the whole thing is still a demo/showcase).

Face module of the mobilesec Android authentication framework: https://github.com/mobilesec/authentication-framework-module-face

To cite the face authentication module please again use my master thesis:

Findling, R. D. Pan Shot Face Unlock: Towards Unlocking Personal Mobile Devices using Stereo Vision and Biometric Face Information from multiple Perspectives. Department of Mobile Computing, School of Informatics, Communication and Media, University of Applied Sciences Upper Austria, 2013

UPDATE (2014)

In 2013 I’ve finished my master thesis about the pan shot face unlock. As part of the thesis I’ve prototypically implemented several face detection and recognition prototypes, including the pan shot face recognition prototype for Android 4.3, using OpenCV 2.4.8 for Android. This prototype features the same functionality as the old face detection demo described in this post – but extends it by face recognition based on KNN or SVM, with training and classification both done on the device. For those reason you should stick to the new code available in the following repository: https://github.com/mobilesec/panshot-face-recognition-demo

Details on the background of the prototype are available in my master thesis:

Findling, R. D. Pan Shot Face Unlock: Towards Unlocking Personal Mobile Devices using Stereo Vision and Biometric Face Information from multiple Perspectives. Department of Mobile Computing, School of Informatics, Communication and Media, University of Applied Sciences Upper Austria, 2013

UPDATE (2013)

OpenCV now features Android support natively. Therefore you should start with OpenCV for Android (http://opencv.org/platforms/android.html) and add other haar or LBP cascades there (as also done in this post).

What is HaarCascadeTypes supposed to do?
The Android app “HaarCascadeTypes” extends the app “FacePreview” from the JavaCV project homepage. It’s a very small app that demonstrates which standard OpenCV haarcascades detect which types of faces (frontal, profile, …). As it is only a demo, it is not optimized in any way (e.g. it’s quite big).

The Application
The pictures below show what types of faces are detected by which haarcascades of OpenCV. The frame colors mean the usage of a specific haarcascade specification of OpenCV for the detected face:

  • Red: haarcascade_frontalface_alt.xml
  • Green: haarcascade_frontalface_alt2.xml
  • Blue: haarcascade_frontalface_alt_tree.xml
  • Yellow: haarcascade_frontalface_default.xml
  • White: haarcascade_profileface.xml

Download
You can download either the final apk or the complete source code of the project. In the apk only two classifiers are enabled: one for frontal, one for profile face detection. Note that the detection is rather slow as these two face detections are done separately. The android java part of the source is also attached at the bottom of the post for quick review. Important: the opencv-libraries delivered with the source and apk are working only on Android < 4.x. If you want to use it on Android 4.x, you will have to get the opencv libraries precompiled elsewhere or compile it on your own, which is obviously not the easiest task.
apk download, md5: 054292522a2062a3c6b9c6a4664a727e, sha1: 41759a699a2a1adf2e6ce3443ac427d32aae0aab
source download, md5: 78b67179e5e87ed6b1b2634c1b3f9d23, sha1: 71484d13f73ea37c0a73bd2c39aa5b30a3b27fe0

Compiling the source
There are several things you have to concern when compiling the source on your own: e.g. you need a working android environment. A detailed description of how to get JavaCV working for android is stated at the JavaCV project homepage.

The Android-Java part of the source (containing the JavaCV-API calls):

/*
 * Copyright (C) 2010,2011 Samuel Audet
 *
 * FacePreview - A fusion of OpenCV's facedetect and Android's CameraPreview
 * samples, with JavaCV + JavaCPP as the glue in between.
 *
 * This file was based on CameraPreview.java that came with the Samples for
 * Android SDK API 8, revision 1 and contained the following copyright notice:
 *
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the &amp;amp;amp;amp;quot;License&amp;amp;amp;amp;quot;); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &amp;amp;amp;amp;quot;AS IS&amp;amp;amp;amp;quot; BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 *
 * IMPORTANT - Make sure your AndroidManifiest.xml file includes the following:
 *
 *
 */

package com.googlecode.javacv.facepreview;

import static com.googlecode.javacv.cpp.opencv_core.IPL_DEPTH_8U;
import static com.googlecode.javacv.cpp.opencv_core.cvGetSeqElem;
import static com.googlecode.javacv.cpp.opencv_core.cvLoad;
import static com.googlecode.javacv.cpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;
import static com.googlecode.javacv.cpp.opencv_objdetect.cvHaarDetectObjects;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;

import com.googlecode.javacpp.Loader;
import com.googlecode.javacv.cpp.opencv_core;
import com.googlecode.javacv.cpp.opencv_objdetect;
import com.googlecode.javacv.cpp.opencv_core.CvMemStorage;
import com.googlecode.javacv.cpp.opencv_core.CvRect;
import com.googlecode.javacv.cpp.opencv_core.CvSeq;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.googlecode.javacv.cpp.opencv_objdetect.CvHaarClassifierCascade;

// ----------------------------------------------------------------------

public class FacePreview extends Activity {

	// ANDROID
	private FrameLayout	layout;
	private FaceView	faceView;
	private Preview		mPreview;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

		// Hide the window title.
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		// Create our Preview view and set it as the content of our activity.
		try {
			layout = new FrameLayout(this);
			faceView = new FaceView(this);
			mPreview = new Preview(this, faceView);
			layout.addView(mPreview);
			layout.addView(faceView);
			setContentView(layout);
		} catch (IOException e) {
			e.printStackTrace();
			new AlertDialog.Builder(this).setMessage(e.getMessage()).create().show();
		}
	}
}

// ----------------------------------------------------------------------

class FaceView extends View implements Camera.PreviewCallback {
	public static final int	SUBSAMPLING_FACTOR	= 4;

	private IplImage		grayImage;

	// HAARCASCADE TYPES
	public static enum Feature {
		FRONTALFACE_ALT, FRONTALFACE_ALT2, FRONTALFACE_ALT_TREE, FRONTALFACE_DEFAULT, PROFILEFACE
	}

	// HAARCASCADE MEMBERS
	private static HashMap				mClassifierFiles	= new HashMap();
	private static String								mClassifierPrefix	= &amp;amp;amp;amp;quot;/com/googlecode/javacv/facepreview/&amp;amp;amp;amp;quot;;
	static {
		 mClassifierFiles.put(Feature.FRONTALFACE_ALT, mClassifierPrefix +
		 &amp;amp;amp;amp;quot;haarcascade_frontalface_alt.xml&amp;amp;amp;amp;quot;);
		mClassifierFiles.put(Feature.PROFILEFACE, mClassifierPrefix + &amp;amp;amp;amp;quot;haarcascade_profileface.xml&amp;amp;amp;amp;quot;);
		 mClassifierFiles.put(Feature.FRONTALFACE_ALT_TREE, mClassifierPrefix
		 + &amp;amp;amp;amp;quot;haarcascade_frontalface_alt_tree.xml&amp;amp;amp;amp;quot;);
		mClassifierFiles.put(Feature.FRONTALFACE_ALT2, mClassifierPrefix + &amp;amp;amp;amp;quot;haarcascade_frontalface_alt2.xml&amp;amp;amp;amp;quot;);
		 mClassifierFiles.put(Feature.FRONTALFACE_DEFAULT, mClassifierPrefix +
		 &amp;amp;amp;amp;quot;haarcascade_frontalface_default.xml&amp;amp;amp;amp;quot;);
	}
	private HashMap						mFaces				= new HashMap();
	private HashMap				mStorages			= new HashMap();
	private HashMap	mClassifiers		= new HashMap();

	public FaceView(FacePreview context) throws IOException {
		super(context);

		// Preload the opencv_objdetect module to work around a known bug.
		Loader.load(opencv_objdetect.class);

		for (Feature f : mClassifierFiles.keySet()) {
			File classifierFile = Loader.extractResource(getClass(), mClassifierFiles.get(f), context.getCacheDir(),
					&amp;amp;amp;amp;quot;classifier&amp;amp;amp;amp;quot;, &amp;amp;amp;amp;quot;.xml&amp;amp;amp;amp;quot;);
			if (classifierFile == null || classifierFile.length() 				throw new IOException(&amp;amp;amp;amp;quot;Could not extract the classifier file from Java resource.&amp;amp;amp;amp;quot;);
			}
			mClassifiers.put(f, new CvHaarClassifierCascade(cvLoad(classifierFile.getAbsolutePath())));
			classifierFile.delete();
			if (mClassifiers.get(f).isNull()) {
				throw new IOException(&amp;amp;amp;amp;quot;Could not load the classifier file.&amp;amp;amp;amp;quot;);
			}
			mStorages.put(f, CvMemStorage.create());
		}
	}

	public void onPreviewFrame(final byte[] data, final Camera camera) {
		try {
			Camera.Size size = camera.getParameters().getPreviewSize();
			processImage(data, size.width, size.height);
			camera.addCallbackBuffer(data);
		} catch (RuntimeException e) {
			// The camera has probably just been released, ignore.
		}
	}

	protected void processImage(byte[] data, int width, int height) {
		// First, downsample our image and convert it into a grayscale IplImage
		int f = SUBSAMPLING_FACTOR;
		if (grayImage == null || grayImage.width() != width / f || grayImage.height() != height / f) {
			grayImage = IplImage.create(width / f, height / f, IPL_DEPTH_8U, 1);
		}
		int imageWidth = grayImage.width();
		int imageHeight = grayImage.height();
		int dataStride = f * width;
		int imageStride = grayImage.widthStep();
		ByteBuffer imageBuffer = grayImage.getByteBuffer();
		for (int y = 0; y &amp;amp;amp;amp;lt; imageHeight; y++) {
			int dataLine = y * dataStride;
			int imageLine = y * imageStride;
			for (int x = 0; x &amp;amp;amp;amp;lt; imageWidth; x++) {
				imageBuffer.put(imageLine + x, data[dataLine + f * x]);
			}
		}

		for (Feature feat : mClassifierFiles.keySet()) {
			mFaces.put(feat, cvHaarDetectObjects(grayImage, mClassifiers.get(feat), mStorages.get(feat), 1.1, 3,
					CV_HAAR_DO_CANNY_PRUNING));
			postInvalidate();
			opencv_core.cvClearMemStorage(mStorages.get(feat));
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		Paint paint = new Paint();
		paint.setTextSize(20);

		String s = &amp;amp;amp;amp;quot;FacePreview - This side up.&amp;amp;amp;amp;quot;;
		float textWidth = paint.measureText(s);
		canvas.drawText(s, (getWidth() - textWidth) / 2, 20, paint);

		for (Feature f : mClassifierFiles.keySet()) {
			paint.setColor(featureColor(f));
			if (mFaces.get(f) != null) {
				paint.setStrokeWidth(2);
				paint.setStyle(Paint.Style.STROKE);
				float scaleX = (float) getWidth() / grayImage.width();
				float scaleY = (float) getHeight() / grayImage.height();
				int total = mFaces.get(f).total();
				for (int i = 0; i &amp;amp;amp;amp;lt; total; i++) { 					CvRect r = new CvRect(cvGetSeqElem(mFaces.get(f), i)); 					int x = r.x(), y = r.y(), w = r.width(), h = r.height(); 					canvas.drawRect(x * scaleX, y * scaleY, (x + w) * scaleX, (y + h) * scaleY, paint); 				} 			} 		} 	} 	private int featureColor(Feature _f) { 		switch (_f) { 			case FRONTALFACE_ALT: 				return Color.RED; 			case FRONTALFACE_ALT2: 				return Color.GREEN; 			case FRONTALFACE_ALT_TREE: 				return Color.BLUE; 			case FRONTALFACE_DEFAULT: 				return Color.YELLOW; 			case PROFILEFACE: 				return Color.WHITE; 			default: 				throw new NullPointerException(&amp;amp;amp;amp;quot;no color defined for this feature type: &amp;amp;amp;amp;quot; + _f); 		} 	} } // ---------------------------------------------------------------------- class Preview extends SurfaceView implements SurfaceHolder.Callback { 	SurfaceHolder			mHolder; 	Camera					mCamera; 	Camera.PreviewCallback	previewCallback; 	Preview(Context context, Camera.PreviewCallback previewCallback) { 		super(context); 		this.previewCallback = previewCallback; 		// Install a SurfaceHolder.Callback so we get notified when the 		// underlying surface is created and destroyed. 		mHolder = getHolder(); 		mHolder.addCallback(this); 		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 	} 	public void surfaceCreated(SurfaceHolder holder) { 		// The Surface has been created, acquire the camera and tell it where 		// to draw. 		mCamera = Camera.open(); 		try { 			mCamera.setPreviewDisplay(holder); 		} catch (IOException exception) { 			mCamera.release(); 			mCamera = null; 			// TODO: add more exception handling logic here 		} 	} 	public void surfaceDestroyed(SurfaceHolder holder) { 		// Surface will be destroyed when we return, so stop the preview. 		// Because the CameraDevice object is not a shared resource, it's very 		// important to release it when the activity is paused. 		mCamera.stopPreview(); 		mCamera.release(); 		mCamera = null; 	} 	private Size getOptimalPreviewSize(List sizes, int w, int h) { 		final double ASPECT_TOLERANCE = 0.05; 		double targetRatio = (double) w / h; 		if (sizes == null) 			return null; 		Size optimalSize = null; 		double minDiff = Double.MAX_VALUE; 		int targetHeight = h; 		// Try to find an size match aspect ratio and size 		for (Size size : sizes) { 			double ratio = (double) size.width / size.height; 			if (Math.abs(ratio - targetRatio) &amp;amp;amp;amp;gt; ASPECT_TOLERANCE)
				continue;
			if (Math.abs(size.height - targetHeight) &amp;amp;amp;amp;lt; minDiff) {
				optimalSize = size;
				minDiff = Math.abs(size.height - targetHeight);
			}
		}

		// Cannot find the one match the aspect ratio, ignore the requirement
		if (optimalSize == null) {
			minDiff = Double.MAX_VALUE;
			for (Size size : sizes) {
				if (Math.abs(size.height - targetHeight) &amp;amp;amp;amp;lt; minDiff) {
					optimalSize = size;
					minDiff = Math.abs(size.height - targetHeight);
				}
			}
		}
		return optimalSize;
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		// Now that the size is known, set up the camera parameters and begin
		// the preview.
		Camera.Parameters parameters = mCamera.getParameters();

		List sizes = parameters.getSupportedPreviewSizes();
		Size optimalSize = getOptimalPreviewSize(sizes, w, h);
		parameters.setPreviewSize(optimalSize.width, optimalSize.height);

		mCamera.setParameters(parameters);
		if (previewCallback != null) {
			mCamera.setPreviewCallbackWithBuffer(previewCallback);
			Camera.Size size = parameters.getPreviewSize();
			byte[] data = new byte[size.width * size.height * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8];
			mCamera.addCallbackBuffer(data);
		}
		mCamera.startPreview();
	}
}