Secure Message iOS App in Swift

Using: Curve25519-ECDH, SHA256-HMAC, AES256-CBC — [Link to the GitHub Project Repo]

 
tcpdump pcap

01:07:18.371915 IP 192.168.1.171.64115 > 31.170.162.63.80: Flags [P.], seq 258:712, ack 1, win 8192, length 454: HTTP
mode=mesg&user=bob&rcpt=jon&mesg=key,31237969521267061265339920357754601118035663355449910125550985699827426505154,7903087581924324533759768883989441673564165222638106623548350836149345932721
--
01:07:18.783449 IP 31.170.162.63.80 > 192.168.1.171.64116: Flags [P.], seq 1:353, ack 539, win 250, length 352: HTTP: HTTP/1.1 200 OK
1450084038.47668400.66932:bob:key,31237969521267061265339920357754601118035663355449910125550985699827426505154,7903087581924324533759768883989441673564165222638106623548350836149345932721
--
01:07:19.547229 IP 192.168.1.171.64117 > 31.170.162.63.80: Flags [P.], seq 258:712, ack 1, win 8192, length 454: HTTP
mode=mesg&user=jon&rcpt=bob&mesg=key,20845162869924781138108110300526986101524551329253196414272755241823588505763,3845740679180735844612211279137657051909097428357280103024866020367868787613
--
01:07:20.637809 IP 31.170.162.63.80 > 192.168.1.171.64118: Flags [P.], seq 1:353, ack 539, win 250, length 352: HTTP: HTTP/1.1 200 OK
1450084039.65591500.56627:jon:key,20845162869924781138108110300526986101524551329253196414272755241823588505763,3845740679180735844612211279137657051909097428357280103024866020367868787613
--
01:07:30.861632 IP 192.168.1.171.64123 > 31.170.162.63.80: Flags [P.], seq 258:672, ack 1, win 8192, length 414: HTTP
mode=mesg&user=bob&rcpt=jon&mesg=msg,471776850.748092,8017053b9ea82e9d52e41052cc3518cd,8bd681694098a46a6ffbb01158672929d1fc5f3859f3fc5cc5030de8e4ed6d6e
--
01:07:33.788855 IP 31.170.162.63.80 > 192.168.1.171.64124: Flags [P.], seq 1:313, ack 539, win 250, length 312: HTTP: HTTP/1.1 200 OK
1450084050.96252100.73649:bob:msg,471776850.748092,8017053b9ea82e9d52e41052cc3518cd,8bd681694098a46a6ffbb01158672929d1fc5f3859f3fc5cc5030de8e4ed6d6e

/* c helper swift functions */

bnum *getx(ecc *e) { return e->x; }
bnum *gety(ecc *e) { return e->y; }

ecc *eccryp()
{
	char *a = "486662", *b = "1", *p = "57896044618658097711785492504343953926634992332820282019728792003956564819949";
	char *x = "9", *y = "43114425171068552920764898935933967039370386198203806730763910166200978582548";
	bnum *t, *u, *v, *w;
	ecc *e;
	
	t = bndec(p);
	u = bninit(t->size); w = bndec(x); bncopy(w, u); bnfree(w);
	v = bninit(t->size); w = bndec(y); bncopy(w, v); bnfree(w);
	e = ecinit(bndec(a), bndec(b), t, u, v);
	
	return e;
}

bnum *bnrnd(int size)
{
	FILE *f;
	bnum *r;
	
	r = bninit(size);
	f = fopen("/dev/urandom", "r");
	fread(r->nums, sizeof(unsigned int), size - 1, f);
	fclose(f);
	r->leng = size;
	while ((r->leng > 1) && (r->nums[r->leng - 1] == 0)) { r->leng -= 1; }
	
	return r;
}

/*
 Curve25519: a=486662, b=1, p=2^255 - 19
 ECC DH: o * (n * P) == onP == noP == n * (o * P)
 */

char *ecdh(ecc *e, const char *n)
{
	char *r = (char *)n;
	bnum *m;
	ecc *f;
	
	if (r == NULL)
	{
		m = bnrnd((e->p)->size);
		r = bnstr(m);
	}
	
	else
	{
		m = bndec((char *)n);
	}
	
	f = ecdup(e);
	pmul(m, e, f);
	
	(f->x)->leng = min((f->x)->leng, (e->p)->size); bncopy(f->x, e->x);
	(f->y)->leng = min((f->y)->leng, (e->p)->size); bncopy(f->y, e->y);
	bnfree(m);
	ecfree(f);
	
	return r;
}

void setexy(ecc *e, const char *x, const char *y)
{
	bnum *t;
	t = bndec((char *)x); t->leng = min(t->leng, (e->p)->size);
	bncopy(t, e->x); bnfree(t);
	t = bndec((char *)y); t->leng = min(t->leng, (e->p)->size);
	bncopy(t, e->y); bnfree(t);
}

char *shash(const char *m)
{
	char *o = malloc(256);
	sha256 hobj;
	sha256init(&hobj);
	sha256update(&hobj, (unsigned char *)m, (unsigned int)strlen(m));
	sha256final(&hobj, o);
	return o;
}

char *sencr(const char *i, const char *m, const char *k, int d)
{
	int x, y, w, z = 0;
	unsigned long ilen = strlen(i), mlen = strlen(m), klen = strlen(k);
	unsigned char tmp[16], ivn[16], msg[16], key[256];
	char *hex = "0123456789abcdef";
	char *enc = malloc(3 * strlen(m));
	
	bzero(key, 256);
	for (x = 0; ((x + 1) < 64) && ((x + 1) < klen); x += 2)
	{
		for (y = 0; y < 16; ++y)
		{
			if (k[x] == hex[y]) { key[x / 2] |= (y << 4); }
			if (k[x + 1] == hex[y]) { key[x / 2] |= y; }
		}
	}
	aes256keys(key);
	
	for (x = 0; x < 16; ++x)
	{
		ivn[x] = 0;
		if (x < ilen) { ivn[x] = i[x]; }
	}
	
	x = 0;
	while (x < mlen)
	{
		if (d == 0)
		{
			for (y = 0; y < 16; ++y)
			{
				msg[y] = 0;
				if (x < mlen) { msg[y] = m[x]; ++x; }
				msg[y] ^= ivn[y];
			}
		}
		else
		{
			for (y = 0; y < 16; ++y)
			{
				msg[y] = 0;
				if ((x + 1) < mlen)
				{
					for (w = 0; w < 16; ++w)
					{
						if (m[x] == hex[w]) { msg[y] |= (w << 4); }
						if (m[x + 1] == hex[w]) { msg[y] |= w; }
					}
					x += 2;
				}
				tmp[y] = msg[y];
			}
		}
		
		aes256core(msg, key, d);
		
		if (d == 0)
		{
			for (y = 0; y < 16; ++y)
			{
				enc[z] = hex[(msg[y] >> 4) & 0xf]; ++z;
				enc[z] = hex[msg[y] & 0xf]; ++z;
				ivn[y] = msg[y];
			}
		}
		else
		{
			for (y = 0; y < 16; ++y)
			{
				enc[z] = (msg[y] ^ ivn[y]); ++z;
				ivn[y] = tmp[y];
			}
		}
	}
	enc[z] = 0;
	
	return enc;
}

char *hmac(const char *m, const char *k)
{
	int x, y, i, j, bs = 64;
	unsigned long mlen = strlen(m), klen = strlen(k);
	char *hex = "0123456789abcdef", *hmout = malloc(256);
	unsigned char key[bs], ipad[bs], opad[bs];
	sha256 hobj;
	
	bzero(key, bs);
	for (x = 0; ((x + 1) < 64) && ((x + 1) < klen); x += 2)
	{
		for (y = 0; y < 16; ++y)
		{
			if (k[x] == hex[y]) { key[x / 2] |= (y << 4); }
			if (k[x + 1] == hex[y]) { key[x / 2] |= y; }
		}
	}
	
	for (x = 0; x < bs; ++x)
	{
		ipad[x] = (0x36 ^ key[x]);
		opad[x] = (0x5C ^ key[x]);
	}
	
	sha256init(&hobj);
	sha256update(&hobj, ipad, bs);
	sha256update(&hobj, (unsigned char *)m, (unsigned int)mlen);
	sha256final(&hobj, hmout);
	
	for (x = 0; x < 8; ++x)
	{
		for (y = 0; y < 4; ++y)
		{
			i = ((x * 4) + y);
			j = (24 - (y * 8));
			key[i] = ((hobj.h[x] >> j) & 0xff);
		}
	}
	
	sha256init(&hobj);
	sha256update(&hobj, opad, bs);
	sha256update(&hobj, key, 32);
	sha256final(&hobj, hmout);
	
	return hmout;
}

//
//  ViewController.swift
//  smsg
//

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

	let userText: UITextField = UITextField(frame: CGRect(x: 10.00, y: 32.00, width: 200.00, height: 30.00))
	let passText: UITextField = UITextField(frame: CGRect(x: 10.00, y: 64.00, width: 200.00, height: 30.00))
	let authButn: UIButton = UIButton(frame: CGRect(x: 225.00, y: 64.00, width: 100.00, height: 30.00))
	
	let frndText: UITextField = UITextField(frame: CGRect(x: 10.00, y: 96.00, width: 200.00, height: 30.00))
	let frndButn: UIButton = UIButton(frame: CGRect(x: 225.00, y: 96.00, width: 100.00, height: 30.00))
	let seckText: UILabel = UILabel(frame: CGRect(x: 10.00, y: 128.00, width: 300.00, height: 30.00))
	
	var mesgList: [String] = ["line 1", "line 2", "line 3"]
	let mesgHist: UITableView = UITableView(frame: CGRect(x: 10.00, y: 160.00, width: 300.00, height: 120.00))
	let mesgText: UITextView = UITextView(frame: CGRect(x: 10.00, y: 284.00, width: 200.00, height: 60.00))
	let mesgButn: UIButton = UIButton(frame: CGRect(x: 225.00, y: 284.00, width: 100.00, height: 30.00))
	
	let vers = "1.9.9.1"
	let serv = "http://smsg.site88.net"
	var authkey: String = ""
	var dhkey: String = ""
	var dhx: String = ""
	var dhy: String = ""
	var skey: String = ""
	var tkey: String = ""
	var ecobj = eccryp()
	
	func readReply(mode: Int, response: String) {
		if (response != "") { print("r>"+response) }
		
		if (response.characters.count > 5)
		{
			if (mode == 0)
			{
				self.authkey = response.substringWithRange(Range<String.Index>(start:response.startIndex.advancedBy(5), end:response.endIndex))
				
				dispatch_async(dispatch_get_main_queue()) { self.seckText.text = (" " + self.authkey) }
			}
			
			else if (mode == 3)
			{
				let linelist = response.characters.split{$0 == "\n"}.map(String.init)
				for line in linelist
				{
					let readlist = line.characters.split{$0 == ":"}.map(String.init)
					if (readlist.count > 2)
					{
						let infolist = readlist[2].characters.split{$0 == ","}.map(String.init)
						
						if (infolist.count == 3)
						{
							if ((readlist[1] == frndText.text) && (infolist[0] == "key"))
							{
								if (self.dhkey == "")
								{
									self.dhkey = String.fromCString(ecdh(self.ecobj, nil))!
									self.dhx = String.fromCString(bnstr(getx(self.ecobj)))!
									self.dhy = String.fromCString(bnstr(gety(self.ecobj)))!
									self.sendMesg(1, mesg: self.dhx+","+self.dhy)
								}
								
								setexy(self.ecobj, infolist[1], infolist[2])
								ecdh(self.ecobj, self.dhkey)
								let tdhx = String.fromCString(bnstr(getx(self.ecobj)))
								let tdhy = String.fromCString(bnstr(gety(self.ecobj)))
								
								print("d>"+tdhx!+","+tdhy!)
								self.skey = String.fromCString(shash(tdhx!+","+tdhy!))!
								self.tkey = String.fromCString(shash(self.skey))!
								print("k>"+self.skey+","+self.tkey)
								dispatch_async(dispatch_get_main_queue()) { self.seckText.text = (" " + self.skey) }
							}
						}
						
						else if (infolist.count == 4)
						{
							if ((readlist[1] == frndText.text) && (infolist[0] == "msg"))
							{
								let smac = String.fromCString(hmac(infolist[1]+","+infolist[2], self.tkey))
								print("v>"+smac!+"=="+infolist[3])
								if (smac == infolist[3])
								{
									let smesg = String.fromCString(sencr(infolist[1], infolist[2], self.skey, 1))
									self.mesgList.append(smesg!)
									dispatch_async(dispatch_get_main_queue()) { self.mesgHist.reloadData() }
								}
							}
						}
					}
				}
			}
		}
	}
	
	func sendMesg(mode: Int, mesg: String) {
		var postdata: String = ""
		
		if (mode == 0)
		{
			postdata = ("mode=auth&user="+self.userText.text!+"&pass="+self.passText.text!)
		}
		
		else if (mode == 1)
		{
			postdata = ("mode=mesg&user="+self.userText.text!+"&auth="+self.authkey+"&rcpt="+self.frndText.text!+"&mesg=key,"+mesg)
		}
		
		else if (mode == 2)
		{
			postdata = ("mode=mesg&user="+self.userText.text!+"&auth="+self.authkey+"&rcpt="+self.frndText.text!+"&mesg=msg,"+mesg)
		}
		
		else if (mode == 3)
		{
			postdata = ("mode=read&user="+self.userText.text!+"&auth="+self.authkey)
		}
		
		let request = NSMutableURLRequest(URL: NSURL(string: self.serv)!)
		request.HTTPMethod = "POST"
		request.HTTPBody = postdata.dataUsingEncoding(NSUTF8StringEncoding)
		let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
			data, response, error in
			let reply = NSString(data: data!, encoding: NSUTF8StringEncoding)
			self.readReply(mode, response: reply as! String)
		}
		task.resume()
	}
	
	func butnPress(butn: UIButton) {
		if (butn.titleLabel?.text == "Login") { self.sendMesg(0, mesg: "") }
		if (butn.titleLabel?.text == "Add")
		{
			if (self.dhkey == "")
			{
				self.dhkey = String.fromCString(ecdh(self.ecobj, nil))!
				self.dhx = String.fromCString(bnstr(getx(self.ecobj)))!
				self.dhy = String.fromCString(bnstr(gety(self.ecobj)))!
			}
			self.sendMesg(1, mesg: self.dhx+","+self.dhy)
		}
		if (butn.titleLabel?.text == "Send")
		{
			if (self.skey != "")
			{
				let stime = String(NSDate.timeIntervalSinceReferenceDate())
				let smesg = String.fromCString(sencr(stime, self.mesgText.text, self.skey, 0))
				let smac = String.fromCString(hmac(stime+","+smesg!, self.tkey))
				self.sendMesg(2, mesg: stime+","+smesg!+","+smac!)
			}
		}
	}
	
	func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
		return self.mesgList.count
	}
	
	func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
		let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
		cell.textLabel!.text = self.mesgList[indexPath.row]
		cell.textLabel!.font = cell.textLabel!.font.fontWithSize(10)
		return cell
	}
	
	func backFunc(timer: NSTimer) {
		self.sendMesg(3, mesg: "")
	}
	
	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view, typically from a nib.
		
		print("i>draw init")
		
		/* login elements */
		
		let userPadd = UIView(frame: CGRectMake(0, 0, 5, self.userText.frame.size.height))
		self.userText.leftView = userPadd
		self.userText.leftViewMode = UITextFieldViewMode.Always
		self.userText.layer.cornerRadius = 2.0
		self.userText.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.userText.layer.borderWidth = 2.0
		self.userText.autocorrectionType = UITextAutocorrectionType.No
		self.userText.autocapitalizationType = UITextAutocapitalizationType.None
		self.view.addSubview(self.userText)
		
		let passPadd = UIView(frame: CGRectMake(0, 0, 5, self.passText.frame.size.height))
		self.passText.leftView = passPadd
		self.passText.leftViewMode = UITextFieldViewMode.Always
		self.passText.layer.cornerRadius = 2.0
		self.passText.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.passText.layer.borderWidth = 2.0
		self.passText.secureTextEntry = true
		self.view.addSubview(self.passText)
		
		self.authButn.layer.cornerRadius = 2.0
		self.authButn.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.authButn.layer.borderWidth = 2.0
		self.authButn.setTitle("Login", forState: UIControlState.Normal)
		self.authButn.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
		self.authButn.addTarget(self, action: "butnPress:", forControlEvents: UIControlEvents.TouchUpInside)
		self.view.addSubview(self.authButn)
		
		/* friend elements */
		
		let frndPadd = UIView(frame: CGRectMake(0, 0, 5, self.userText.frame.size.height))
		self.frndText.leftView = frndPadd
		self.frndText.leftViewMode = UITextFieldViewMode.Always
		self.frndText.layer.cornerRadius = 2.0
		self.frndText.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.frndText.layer.borderWidth = 2.0
		self.frndText.autocorrectionType = UITextAutocorrectionType.No
		self.frndText.autocapitalizationType = UITextAutocapitalizationType.None
		self.view.addSubview(self.frndText)
		
		self.frndButn.layer.cornerRadius = 2.0
		self.frndButn.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.frndButn.layer.borderWidth = 2.0
		self.frndButn.setTitle("Add", forState: UIControlState.Normal)
		self.frndButn.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
		self.frndButn.addTarget(self, action: "butnPress:", forControlEvents: UIControlEvents.TouchUpInside)
		self.view.addSubview(self.frndButn)
		
		self.seckText.layer.cornerRadius = 2.0
		self.seckText.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.seckText.layer.borderWidth = 2.0
		self.seckText.font = self.seckText.font.fontWithSize(8)
		self.seckText.text = (" v" + self.vers)
		self.view.addSubview(self.seckText)
		
		/* message elements */
		
		self.mesgHist.layer.cornerRadius = 2.0
		self.mesgHist.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.mesgHist.layer.borderWidth = 2.0
		self.mesgHist.rowHeight = 20.0
		self.mesgHist.delegate = self
		self.mesgHist.dataSource = self
		self.view.addSubview(self.mesgHist)
		
		self.mesgText.layer.cornerRadius = 2.0
		self.mesgText.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.mesgText.layer.borderWidth = 2.0
		self.mesgText.autocorrectionType = UITextAutocorrectionType.No
		self.mesgText.autocapitalizationType = UITextAutocapitalizationType.None
		self.mesgText.text = "message string"
		self.view.addSubview(self.mesgText)
		
		self.mesgButn.layer.cornerRadius = 2.0
		self.mesgButn.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).CGColor
		self.mesgButn.layer.borderWidth = 2.0
		self.mesgButn.setTitle("Send", forState: UIControlState.Normal)
		self.mesgButn.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
		self.mesgButn.addTarget(self, action: "butnPress:", forControlEvents: UIControlEvents.TouchUpInside)
		self.view.addSubview(self.mesgButn)
		
		/* background reader */
		
		NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "backFunc:", userInfo: nil, repeats: true)
	}

	override func didReceiveMemoryWarning() {
		super.didReceiveMemoryWarning()
		// Dispose of any resources that can be recreated.
	}

}

Leave a comment