Agile development w/ CI/CD – Automated cloud building & deployment (from scratch)

So I went to an interview recently and I was asked on the spot how I would implement an entire agile development platform, end-to-end, with specific details, (no requirements listed) – and it had to be cloud friendly/scalable! I was running different ideas through my head because it was a fairly open ended question and I felt like it deserved more thought than I could provide in just the 30 minutes given to me…

There are a dozen different ways this can be achieved but I wanted to post an example proof-of-concept of the essential steps needed to accomplish something like this. There are basically 5 different pieces to this puzzle that need to be solved:

  1. The developers need a central repository with local branching capabilities so they can test different versions of their changes on demand
  2. They need to be able to take an automatic snapshot of their code base that they are currently working on so they can specify which change they want to test
  3. They need a simple mechanism, which when triggered, takes that code-change snapshot and sends it to a build server for compilation
  4. The build server, when finished, should deploy and run their compiled test code, usually in a temporary, clean, isolated environment like a vm
  5. The build server returns a simple and unique URL pointing to their new test instance so the developer can easily see and test their desired changes

Example Tech Env: GIT, SSH, NetCat, Bash Scripts, Java

Components Workflow:

  • Developer (java)
  • Central Repo (git/ssh)
  • Automatic Snapshot Transfer (netcat)
  • Automatic Building (javac)
  • Automatic Deployment (bash script)
  • Temporary Test Instance (netcat website)

 

[devops engineer setup]

git init
git add project
git commit -a

# setup initial supporting scripts

 

[developer]

# get the code
git clone ssh://[email protected]:/home/git/projects

# make a change
cd project/
vim src/program.java
# @@ -11,5 +11,6 @@ public class program {
#  			System.out.println(x+": "+resList.get(x));
#  		}
# +		System.out.println("Changed!");
#  	}
#  }

./make.sh # magic!
# this script tar's up the current code and connects to the server to run an init.sh bootstrap script
# the init.sh script chooses an initial random port and calls the deploy.sh script
#   to perform the snapshot code transfer
# the deploy.sh script opens a netcat port to receive the snapshot'd code
#   and then runs build.sh on the result (port is closed after)
# the build.sh script opens a netcat port to communicate the build results
#   and then runs a http netcat instance of the executed code for the developer to test/visit

 

[automated build, deploy, test instance]

Extracting build...
./src/
./src/dep.java
./src/program.java
Extraction completed!

Building started...
Build completed!


Visit the build URL below:
http://192.168.161.2:29792/testing

[example web change test output]

Fri Nov 11 05:22:10 UTC 2016

0: [A, D]
1: [B, E]
2: [C, F]
Changed!

Completed!

 

[project file tree layout]

project/
|-- make.sh
|-- init.sh
|-- deploy.sh
|-- build.sh
`-- src
    |-- dep.java
    |-- lib
    `-- program.java

 

[script source code]

[make.sh]

#!/bin/bash
tar -czf snapshot.tgz ./src
USRN=`git config --get remote.origin.url | sed -e 's/@.*$//' -e '[email protected]^.*/@@'`
REPO=`git config --get remote.origin.url | sed -e 's/^.*://'`
PROJ="project"
PORT=`ssh "${USRN}@buildsrv" "${REPO}/${PROJ}/init.sh"`
false
while [ $? -ne 0 ] ; do
	sleep 1
	nc buildsrv "$PORT" < snapshot.tgz
done
rm snapshot.tgz
let PORT="$PORT + 1"
false
while [ $? -ne 0 ] ; do
	sleep 1
	nc buildsrv "$PORT"
done

[init.sh]

#!/bin/bash

DIR=$(dirname "$0")
let PORT="1028 + ( $RANDOM % 64000 )"

nohup "$DIR/deploy.sh" "$PORT" > /dev/null 2> /dev/null < /dev/null &
echo "$PORT"

[deploy.sh]

#!/bin/bash

DIR=$(dirname "$0")
PORT="$1"

TMP="/tmp/build.`date '+%s'`"

mkdir -p "$TMP/"
nc -l -p "$PORT" -w 30 > "$TMP/src.tgz"

if [ -s "$TMP/src.tgz" ] ; then
	let PORT="$PORT + 1"
	"$DIR/build.sh" "$PORT" "$TMP" | nc -l -p "$PORT" -w 30 -q 1
fi

[build.sh]

#!/bin/bash

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

let PORT="$1 + 1"
TMP="$2"

echo
echo "Extracting build..."
cd "$TMP/"
tar -xzvf "src.tgz"
cd src/
echo "Extraction completed!"
echo
echo "Building started..."
javac -d . dep.java 2>&1
javac -cp . program.java 2>&1
echo "Build completed!"
echo

GW=`netstat -nr | grep -i '^0.0.0.0' | awk '{ print $2 }' | cut -c 1-4`
IP=`ifconfig | grep -i "inet addr:$GW" | awk '{ print $2 }' | sed -e 's/^[^:]*://'`

echo
echo "Visit the build URL below:"
echo "http://${IP}:${PORT}/testing"
echo

( echo -e 'HTTP/1.1 200 OK\r\n\r\n' ; date ; echo ; java program ; echo ; echo 'Completed!' ) | nohup nc -l -p "$PORT" -w 60 -q 1 > /dev/null 2> /dev/null &

[src/dep.java]

package lib;
import java.util.*;
public class dep {
	public dep() {
	}
	public static ArrayList merge(ArrayList listA, ArrayList listB) {
		ArrayList outList = new ArrayList();
		for (int x = 0; x < listA.size(); ++x) {
			ArrayList tmpList = new ArrayList();
			tmpList.add(listA.get(x));
			tmpList.add(listB.get(x));
			outList.add(tmpList);
		}
		return outList;
	}
}

[src/program.java]

import java.util.*;
import lib.dep;
public class program {
	public static void main(String[] args) {
		ArrayList inA = new ArrayList();
		ArrayList inB = new ArrayList();
		inA.add("A"); inA.add("B"); inA.add("C");
		inB.add("D"); inB.add("E"); inB.add("F");
		dep help = new dep();
		ArrayList resList = help.merge(inA, inB);
		for (int x = 0; x < resList.size(); ++x) {
			System.out.println(x+": "+resList.get(x));
		}
	}
}
Agile development w/ CI/CD – Automated cloud building & deployment (from scratch)

OpenWRT dhcp-lease file arping-based host-removal (dnsmasq)

To help prevent DHCP starvation DoS attacks (not fool-proof though if you can fake ARP replies back)

rm /tmp/arp.*
cat /tmp/dhcp.leases | awk '{ print $3 }' | while read a ; do
  ( arping -f -q -c 1 -w 1 -I br-lan "$a" ; if [ $? -ne 0 ] ; then echo "$a" > /tmp/arp.host."$a" ; fi ) &
done
sleep 3
cat /tmp/arp.host.* | while read a ; do
  sed "/^.*$a.*$/d" -i /tmp/dhcp.leases
  touch /tmp/arp.run
done
if [ -f /tmp/arp.run ] ; then
  kill `ps | grep -i 'dnsmasq' | grep -iv 'grep' | awk '{ print $1 }'`
  /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf -k -x /var/run/dnsmasq/dnsmasq.pid &
fi
rm /tmp/arp.*
OpenWRT dhcp-lease file arping-based host-removal (dnsmasq)

Having fun with numbers & Python – Randomization & Shuffling – Stream Cipher

import sys,time
def hexs(s):
	o = ""
	for c in s:
		h = hex(ord(c))[2:]
		if (len(h) < 2):
			h = ("0" + h)
		if (o != ""):
			o += ":"
		o += h
	return o
def nice(s):
	o = ""
	for i in range(0, len(s)):
		if (i > 0):
			if ((i % 16) == 0):
				o += "\n  "
			else:
				o += ":"
		t = str(s[i])
		while (len(t) < 3):
			t = ("0" + t)
		o += t
	return o
def rnds():
	n = 2; s = ""
	while ((int(time.time()) % n) != 0):
		pass
	while (len(s) != 32):
		time.sleep(1)
		r = 0
		while ((int(time.time()) % n) != 0):
			r = (r + 1)
		#sys.stderr.write("r="+str(r)+"\n");break
		s = (s + chr((r >> 8) & 0xff) + chr(r & 0xff))
	return s
if (len(sys.argv) < 2):
	h = rnds()
	sys.stderr.write("k="+hexs(h)+"\n")
else:
	def keys(k):
		s = []
		for x in range(0, 256):
			s.append(x)
		for y in range(0, len(k)):
			l = ord(k[y])
			for x in range(0, 256):
				t = s[x] ; s[x] = s[l] ; s[l] = t
				l = ((l + x) % 256)
		return s
	s = keys(sys.argv[1])
	if (len(sys.argv) > 2):
		sys.stderr.write("s="+nice(s)+"\n\n")
	def ciph(m, s, z):
		j = 0; o = ""
		for y in range(0, len(m)):
			l = ord(m[y])
			c = (l ^ s[y % 256])
			o += chr(c)
			i = l
			if (z == "d"):
				i = c
			j = ((j ^ i) % 256)
			for x in range(0, 256):
				t = s[x] ; s[x] = s[j] ; s[j] = t
				j = ((j + x) % 256)
		return o
	if (len(sys.argv) < 3):
		m = sys.stdin.read()
		z = "d"
	else:
		m = sys.argv[2]
		z = "e"
	o = ciph(m, s, z)
	if (len(sys.argv) > 2):
		sys.stderr.write("m="+hexs(o)+"\n")
	sys.stdout.write(o)

$ clear ; echo ; python fun.py ; python fun.py ; echo ; python fun.py key msg | python fun.py key ; echo

k=74:ed:3c:1d:62:72:92:61:3c:27:f8:7c:c4:98:92:47:0d:18:c0:47:a6:44:b6:13:19:5c:90:30:4b:23:cf:12
k=fd:e7:4d:1d:c7:b0:a6:d9:45:0e:c9:68:95:35:46:e9:08:e9:8c:4f:53:42:9d:29:7b:30:65:f6:cd:00:ae:f6

s=054:166:020:190:252:207:029:030:042:098:156:055:041:108:058:032
  073:208:253:242:115:250:053:106:192:168:087:173:045:161:124:186
  163:049:162:067:040:096:086:039:057:023:100:216:203:123:217:175
  248:107:224:131:038:172:014:011:085:003:214:149:110:215:159:129
  046:125:144:043:077:126:150:167:200:169:097:153:188:201:071:254
  246:244:080:184:232:010:006:176:241:183:019:027:118:090:104:065
  000:033:210:220:236:234:063:205:048:155:095:133:218:050:022:158
  130:068:221:170:202:079:213:101:164:111:099:074:034:239:082:238
  031:181:092:230:088:017:007:251:083:193:051:245:237:209:064:070
  016:139:013:037:113:180:119:009:191:148:026:189:136:059:142:233
  056:160:243:135:076:165:012:035:222:212:231:084:249:061:152:093
  255:015:174:178:223:102:052:177:021:062:117:028:225:105:025:069
  121:002:227:122:143:047:134:137:146:219:194:060:005:089:044:140
  182:247:147:145:179:198:075:081:138:094:109:141:151:116:187:127
  103:128:001:132:036:154:066:112:078:196:229:004:226:211:206:199
  091:072:171:008:114:228:197:157:240:120:185:235:018:204:195:024

m=5b:c4:90
msg

Having fun with numbers & Python – Randomization & Shuffling – Stream Cipher

Basic HTTPS TLS Proxy Server In Python

#!/usr/bin/python
# Usage: ./net.py
# Description: Proof-of-concept HTTPS proxy script to hide the domain name in a TLS connection
#              so that firewalling appliances can't see who you are trying to connect to.
# Example setup: Edit ../hosts to include "127.0.0.1 imgur.com"
# Browser: http://imgur.com
#          -> TCP 127.0.0.1 80 (Python is listening & reads headers "GET / && Host imgur.com")
#          -> UDP 4.2.2.1 53 (Python performs DNS query on "imgur.com" [non-hosts file lookup])
#          -> TCP 1.2.3.4 443 (Python connects to TLS server & sends headers over encrypted channel [non-cert verify])
#          -> HTTPS response data is proxied back to browser localhost connection
# Warning: This script is very slow and insecure

import re
import socket
import ssl
import subprocess
import SocketServer

resolv = {}

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        global resolv
        
        heads = self.request.recv(10024).strip().split("\n")
        
        page = ""; host = ""
        for head in heads:
            match = re.match("^get ([^ ]+).*$", head.strip(), re.I)
            if (match):
                page = match.group(1)
            match = re.match("^host: ([^ ]+).*$", head.strip(), re.I)
            if (match):
                host = match.group(1)
        
        if ((not page) or (not host)):
            print("err",heads)
        else:
            addr = ""
            if (host in resolv.keys()):
                addr = resolv[host]
            else:
                looks = subprocess.check_output(["nslookup", host]).split("\n")
                for look in looks:
                    match = re.match("^address:[ ]+([^ ]+).*$", look.strip(), re.I)
                    if (match):
                        addr = match.group(1)
                resolv[host] = addr
            
            print(page,host,addr)
            
            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
            context.verify_mode = ssl.CERT_NONE
            context.check_hostname = False
            conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=addr)
            conn.connect((addr, 443))
            conn.sendall("GET "+page+" HTTP/1.1\r\n"+"Host: "+host+"\r\n"+"Connection: close\r\n"+"\r\n")
            
            resp = "";
            while (1):
                temp = conn.recv(10024)
                if (not temp):
                    break
                resp += temp
            self.request.sendall(resp)

if (__name__ == "__main__"):
    (HOST, PORT) = ("127.0.0.1", 80)
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Basic HTTPS TLS Proxy Server In Python

A cool SSH auto password program – sshpass – for sys admin remote automation (probably should use keys instead tho)

#!/usr/bin/python
# Usage: ./sshpw.py <password> ssh user@host date
# Description: This is a demo SSH auto password login script via a pseudo tty device (stolen from sshpass - not recommended obviously!)
# Note: This is a rough example script which assumes you have already accepted the fingerprint & have the right password

import os, sys, time

import glob, struct, termios, fcntl, pty

from ctypes import *

try:
	libc = cdll.LoadLibrary(glob.glob("/lib/*-linux-gnu/libc-*.so")[0])
except:
	libc = cdll.LoadLibrary(glob.glob("/usr/lib/libc.dylib")[0])

(master, slave) = pty.openpty()
#fcntl.fcntl(master, fcntl.F_SETFL, os.O_NONBLOCK)

libc.ptsname.restype = c_char_p
name = libc.ptsname(c_int(master))
slavept = -10

ourtty = os.open("/dev/tty", 0)

s = struct.pack("HHHH", 0, 0, 0, 0)
t = fcntl.ioctl(ourtty, termios.TIOCGWINSZ, s)
fcntl.ioctl(master, termios.TIOCSWINSZ, t)

pidn = os.fork()

if (pidn == 0):
	os.setsid()
	slavept = os.open(name, os.O_RDWR)
	os.close(slavept)
	os.close(master)
	os.execvp(sys.argv[2], sys.argv[2:])
	os._exit(0)

else:
	slavept = os.open(name, os.O_RDWR | os.O_NOCTTY)
	print("["+os.read(master, 128)+"]")
	os.write(master, sys.argv[1] + "\r\n")
	print("["+os.read(master, 8)+"]")
	os.wait()

A cool SSH auto password program – sshpass – for sys admin remote automation (probably should use keys instead tho)

Simple LinkedIn Resume Generator (js)

LinkedIn recently disabled it’s third party APIs which took out an auto-resume builder service based on my public profile. I wrote some JS to try and at least generate a basic resume from my LinkedIn profile now:

From LinkedIn Profile -to- a simple clean resume! with just a little bit of effort! (done in Safari with print-to-PDF graphics enabled & headers disabled):

var s = 0, q = 0;
function y() {
  if (s < document.body.scrollHeight) {
    // scroll
    window.scrollBy(0, 125); s += 125;
    setTimeout(y, 0.125 * 1000);
  } else {
    if (q == 0) { q = 1; z(); }
    else { window.scrollTo(0, 0); }
  }
}

var mail = ["[email protected]", "[email protected]"];
var webs = ["fossjon.com", "github.com/fossjon", "github.com/stoops"];

function z() {
  // hide
  var l = [
    "resume-builder-top-nav", "scaffold-layout__header", "scaffold-layout__aside",
    "resume-builder__section-edit-button", "resume-builder__subsection-edit-button",
    "global-nav__a11y-menu", "global-nav", "msg-overlay-container"];
  for (var i in l) {
    var a = document.getElementById(l[i]);
    var b = document.getElementsByClassName(l[i]);
    var c = 1;
    try { a.setAttribute("style", "display: none !important;"); c = 0; } catch(e) { /* no-op */ }
    for (var j in b) { try { b[j].setAttribute("style", "display: none !important;"); c = 0; } catch(e) { /* no-op */ } }
    if (c == 1) { console.log("hide:"+l[i]); }
  }
  var p = document.getElementsByClassName("resume-builder__job-apply-educational-card-illustration");
  if (p) { p[0].parentNode.setAttribute("style", "display: none !important;"); }

  document.title = "resume";
  document.body.setAttribute("style", "background-color: white !important;");

  // high
  try {
    document.getElementsByClassName("authentication-outlet")[0].style.background = "white";
    document.getElementsByClassName("authentication-outlet")[0].style.paddingTop = "1px";
    document.getElementsByClassName("authentication-outlet")[0].style.paddingBottom = "256px";
    var p = document.getElementsByClassName("resume-builder-contact-info__name")[0].parentNode.parentNode;
    p.setAttribute("style", "padding-top: 1px !important; padding-bottom: 0px !important; box-shadow: none !important;");
  } catch(e) { /* no-op */ }

  // wide
  try {
    document.getElementsByClassName("scaffold-layout__inner")[0].style.marginLeft = "10px";
    document.getElementsByClassName("scaffold-layout__inner")[0].style.marginRight = "10px";
    document.getElementById("main").style.width = (document.getElementById("main").parentNode.offsetWidth+"px");
  } catch(e) { /* no-op */ }

  // dots
  while (document.getElementsByClassName("white-space-pre-wrap").length > 0) {
    var li = document.getElementsByClassName("white-space-pre-wrap")[0];
    var h = li.innerHTML; if (!h) { h = ""; }
    h = h.replace(/\*/mig, "•");
    while (h.match(/^.*••.*$/mig)) {
      h = h.replace(/••([^\r\n]+)/mig, "<table style='display:inline;'><tr><td style='padding-left:25px;'>•$1</td></tr></table>");
    }
    console.log("dots:"+i);
    h = h.replace(/•[ ]*([^\r\n]+)/mig, "<table style='display:inline;'><tr><td style='padding-left:25px;white-space:nowrap;'><b style='font-size:16px;'>•&nbsp;</b></td><td>$1</td></tr></table>");
    for (var k in imgs) {
      h = h.replace("["+k+"]", "\n<span><br><img style='width: 65%; margin-left: 25px;' src='data:image/png;base64,"+imgs[k]+"' /></span>");
    }
    h = h.replace(/[\r\n]/mig, "<span><br/></span>") + "<div>&nbsp;<br/></div>";
    li.outerHTML = ('<span class="t-16 t-black main-text">' + h + '</span>');
  }

  // imgs && path && flat
  var info = "";
  var d = {};
  var l = document.getElementsByClassName("lazy-image");
  for (var i = 0; i < l.length; ++i) {
    var f = 1;
    var o = "", v = " opacity: 0%; ";
    if (l[i].src in d) { f = 0; }
    /* imgs */
    if (f == 0) { o = " opacity: 0%; "; }
    l[i].setAttribute("style", "width: 42px; height: 42px; margin-top: 6px !important; margin-right: 4px !important;"+o);
    d[l[i].src] = "*";
    /* dots */
    if (f == 0) { v = ""; }
    if (!document.getElementById("dot"+i)) {
      var x = document.createElement("SPAN");
      x.setAttribute("id", "dot"+i);
      x.setAttribute("class", "pvs-entity__path-node");
      x.setAttribute("style", "left: -28px;" + v);
      l[i].parentNode.insertBefore(x, l[i].nextSibling);
    }
    /* less */
    try {
      var p = l[i].parentNode.getElementsByTagName("div")[0];
      var a = p.getElementsByTagName("*");
      if (i == 0) { info = (" # " + a[0].innerHTML + " @ " + a[1].innerHTML); }
      var e = 0;
      try {
        var t = p.parentNode.parentNode.parentNode.getElementsByTagName("h3")[0];
        if (t.innerHTML.match(/^.*education.*$/mig)) { e = 1; }
      } catch(e) { /* no-op */ }
      a[1].setAttribute("class", "t-14 t-black");
      if (e == 0) {
        a[0].innerHTML = (a[0].innerHTML + " at " + a[1].innerHTML);
        a[1].innerHTML = a[2].innerHTML;
        a[2].innerHTML = " &nbsp; ";
      } else {
          a[0].innerHTML = (a[1].innerHTML + " at " + a[0].innerHTML);
          a[1].innerHTML = a[2].innerHTML;
          a[2].innerHTML = " &nbsp; ";
      }
      if (f == 0) {
        p.classList.add("full-width");
        p.classList.add("pvs-entity--with-path");
      } else {
        var h = p.innerHTML;
        if (h.match(/^.*<.h[4]>.*$/mig)) {
          h = (h.replace(/(<.h[4]>)/mig, '$1<div class="pvs-entity--with-path">') + "</div>");
        }
        p.innerHTML = h;
      }
    } catch(e) { /* no-op */ }
  }

  // find
  var l = document.getElementsByClassName("resume-builder__section-container");
  for (var i = 0; i < l.length; ++i) {
    if (l[i].innerHTML && l[i].innerHTML.match(/^.*this section is empty.*$/mig)) {
      l[i].style.display = "none";
    }
  }

  // info
  try {
    var p = document.getElementsByClassName("resume-builder-contact-info__field-container")[0].parentNode;
    var h = "";
    for (var i = 0; i < Math.max(mail.length,webs.length); ++i) {
      var a = ""; if (i < mail.length) { a = mail[i]; }
      var b = ""; if (i < webs.length) { b = webs[i]; }
      if (a != "") {
        var path = '<path d="M8 1L1 4.47V11a3 3 0 003 3h8a3 3 0 003-3V4.47zm5 10a1 1 0 01-1 1H4a1 1 0 01-1-1V6.51L8 9l5-2.49z"></path>';
        a = ('<a href="mailto:'+a+'"><li-icon type="envelope-open" class="resume-builder-contact-info__email-icon" size="small" role="img" aria-label="Email"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" data-supported-dps="16x16" fill="currentColor" class="mercado-match" width="16" height="16" focusable="false">'+path+'</svg></li-icon>'+a+'</a>');
      }
      h += ('<div class="resume-builder-contact-info__field-container">'+a+'</div>');
      if (b != "") {
        var path = '<path d="M11 2a3 3 0 00-2.1.87L6.87 4.94a2.93 2.93 0 00-.72 1.21 2.93 2.93 0 00-1.21.72L2.87 8.94a3 3 0 104.19 4.19l2.07-2.07a2.93 2.93 0 00.72-1.21 2.93 2.93 0 001.21-.72l2.07-2.07A3 3 0 0011 2zm-5.17 9.89a1.22 1.22 0 01-1.72-1.72l2.06-2.06A3 3 0 007.91 9.8zm6.07-6.07L9.83 7.89A3 3 0 008.09 6.2l2.07-2.07a1.22 1.22 0 011.73 1.7z"></path>';
        b = ('<a href="https://'+b+'"><li-icon type="link" class="resume-builder-contact-info__website-icon" size="small" color="true" role="img" aria-label="Website link"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" data-supported-dps="16x16" fill="currentColor" width="16" height="16" focusable="false">'+path+'</svg></li-icon>'+b+'</a>');
      }
      h += ('<div class="resume-builder-contact-info__field-container">'+b+'</div>');
    }
    p.innerHTML = h;
    var q = document.getElementsByClassName("resume-builder-contact-info__name")[0].parentNode;
    q.getElementsByTagName("p")[0].style.fontFamily = "monospace";
    q.getElementsByTagName("p")[0].style.fontSize = "14px";
    q.getElementsByTagName("p")[0].style.paddingLeft = "3px";
    q.getElementsByTagName("p")[0].style.paddingTop = "6px";
    q.getElementsByTagName("p")[0].innerHTML = info;
  } catch(e) { /* no-op */ }

  // clik
  var l = document.getElementsByClassName("main-text");
  for (var i = 0; i < l.length; ++i) {
    l[i].onclick = function() {
      var pb = parseInt(this.parentNode.parentNode.style.paddingBottom);
      if (!pb) { pb = 0; } this.parentNode.parentNode.style.paddingBottom = ((pb+50)+"px");
    };
  }

  // end!
  window.scrollTo(0, 0);

  return 0;

  // wide
  try {
    document.getElementsByClassName("scaffold-layout__main")[0].style.width = "125%";
    document.getElementsByClassName("scaffold-layout__main")[0].style.background = "white";
    document.getElementsByClassName("scaffold-layout")[0].style.setProperty("--scaffold-layout-gutter", "0px");
    document.getElementsByClassName("scaffold-layout")[0].style.background = "white";
    document.getElementsByClassName("scaffold-layout-container")[0].style.margin = "0px";
    document.getElementsByClassName("scaffold-layout-container")[0].style.paddingLeft = "5px";
    document.getElementsByClassName("scaffold-layout-container")[0].style.background = "white";
  } catch(e) { /* no-op */ }
}

s = 0; y();

var imgs = {};

imgs["img0"] = ""+
"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"+
"u+x/+cSVoCunr3KvXrgWda3jeuL1mzfSbnTeFNx8divv1qvbRbc/35l1l3C37J7mvYr7xver/7D/Y3enR+fRB0EP2h7GP7zziP/oxeOCx1+65j6hPal4ava05pnrsyPdId2Xno953vVC8uJzT+mfWn9WvbR7eeCvgL/aekf3dr2Svup/vfCN4Zttb93etvTF9N1/l//u8/uyD4Yftn9kfTzzKfnT08+TvpC+rP5q/7XpW8S3u/35/f0SnpSn+BXA"+
"YEUzMwF4vQ0AWgoADHg+o4xRnv8UBVGeWRUI/CesPCMqigcAdfD/PbYH/t3cAGDPFnj8gvoaaQDE0ABI8ALoiBGDdeCspjhXygsRngM2Rn3NyM8A/6Yoz5w/xP1zC+SqbuDn9l8D6nxC3rNDpgAAAIplWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAACQAAAAAQAAAJAA"+
"AAABAAOShgAHAAAAEgAAAHigAgAEAAAAAQAABVqgAwAEAAAAAQAAAQQAAAAAQVNDSUkAAABTY3JlZW5zaG901F1L9gAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAddpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYg"+
"eG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MjYwPC9leGlmOlBp"+
"eGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjEzNzA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KxSxVFQAAABxp"+
"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"+
"pkmTBuaaa7ZIl4jGKmsE0Oh84oknbBl/+9vfmh//+MdlXd76XrjRo0ebESNG2GpcdtllZv/996/vVdLylxECz7/4pvnokyWmAf27uP3PzHcabVlGpSu+KPPmzTN//etfbYQTTzzRnHbaacVHroWQ99xzD70Tr7E5vfbaa+a4446rhVzrRxYgIFasWGGaNm1qunbtWj8KTaWMosh88cUXZttttzWNGjWqN+XWgioCmyoCafzwt99+a77//e+badOm"+
"mebNm5sPP/zQfOc730kE4aJFi0yzZs1snPbt25uHH344UXwNrAgoAoqAIqAIlDMCiYhWnaM13aXcZZc1ZunSyDRubKjjtHW6RDRWWSMwaNAg84c//MGW8aGHHjIXXXRR2ZT3zTffNAMGDHDl2W233cxtt93mfvs7gwcPNv/+97/t4bZt2xo0gMvNbr31Vkc+jBw50px66qnlVsRNpjzT3p9nXnjpLVfffVs1N+eccaz7LXceeXK0+WjBEnfozNN+"+
"ag7Yr6X7XS47nboNMP955wNbnOeG9jDbb7dtuRQtUTlef/1106ZNGxvn+uuvz/vcJ0o4g8Cff/652WuvvczKlSttGceMGZNBqhtPEk2aNLHYYPHRJUuqnplyrOHy5cvNXXfdZV566SXzn//8xxWxMTV68NHx9NNPNyeffLI54IAD3DndUQTqCwJoz8FfFWO43/GRpL5YKX74hRdesCMRUNc+ffqYjh07Jq72ddddZ+68804b7+2337YjHBInohEU"+
"AUVAEVAEFIFyRIDUB0UbNfYj/KklQ+C7310dGbMqatx4VbKIGYRu23atzRv5r6r97DOoQf1I4oEHHiCcjf0jorWsCk3DulzZuIxvvPFGbBkvvvhiF/6KK66IDVeXJ2655RZXRiJa67Iom3zeI16eEB17+tU5f2vXfVMNl5Vfr84JgzijX59cLVyWB6ZOn+vyfOjRUUUn/X9d73fxvvxqZdHxyi0gfTBxzwkRrWVVvC5duriykZq1rMpWDoUhktLi"+
"Q0RrORQntgxEtlDbprKs/H6J28YmoicUgVpAoGfPns7n0KiYonPEMxh3T/vH6aNv0emmDUhD/V15jj766LTJ2Hil+GFStUaHHHKILQt8AH00S1yWTz/91NWFPuwnjq8RFAFFQBFQBBSBckUgkaKVGhRqKRBo1WqNmTcvIvVOAzN37lYpUkgf5Zhj1ppx4761CaxcuTUN50uflsaMR6CcFa0YjnXhhRfmFB7zIQ4fPjznGP+45JJLzN///nf7k4hW"+
"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"+
"QAIkEBKg0MoTgQRIgARIgARIgARIgARIgARIgARIgARIgARIgARyEqDQmhMgDycBEiABEiABEiABEiABEiABEiABEiABEiABEiABCq08B0iABEiABEiABEiABEiABEiABEiABEiABEiABEggJ4FMQuuSJUvC7Hr06JEzWx5OAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAq1DIJPQOnv27LDme+65Z+sQYE1IgARIgARIgARIgARIgARIgARI"+
"gARIgARIgARIICeBTEIrLVpz0ubhJEACJEACJEACJEACJEACJEACJEACJEACJEACLUkgk9DakgRYKRIgARIggVgCp5xyihk/frzZZpttzMKFC82mm24aGzduRxFpxKXN7SRAAiRAAiSQROCqq64yQ4cONVtssYWZMWOG2XnnnZOic18ZAtOmTTMHH3xwGGv06NHm8MMPL3MEd5MACZAACZBA1yKQSWilRWtlJ8fVV7eZFSsCs/XW3cwZZ2QXKSrL"+
"tfmPeu+99wxejhF69+5tDjvssOavVEINxo4da2bOnBnGGDJkiPna176WELt5d7399tvm2muvDSvwb//2b6Zv377NW5kWLznOx3333Tes5YUXXmhwXmYNRaSRNc9miY+O/4oVK8JrvRK2zVLPRinnDTfcYN58803TrVs3M2LECLPZZps1StFYjhYlsHr1arNmzRqz7bbbhuddUjW72jsPxLq77747RHLMMceY/fbbLwlPrn0ffvih2WmnncI0jjvu"+
"OHPnnXfmSq+rH7xx40azzz77mFmzZpmePXuG99XNN9+8q2Nh/UmABEiABEggIpBJaKWP1ohbppVvfGO1WbIkMN27G9up3jLTsV05Ml7C//mf/zlEMGDAAHPllVe2NI5f/epXZty4cWEdIQZ897vfNc8//7y56aabonrvuOOOZuTIkdFvd+X22283zzzzTLi5T58+Bh2KRgvPPvtsKJyjXOecc05ifRqt7F2pPEEQmJ/97Gdm8uTJ9t7V3bz77ruZ"+
"xf8i0mhl5ltvvbX54osvDCaY/PjjjxuqqrfddpvBtSoBH0WOP/54+dlp+fvf/94+59onzOzfv39VRZNOmafc8POf/zy6P37++eehlXbKQxmNBFITwDMY7ysvv/yyef/996PjdtttN/Ov//qv5t///d/DZ+BWW20V7cNKV3vnGTVqlOnXr1/I4I477jAnnnhiuF6tf2effba57LLLwuRfeOEF8+Mf/7haWTVFukuXttn3y8XmjTdWh+XdYYevmEsv"+
"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"+
"N6uxoxbUXq5mJQBh9Xvf+154mB1VZKxFedYkwg/r+GAowbrJMdbVivzkkgRIgARIgASamkAmoZUWrU3d1iw8CZAACWQmgA4VrJEQ7CRGmYVWHFdEGkiHgQRIgARIgASyEsCIGVj4wor14IMPzno44zsEMELNTsoYbrXucczWW2/txCj/86abbjLWnVYU0bqKMD/5yU+i31whARIgARIggWYmkEloxXA/BAzrZSABEiABEiABEiABEiABEiABEiCB"+
"LATspGzGzkMQHgJr1rvvvjvL4YxLAiRAAiRAAg1NIJPQSovWhm5LFo4ESIAESIAESIAESIAESIAESIAESIAESIAESKBOBDIJrXUqI7MlARIgARIgARIgARIgARIgARIgARIgARIgARIggYYmkElopUVrQ7clC0cCJEACJEACJEACJEACJEACJEACJEACJEACJFAnApmE1jqVkdmSAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQEMToNDa0M3D"+
"wpEACZAACZAACZAACZAACZAACZAACZAACZAACTQDAQqtzdBKLCMJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEBDE6DQ2tDNw8KRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAk0AwEKrc3QSiwjCZAACZAACZAACZAACZAACZAACZAACZAACZBAQxOg0NrQzcPCkQAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJNAMBCq3N0EosIwmQAAmQAAmQ"+
"AAmQAAmQAAmQAAmQAAmQAAmQQEMToNDa0M3DwpEACZAACZAACZAACZAACZAACZAACZAACZAACTQDAQqtzdBKLCMJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEBDE8gktC5ZsiSsTI8ePRq6UiwcCZAACZAACZAACZAACZAACZAACZAACZAACZAACdSSQCahdfbs2WHZ9txzz1qWkXmRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQEMTyCS0"+
"0qK1oduShSMBEiABEiABEiABEiABEiABEiABEiABEiABEqgTgUxCa53KyGxJgARIgAS6MIG1a9eaH//4x2bhwoXml7/8pfnf//3fLkyDVScBEiCB6hOYNm2aOfjgg8OMRo8ebQ4//PDqZ8ocSIAESIAESIAESKAFCGQSWmnR2gItzioUQiAIAvPZZ5+Zrbbaymy22WZl07zlllvM3LlzTbdu3czFF19sttxyy7LHNGuETz75xAwfPjws/v7772+O"+
"PvroulQFncS77747zPuYY44x++23X13KwUyNef75582sWbPMO++8YyCaIuDaueiii1Lh+dOf/mTOOOOMMO7TTz9tfv7znyceN2TIEPPFF1+Yr3zlK+bSSy9NjMudJFBrAr///e/Nhg0bzE477WSw3sqhq9yHN27caM466yyD5S677BLdr5q5bVGXffbZJ7x39+zZ07z55ptm8803b+YqsewkQAIkQAIkQAIkUBMCmYRW+mitSZswkwYlsHz5cnP5"+
"5Zebxx9/3Lz44otRKbt37x6KeP/xH/9hDjnkELP77rtH+2TlF7/4RXgcfuODxfbbby+7Wm4JQVkYnHjiieaOO+6oSx1HjRpl+vXrF+aNMqAsEiD6DR48OPz5gx/8IGxX2ecuUZ9zzjkn7EBjH4TjY4891o3G3x4Cq1atClndf//9nfbiulmxYkWn7e6GTz/91PTq1SsUTnv37m0mTZrkRun0e+uttw7jYwc+ijRTgCh90003RUXecccdzciRI6Pf"+
"7srtt99unnnmmXBznz59zHHHHedG4e8GI4APbgi4T77++usNVrpiixN3Hx44cKDBtS3ht7/9rcGHOV/AR0350IL91157rdlmm218Ueu2bf369eGHHRTgRz/6kZk5c2bdylJkxo888kg4igBpXnHFFQbtxkACJEACJEACJEACJFCGgO2Epg4ff/xxgD+G5iHQp88aqzKsDP9WrmyecjdaSW1nI7DCEBSbsn++slsBNjrOCq2+KC2zbc6cOVFdrbhZ"+
"t3pZK+KoHFZoLSnHk08+Ge2zlq4l+/SPBQsWBD169IjiHnTQQYEVD3UUricQsBarETtcO9YqKrAiRPhnRcGEIzt2WUE8SsNas3bsSFjT12pCtIbcZYfoRvWV+83f/va32LKedNJJUfzf/OY3sfG4o3EISLtaobVxClWlksTdh/V9FTzwjIwLb7/9dnSOI+4HH3wQF7Vu29etWxeVEfe4VgnWqjXYa6+9wrrhvmpHCrRK1VgPEiABEiABEiABEqga"+
"AVj7MLQwgQMPXG1fkNuFVr4fV9bQWjhEJw+djV//+tfBhRdeGPTv3z+A+CYdZyx9gUKrj0p1t8V18JFrGqF10aJFoTAobXvAAQewk5mxybSYYocQZzw6CFavXh194IBIa4dbp0qj1YRW65c2tt4UWmPRNOwOuadQaC39cGlHinjbjEKrF0vNNl533XXRO461oK9ZvsyIBEiABEiABEiABJqVgF8ViqkNLVpjwDTwZgqt+RsHlnfSMe7bt29ghzF2"+
"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"+
"Ly7oSbeEmzvkHscuW7as0/WA+Hg2w0I96fkqzyBMLCdB+/mW6wT7tJXt7bffLtFDn9VSPjxT8L6srbhlH+7py5cvj47zrRTxbES6edsVaegPZnhvqCSAk9RflrW6XispL48hARIgARIgARIggbwEuiEB++KTKtjhlWE8+1KaKj4j5SfQ1mbMW29tNHbkmVm3zpiddupmevZs/8ufevkUkL91WWBsP8V84xvdzL77bmI237z8ca0Yw/rZNHZou7Gd"+
"vPDv7/7u78wuu+xidt55Z7PddtuZbt261aTayN92fsIy/OM//qOxYozZdNNNa5K3m4kVIc3LL79sttpqK7P//vubr371q26UVL+t5ZexQk7I9+tf/7o9z3cy3/72t82WW25Z9njrF9W88cYbxg5tNrZja/bee++68cA5Yi1e7fU6316v68J6WEHBXrM9y9ZDIhx88MHmiSeeCH9akd9YkV521WxpZzYP22PBggUG9/s99tgjPMdrVgAno//+7/82"+
"1jdxuNVas5kdd9zRiVHdn83erla4MdbCPTwvrb9F88Mf/jC8vqpLLT51vHagHXHNW0HS7LDDDuG1suuuu6a6du0HJ2OF8SgDK4SZoUOHRr9rsYJzAte5tTg3eDeyopb51re+Fd6DKr0PZi13I7VrI9yHcV7hmYRnAdj85Cc/MV/72teyYi00PspjJzM022+/vdlnn33CZ2WWDPI8G7PkExfXfiw1VigNd+N58J3vfCcuaux2+6HN2A9I0f56XK9R"+
"5lwhARIgARIgARIggRoQyCS01qA8zIIESIAEuiwBa3kVipoAAIETYlS9RPRGagSIhBDREazPRXPVVVc1UvHKloXtWhZR6ggQ0775zW+G4iYOgsBph9LXXVBLXQFGJIEmIQBh9Xvf+15YWmuJbqzbjMwlx/W6zTbbGDviJjyW12tmhDyABEiABEiABEigCQlkElpp0dqELcwikwAJNAUBO1TV2GGlxvoPDMt70UUXmfPOO68pyl6LQk6bNs1Y1wbm"+
"H/7hH4wdmluLLAvJg+1aCMYoEViv/+AHP4h+28mCjJ10KvrNFRIggWIIWHdFZsaMGWFi1gWN2XrrrTMnbF20mH/6p3+KjuP1GqHgCgmQAAmQAAmQQAsTyCS04oUJYc8992xhJKwaCZAACdSGgPUrGA55tv7qoqHxyBlWP9bHaV2H69eGQGvmwnatXrvaCbrMySefHGaA6wRW31gykAAJNB6BiRMnmt/+9rdhwbbddttQuOX12njtxBKRAAmQAAmQ"+
"AAkUSyCT0EqL1mLhMzUSIIGuTUAPiRcS6ITCqnXfffeVTVw2GQG2a/Ua7K9//av5y1/+YjbZZBMDKzs7IVj1MmPKJEACJEACJEACJEACJEACJJCRQCahNWPajE4CJEACJJBAABPpyGRXmCzlZz/7Weg+QA+NTjicuxqUANu1QRuGxSIBEiABEiABEiABEiABEiCBKhPIJLTSorXKrcHkSYAESIAESIAESIAESIAESIAESIAESIAESIAEmpJAJqG1"+
"KWvIQpMACZAACZAACZAACZAACZAACZAACZAACZAACZBAlQlQaK0yYCZPAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQ+gQotLZ+G7OGJEACJEACJEACJEACJEACJEACJEACJEACJEACVSZAobXKgJk8CZAACZAACZAACZAACZAACZAACZAACZAACZBA6xOg0Nr6bcwakgAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJVJkAhdYqA2byJEACJEAC"+
"JEACJEACJEACJEACJEACJEACJEACrU+AQmvrtzFrSAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkUGUCFFqrDJjJkwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJtD4BCq2t38asIQmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQJUJUGitMmAmTwIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIk0PoEKLS2fhuzhiRAAiRAAiRAAiRAAiRAAiRA"+
"AiRAAiRAAiRAAlUmkFtoXbhwoVm6dKnZbrvtTI8ePcyWW25Z5SIzeRIgARIgARIgARIgARIgARIgARIgARIgARIgARJoLAK5hdbjjz/ejBkzJqoVxNb/+Z//MQMGDDBbbLFFtJ0rJEACJEACJEACJEACJEACJEACJEACJEACJEACJNCqBHILrSeeeKIZPXp0Jz6XXHKJGTx4cKft3EACJEACJEACJEACJEACJEACJEACJEACJEACJEACrUYgt9Da"+
"1tZmVq1aZZYsWWLGjRtnhg4dGjHauHGj6datW/SbKyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiTQigRyC60ulD322MPMmTMn3Pzxxx+HflvdOPxNAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAq1EoHCh9aijjjL33HNPyOiVV14xe++9dyvxYl1IgARIgARIgARIgARIgARIgARIgARIgARIgARIoBOBwoXWfv36mVGjRoUZTZ061Rxw"+
"wAGdMuUGEiABEiABEiABEiABEiABEiABEiABEiABEiABEmglAoULraeccoq55ZZbQkYUWlvpVGFdSIAESIAESIAESIAESIAESIAESIAESIAESIAE4ghQaI0jw+0kQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkJJAVYXWZ5991vzLv/xLyqIwGgmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAk0J4HChdY//OEPZvjw4SGNsWPHml/96lfN"+
"SYalJgESIAESIAESIAESIAESIAESIAESIAESIAESIIGUBAoXWuGfFX5aEfr27WseeeSRlEVhNBIgARIgARIgARIgARIgARIgARIgARIgARIgARJoTgKFC62fffaZ2XXXXc0XX3wREunXr5859dRTw22bbbaZ2Xzzzc0mm2zSnLRYahIgARIgARIgARIgARIgARIgARIgARIgARIgARLwEChcaEUe8+bNM5dccol58MEHI8FV8r733nvNf/7nf8pP"+
"LkmABEiABEiABEiABEiABEiABEiABEiABEiABEig6QlURWhdtWqVgQuB++67z0yePLkE0j333GP+67/+q2Qbf5AACZAACZAACZAACZAACZAACZAACZAACZAACZBAMxMoXGhdv3692Xfffc2sWbNCLj169DBDhgwxe+yxh9lyyy3NbrvtZrbddttmZsaykwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEAJgcKFVlisHnXUUWEm3bt3NwsWLDAQ"+
"WxlIgARIgARIgARIgARIgARIgARIgARIgARIgARIoFUJFC60wnr1oosuCnmNGDHCDBo0qFXZsV4kQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkEBIoXGg95ZRTQv+sSP3xxx83Bx98MFGTAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQEsTqKrQOnXqVHPAAQe0NEBWjgRIgARIgARIgARIgARIgARIgARIgARIgARIgAQotPIcIAESIAES"+
"IAESIAESIAESIAESIAESIAESIAESIIGcBAoXWk888UQzevTosFi0aM3ZOjycBEiABEiABEiABEiABEiABEiABEiABEiABEigKQgULrT+4he/CH2zovavvfaa2XPPPZsCBAtJAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAApUSKFRoXbhwoenVq1dUllWrVpktttgi+s0VEiABEiABEiABEiABEiABEiABEiABEiABEiABEmhFArmF1j//+c9m"+
"8uTJ5qOPPjKPPfZYxKh3795m0qRJ0W+ukAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkECrEsgttB5//PFmzJgxJXx69uwZiq50G1CChT9IgARIgARIgARIgARIgARIgARIgARIgARIgARalEBuoXXUqFHm+eefN9tvv73p0aOH+f73v28OPfRQs+mmm7YoMlaLBEiABEiABEiABEiABEiABEiABEiABEiABEiABEoJ5BZaS5PjLxIgARIgARIg"+
"ARIgARIgARIgARIgARIgARIgARLoegQotHa9NmeNSYAESIAESIAESIAESIAESIAESIAESIAESIAECiZAobVgoEyOBEiABEiABEiABEiABEiABEiABEiABEiABEig6xGg0Nr12pw1JgESIAESIAESIAESIAESIAESIAESIAESIAESKJgAhdaCgTI5EiABEiABEiABEiABEiABEiABEiABEiABEiCBrkeAQmvXa3PWmARIgARIgARIgARIgARIgARI"+
"gARIgARIgARIoGACFFoLBsrkSIAESIAESIAESIAESIAESIAESIAESIAESIAEuh4BCq1dr81ZYxIgARIgARIgARIgARIgARIgARIgARIgARIggYIJUGgtGCiTIwESIAESIAESIAESIAESIAESIAESIAESIAES6HoEKLR2vTZnjUmABEiABEiABEiABEiABEiABEiABEiABEiABAomQKG1YKBMjgRIgARIgARIgARIgARIgARIgARIgARIgARIoOsR"+
"oNDa9dqcNSYBEiABEiABEiABEiABEiABEiABEiABEiABEiiYwP8HK2PMuJzZbBEAAAAASUVORK5CYII="+
"";

Simple LinkedIn Resume Generator (js)

Using a good Curve (25519) to implement a Dual_EC_DRBG based PRNG (bad) in Py

('(secret)', 'e=17211717500188099600568359980754843411619158773615107210604175251693280840607')

('(pub)', 'P', [15395204427582176828288140787842485523287609441875116301821962120815246523165L, 50972925492742860438904710940702337165443708937045978727200475339385656596802L])
('(pub)', 'Q', [54379753906375494870435841730857804485220282788952166778590221998932647140003L, 10542862410506568609831388869015874299228416047841839543681429954753256816461L])

Running first 3 prng outputs:
(1, '(pub)', 'rQ', [35085355371653225423586630859880842591953206407407058841980055259804158729190L, 17642611272178418467727837731595189739886826601909812633308152323681611088547L])
(2, '(pub)', 'rQ', [49239914602839976828302644790025511534554523065665058491590572885136905915179L, 8379389248907743775496938151971165397933498701999710225313910125914506554304L])
(3, '(pub)', 'rQ', [46183005535555809253746127552459776700939930656325949263490016267446108722393L, 39473869171813125431282259087083131250343325900631309529780555199432408899154L])

Predicting next prng output:
(4, '(pub)', 'sQ', [41709487842035263418257031106229715942424730101066821754384094668791902320545L, 47532264341329149594994883777108416347984877171111029279692175022666589952933L])
Running next prng output:
(4, '(pub)', 'rQ', [41709487842035263418257031106229715942424730101066821754384094668791902320545L, 47532264341329149594994883777108416347984877171111029279692175022666589952933L])
import sys
import random

def egcd(a, b):
	s = 0; news = 1
	r = b; newr = a
	while r != 0:
		quot = (newr / r)
		prev = s
		s = (news - (quot * prev))
		news = prev
		prev = r
		r = (newr - (quot * prev))
		newr = prev
	if (news < 0):
		news += b
	return news

def dub(p, a, b, n):
	# l = 3*x^2 + 2*a*x + 1 / 2*b*y
	xx = pow(p[0], 2, n)
	ya = (((3 * xx) + (2 * a * p[0]) + 1) % n)
	yd = ((2 * b * p[1]) % n)
	l = ((ya * egcd(yd, n)) % n)
	# xr = b*l^2 - a - 2*x
	ll = pow(l, 2, n)
	newx = (((b * ll) - a - (2 * p[0])) % n)
	# yr = ((3*x + a) * l) - b*l^3 - y
	yb = (((3 * p[0]) + a) % n)
	newy = (((yb * l) - (b * ll * l) - p[1]) % n)
	return [newx, newy]

def add(p, q, a, b, n):
	# l = (Qy - Py) / (Qx - Px)
	ya = ((q[1] - p[1]) % n)
	yd = ((q[0] - p[0]) % n)
	l = ((ya * egcd(yd, n)) % n)
	# xr = b*l^2 - a - Px - Qx
	ll = pow(l, 2, n)
	newx = (((b * ll) - a - p[0] - q[0]) % n)
	# yr = ((2*Px + Qx + a) * l) - b*l^3 - Py
	yb = (((2 * p[0]) + q[0] + a) % n)
	yc = ((b * ll * l) % n)
	newy = ((((yb * l) % n) - yc - p[1]) % n)
	return [newx, newy]

def mul(m, p, a, b, n):
	r = None
	while (m > 0):
		if ((m % 2) == 1):
			if (r == None):
				r = p
			else:
				r = add(r, p, a, b, n)
		p = dub(p, a, b, n)
		m = (m >> 1)
	return r

a = 486662
b = 1
n = 57896044618658097711785492504343953926634992332820282019728792003956564819949
try:
	p = [int(sys.argv[2]), int(sys.argv[3])]
except:
	p = [9, 43114425171068552920764898935933967039370386198203806730763910166200978582548]

# e is our secret key here
d = random.randint(3, n - 3)
e = random.randint(3, n - 3)
print("(secret)","e=%d"%e)
print("")

# let G = any base point
# calculate public point constants: P = edG & Q = dG
g = mul(d, p, a, b, n)
g = mul(e, g, a, b, n)
print("(pub)","P",g)
h = mul(d, p, a, b, n)
print("(pub)","Q",h)
print("")

# multiply random seed with the points: P' = rP & Q' = rQ
# these then equate to: P' = redG & Q' = rdG
# the 'key' to this is basing the next r' off of the output of P' and publishing the point info of Q' and repeat
r = random.randint(3, n - 3)
print("Running first 3 prng outputs:")
for x in range(1, 4):
	t = mul(r, g, a, b, n)
	u = mul(r, h, a, b, n)
	print(x,"(pub)","rQ",u)
	r = ((t[0] + t[1]) % n)
print("")

# to predict the next output then multiply: eQ' == erdQ == erdG == redG == P'
# you can now get r' and calculate the next P'' & Q''
print("Calculating next prng output (using e & latest Q):")
z = mul(e, [u[0], u[1]], a, b, n)
s = ((z[0] + z[1]) % n)
v = mul(s, z, a, b, n)
w = mul(s, h, a, b, n)
print(x+1,"(pub)","sQ",w)

# just to be sure
print("Running next prng output:")
for y in range(0, 1):
	t = mul(r, g, a, b, n)
	u = mul(r, h, a, b, n)
	print(x+1,"(pub)","rQ",u)
	r = ((t[0] + t[1]) % n)
print("")

Using a good Curve (25519) to implement a Dual_EC_DRBG based PRNG (bad) in Py

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.
	}

}

Secure Message iOS App in Swift

A Basic ECC Crypto C Library

$ gcc -Wall -O2 -o crypto.out crypto.c 
$ ./crypto.out pgen 

x: [0][7][8]=[4880449572248532107701327692677150037572104965674444437385142643512]
y^2: [0][8][8]=[55380869580354224246510320945966028555317502525493429017926966133843051295792]
y: [0][8][8]=[29139577269574826820926935201858599433659429201914524387051377879407947248399]

$ ./crypto.out ecdh 

  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    17101372528228046186917539708449150102286202153232625914396359604563
      * P(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)

      = Q(x, y) = (38616894501258673852970036866543659072635089662921568068340331697101576554270, 11643169480892671629858370512120070259223559167488419079368716073871895962191)


  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    12761708460950637051849206961623904377595162559992784492118523127724
      * P(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)

      = Q(x, y) = (32477568348650104756356831653582245276448206004887493791495532411741897328458, 28063399834915231969701720976238207473037393607389857712493721633327966471074)


  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    17101372528228046186917539708449150102286202153232625914396359604563
      * P(x, y) = (32477568348650104756356831653582245276448206004887493791495532411741897328458, 28063399834915231969701720976238207473037393607389857712493721633327966471074)

      = Q(x, y) = (50351522368134241142819091458530332577142187603512687882689541070260459723459, 50221873001104206223375144395698504985316658966829066883833880358017679689691)


  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    12761708460950637051849206961623904377595162559992784492118523127724
      * P(x, y) = (38616894501258673852970036866543659072635089662921568068340331697101576554270, 11643169480892671629858370512120070259223559167488419079368716073871895962191)

      = Q(x, y) = (50351522368134241142819091458530332577142187603512687882689541070260459723459, 50221873001104206223375144395698504985316658966829066883833880358017679689691)

$ ./crypto.out psig 1234
Sign:

d=[0][7][8]=[6538729008379423383518670268808646995288081785172776771199052697947]
P=(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)
dP=(x, y) = (14803911054618354585053684993263408691271520722671720792800356230205975217771, 11341018286853926408444620055947798723342444873304700039478772171855673884697)

h=[0][1][1]=[1234]

k=[0][7][8]=[8232207578348459001023088138872857936131335029042440438075457201646]
kP=(x, y) = (31369530948108575720266905846722071920323191212477028674668598541699791483098, 9139751042718096616566254849289158387164951389944576741553517798024823610265)
r=[0][15][40]=[149879290832663482850783820956010355668942537394521944880131051841396204961042266046319521062426903751537961621638691535228251607511236059222514]

Verify:

P=(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)
dP=(x, y) = (14803911054618354585053684993263408691271520722671720792800356230205975217771, 11341018286853926408444620055947798723342444873304700039478772171855673884697)

h=[0][1][1]=[1234]

kP=(x, y) = (31369530948108575720266905846722071920323191212477028674668598541699791483098, 9139751042718096616566254849289158387164951389944576741553517798024823610265)
r=[0][15][40]=[149879290832663482850783820956010355668942537394521944880131051841396204961042266046319521062426903751537961621638691535228251607511236059222514]

(ek+hd)P=(x, y) = (51608966471038425598161188547671772160458281462630354621077601757763167308540, 17341325372123419326180270058693292718501038313000707228169935748980907943680)
==
rP=(x, y) = (51608966471038425598161188547671772160458281462630354621077601757763167308540, 17341325372123419326180270058693292718501038313000707228169935748980907943680)

[GOOD]
$ ./crypto.out penc 1234
Encrypt:

P=(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)
dP=(x, y) = (19547865285467707344496388019666506767309887017163137556078134582670804301851, 36180672281078333342162157694938953906019362565570590334768364807054263309922)

h=[0][1][1]=[1234]

k=[0][7][8]=[3873062938517225834483697070677896532844396051944377142545351178872]
kP=(x, y) = (31307487414183777373465427652384958249613501222520858769326720798351611112341, 42327343332069595141617190350930625663948508466032477124028143798481079201841)
kdP=(x, y) = (17546014782981223520910010062776248455503090940217295620750099274078977893385, 40213720617506594547897783150731613585768378209283692064354760696398013829358)

Decrypt:

P=(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)
dP=(x, y) = (19547865285467707344496388019666506767309887017163137556078134582670804301851, 36180672281078333342162157694938953906019362565570590334768364807054263309922)

d=[0][7][8]=[22873181922607864299317581582398892665871088710637471622228302193764]
dkP=(x, y) = (17546014782981223520910010062776248455503090940217295620750099274078977892151, 40213720617506594547897783150731613585768378209283692064354760696398013829358)

t=[0][1][8]=[1234]

$ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include "../lib/sha256.c"
#include "../lib/aes256.c"
#include "../lib/ec.c"

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;
}

void eccp(ecc *e, char *xs)
{
	int psiz = (e->p)->size;
	bnum *x = bninit(psiz), *y = bninit(psiz), *t;
	
	bnum *xa = bninit(psiz * 4), *xx = bninit(psiz * 4);
	bnum *yy = bninit(psiz * 4), *vv = bninit(psiz * 4);
	
	if (xs != NULL) { t = bndec(xs); }
	else { t = bnrnd(psiz); }
	bncopy(t, x);
	
	while (1)
	{
		// yy = ((x * (x * x)) + (a * (x * x)) + x) % m
		bnzero(xx); bnmul(x, x, xx);
		bnzero(xa); bnmul(xx, e->a, xa);
		bnzero(vv); bnmul(x, xx, vv);
		
		bnzero(yy); bnadd(vv, xa, yy, 1); bnadd(yy, x, vv, 1);
		bndiv(vv, e->p, xx, t);
		// todo: divide t by b?
		
		printf("\n");
		bnout("x: ", x, "\n");
		bnout("y^2: ", t, "\n");
		
		if (sqrtmod(t, e->p, y) == 0) { break; }
		
		if (x != NULL) { bnfree(x); }
		x = bnrnd(psiz);
	}
	
	bnout("y: ", y, "\n\n");
	// note: another y point == p - y
	
	bnzero(yy); bncopy(t, yy);
	bnzero(vv); bnmul(y, y, vv);
	bnzero(xx); bndiv(vv, e->p, xa, xx);
	
	bnfree(xa); bnfree(vv); bnfree(t);
	
	if (bncmp(xx, yy) != 0)
	{
		bnout("y^2: ",xx," != "); bnout("y^2: ",yy,"\n");
	}
	
	bnfree(xx); bnfree(yy);
	bnfree(x); bnfree(y);
}

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

char *ecdh(ecc *e, char *n, int o)
{
	char *r = n;
	bnum *m;
	ecc *f;
	
	if (r == NULL)
	{
		m = bnrnd((e->p)->size);
		r = bnstr(m);
	}
	
	else
	{
		m = bndec(n);
	}
	
	f = ecdup(e);
	pmul(m, e, f);
	
	if (o == 1)
	{
		char v[2050];
		bzero(v, 2050 * sizeof(char));
		strncat(v, "    "       , max(0, 2048 - strlen(v)));
		strncat(v, r            , max(0, 2048 - strlen(v)));
		strncat(v, "\n      * P", max(0, 2048 - strlen(v)));
		printf("\n");
		ecout(1,v,e,"\n\n");
		ecout(0,"      = Q",f,"\n");
		printf("\n");
	}
	
	bnfree(e->x); e->x = bndup(f->x);
	bnfree(e->y); e->y = bndup(f->y);
	bnfree(m);
	ecfree(f);
	
	return r;
}

/*
ECC - Private Sign / Public Verify

* initialize
- generate secret key          : d
- publish                      : P, dP

* private sign
- hash message                 : h = SHA256(m)
- secret unique multiplier     : k
- calculate public multipliers : kP
                               : e = hP(x)
                               : r = ((e * k) + (h * d))
- publish                      : kP, r

* public verify
                               : e = hP(x)
-                              : (e * kP) + (h * dP) == rP
*/

int ecsig(ecc *e, bnum *d, ecc *dp, char *hh, ecc *kp, bnum *r, int o)
{
	int a = 0, psiz = (e->p)->size;
	bnum *k, *h;
	bnum *t = bninit(psiz * 5), *u = bninit(psiz * 5);
	ecc *hp = ecdup(e), *ekp = ecdup(e), *hdp = ecdup(e), *ekhdp = ecdup(e), *rp = ecdup(e);
	ect *tadd = etinit(e);
	
	if (hh != NULL)
	{
		if (((kp->x)->leng == 1) && ((kp->x)->nums[0] == 0))
		{
			if ((d->leng == 1) && (d->nums[0] == 0))
			{
				k = bnrnd(psiz); bncopy(k, d); bnfree(k);
				pmul(d, e, dp);
				
				if (o == 1) { bnout("d=", d, "\n"); }
			}
		}
		
		if (o == 1)
		{
			ecout(0, "P=", e, "\n");
			ecout(0, "dP=", dp, "\n\n");
		}
		
		h = bndec(hh);
		pmul(h, e, hp);
		
		if (o == 1) { bnout("h=", h, "\n\n"); }
		
		if (((kp->x)->leng == 1) && ((kp->x)->nums[0] == 0))
		{
			k = bnrnd(psiz);
			pmul(k, e, kp);
			
			if (o == 1) { bnout("k=", k, "\n"); }
			
			bnzero(t); bnmul(hp->x, k, t);
			bnzero(u); bnmul(h, d, u);
			bnadd(t, u, r, 1);
			
			bnfree(k);
		}
		
		if (o == 1)
		{
			ecout(0, "kP=", kp, "\n");
			bnout("r=", r, "\n\n");
		}
		
		pmul(hp->x, kp, ekp);
		pmul(h, dp, hdp);
		padd(ekp, hdp, ekhdp, tadd);
		pmul(r, e, rp);
		
		if ((d->leng == 1) && (d->nums[0] == 0))
		{
			if (o == 1)
			{
				ecout(0, "(ek+hd)P=", ekhdp, "\n==\n");
				ecout(0, "rP=", rp, "\n\n");
			}
		}
		
		if (bncmp(ekhdp->x, rp->x) == 0)
		{
			if (bncmp(ekhdp->y, rp->y) == 0)
			{
				a = 1;
			}
		}
		
		bnfree(h);
	}
	
	bnfree(t); bnfree(u);
	ecfree(hp); ecfree(ekp); ecfree(hdp); ecfree(ekhdp); ecfree(rp);
	etfree(tadd);
	
	return a;
}

/*
ECC ElGamal - Public Encrypt / Private Decrypt

* initialize
- generate secret integer  : d
- publish                  : P, dP

* public encrypt
- generate secret key      : t = (SHA256(pwd) || tmpkey)
- secret unique multiplier : k = rand()
- public key encrypt       : r = k * P        = kP
                           : s = t + (k * dP) = t + kdP = kdP(x + t, y + t)
- encrypt optional message : i = rand()
                           : e = AES256CBC(m, i, t)
- publish                  : [ (r, s) , (i, e) ]

* private decrypt
- private key decrypt      : u = s - (d * r)
                               = (t + kdP) - (d * kP)
                               = kdP(x + t, y + t) - dkP(x, y)
                               = kdP(x - x + t)
                               = t
*/

void ecenc(ecc *e, bnum *d, ecc *dp, char *hh, ecc *kp, ecc *kdp, int o)
{
	int psiz = (e->p)->size;
	bnum *k, *h, *t;
	ecc *dkp;
	
	if (hh != NULL)
	{
		if ((d->leng == 1) && (d->nums[0] == 0))
		{
			k = bnrnd(psiz); bncopy(k, d); bnfree(k); k = NULL;
			pmul(d, e, dp);
		}
		
		if (o == 1)
		{
			ecout(0, "P=", e, "\n");
			ecout(0, "dP=", dp, "\n\n");
		}
		
		h = bndec(hh);
		
		if (((kp->x)->leng == 1) && ((kp->x)->nums[0] == 0))
		{
			if (o == 1) { bnout("h=", h, "\n\n"); }
			
			k = bnrnd(psiz);
			pmul(k, e, kp);
			
			pmul(k, dp, kdp);
			t = kdp->x; kdp->x = bninit(psiz + 1); bncopy(t, kdp->x); bnfree(t);
			bnadd(kdp->x, h, kdp->x, 1);
			
			if (o == 1)
			{
				bnout("k=", k, "\n");
				ecout(0, "kP=", kp, "\n");
				ecout(0, "kdP=", kdp, "\n\n");
			}
			
			bnfree(k);
		}
		
		else
		{
			dkp = ecdup(e);
			pmul(d, kp, dkp);
			t = bninit(psiz); bnsub(kdp->x, dkp->x, t, 1);
			
			if (o == 1)
			{
				bnout("d=", d, "\n");
				ecout(0, "dkP=", dkp, "\n\n");
				bnout("t=", t, "\n\n");
			}
			
			bnfree(t);
			ecfree(dkp);
		}
		
		bnfree(h);
	}
}

int main(int argc, char **argv)
{
	char *a = "486662", *b = "1", *p = "57896044618658097711785492504343953926634992332820282019728792003956564819949";
	char *x = "9", *y = "43114425171068552920764898935933967039370386198203806730763910166200978582548";
	char *n, *m, *z = NULL;
	char hash[256];
	unsigned char msg[16], key[256];
	bnum *d, *r, *s;
	bnum *t, *u, *v, *w;
	ecc *e, *f, *dp, *kp, *kdp;
	
	if (argc > 2) { z = argv[2]; }
	
	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);
	
	if (strcmp(argv[1], "pgen") == 0)
	{
		eccp(e, z);
	}
	
	if (strcmp(argv[1], "pmul") == 0)
	{
		ecdh(e, argv[2], 1);
	}
	
	if (strcmp(argv[1], "ecdh") == 0)
	{
		f = ecdup(e);
		
		n = ecdh(e, NULL, 1);
		m = ecdh(f, NULL, 1);
		
		t = e->x; u = e->y;
		e->x = f->x; e->y = f->y;
		f->x = t; f->y = u;
		
		ecdh(e, n, 1);
		ecdh(f, m, 1);
		
		free(n); free(m);
		ecfree(f);
	}
	
	if (strcmp(argv[1], "psig") == 0)
	{
		d = bninit((e->p)->size);
		dp = ecdup(e); kp = ecdup(e);
		r = bninit((e->p)->size * 5);
		s = bninit((e->p)->size * 5);
		
		printf("Sign:\n\n");
		(kp->x)->leng = 1; (kp->x)->nums[0] = 0;
		(kp->y)->leng = 1; (kp->y)->nums[0] = 0;
		ecsig(e, d, dp, z, kp, r, 1);
		
		printf("Verify:\n\n");
		bnzero(d);
		if (ecsig(e, d, dp, z, kp, r, 1) != 0) { printf("[GOOD]\n"); }
		else { printf("\n[FAILED]\n"); }
		
		bnfree(d); bnfree(r); bnfree(s);
		ecfree(dp); ecfree(kp);
	}
	
	if (strcmp(argv[1], "penc") == 0)
	{
		d = bninit((e->p)->size);
		dp = ecdup(e); kp = ecdup(e); kdp = ecdup(e);
		
		printf("Encrypt:\n\n");
		(kp->x)->leng = 1; (kp->x)->nums[0] = 0;
		(kp->y)->leng = 1; (kp->y)->nums[0] = 0;
		ecenc(e, d, dp, z, kp, kdp, 1);
		
		printf("Decrypt:\n\n");
		ecenc(e, d, dp, z, kp, kdp, 1);
		
		bnfree(d);
		ecfree(dp); ecfree(kp); ecfree(kdp);
	}
	
	if (strcmp(argv[1], "hash") == 0)
	{
		sha256 hobj;
		sha256init(&hobj);
		sha256update(&hobj, (unsigned char *)argv[2], strlen(argv[2]));
		sha256final(&hobj, hash);
		printf("%s\n", hash);
	}
	
	if (strcmp(argv[1], "menc") == 0)
	{
		bzero(key, 256);
		strcpy((char *)key, argv[3]);
		aes256keys(key);
		
		bzero(msg, 16);
		strcpy((char *)msg, argv[2]);
		aes256core(msg, key, 0);
		
		int i;
		for (i = 0; i < 16; ++i) { printf("%02x", msg[i]); }
		printf("\n");
	}
	
	ecfree(e);
	
	return 0;
}

A Basic ECC Crypto C Library

Curve25519 ECDH (Using bn.c)

$ a="486662"; b="1"; p="57896044618658097711785492504343953926634992332820282019728792003956564819949"
$ x="9"; y="43114425171068552920764898935933967039370386198203806730763910166200978582548"
$ ./ec.out "$a" "$b" "$p" "$x" "$y"

  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    25401056036235045220436215630227531782477232918721757282083167532085
      * P(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)

      = Q(x, y) = (2152181765955508802144811366391548358206596269149408838626058763387933371482, 11578758651574146923540708489424215527254544219548915855368449621649032022464)

$ ./ec.out "$a" "$b" "$p" "$x" "$y"

  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    14923829837058818803507170043209691418601607100463140519943405743260
      * P(x, y) = (9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)

      = Q(x, y) = (10616717952671290844210554362351196131260739681824015303968067548318407877443, 44474405861832920486283648404346377112839096552743000067399554821542048806382)

$ x="10616717952671290844210554362351196131260739681824015303968067548318407877443"; y="44474405861832920486283648404346377112839096552743000067399554821542048806382"
$ ./ec.out "$a" "$b" "$p" "$x" "$y" "25401056036235045220436215630227531782477232918721757282083167532085"

  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    25401056036235045220436215630227531782477232918721757282083167532085
      * P(x, y) = (10616717952671290844210554362351196131260739681824015303968067548318407877443, 44474405861832920486283648404346377112839096552743000067399554821542048806382)

      = Q(x, y) = (5796465362698668629448044005371479448781495853974848652041497206752313461872, 30198337259834952114045673142150509214022566212693868468740722556827195886413)

$ x="2152181765955508802144811366391548358206596269149408838626058763387933371482"; y="11578758651574146923540708489424215527254544219548915855368449621649032022464"
$ ./ec.out "$a" "$b" "$p" "$x" "$y" "14923829837058818803507170043209691418601607100463140519943405743260"

  1*(y^2) = x^3 + 486662*(x^2) + x (mod 57896044618658097711785492504343953926634992332820282019728792003956564819949)
    14923829837058818803507170043209691418601607100463140519943405743260
      * P(x, y) = (2152181765955508802144811366391548358206596269149408838626058763387933371482, 11578758651574146923540708489424215527254544219548915855368449621649032022464)

      = Q(x, y) = (5796465362698668629448044005371479448781495853974848652041497206752313461872, 30198337259834952114045673142150509214022566212693868468740722556827195886413)

$ 
#include "bn.c"

struct ecurve {
	bnum *a, *b, *p, *x, *y;
};

#define ecc struct ecurve

struct ectemp {
	bnum *i, *s, *xr, *yr;
	bnum *t, *u, *v;
	bnum *w, *h, *g;
};

#define ect struct ectemp

ecc *ecinit(bnum *a, bnum *b, bnum *p, bnum *x, bnum *y)
{
	ecc *r = malloc(1 * sizeof(ecc));
	r->a = a; r->b = b; r->p = p;
	r->x = x; r->y = y;
	return r;
}

ecc *ecdup(ecc *e)
{
	ecc *r = malloc(1 * sizeof(ecc));
	r->a = bndup(e->a); r->b = bndup(e->b); r->p = bndup(e->p);
	r->x = bndup(e->x); r->y = bndup(e->y);
	return r;
}

void ecfree(ecc *e)
{
	bnfree(e->a); bnfree(e->b); bnfree(e->p);
	bnfree(e->x); bnfree(e->y);
	free(e);
}

void ecout(int d, char *s, ecc *e, char *t)
{
	char *a = bnstr(e->a), *b = bnstr(e->b), *p = bnstr(e->p);
	char *x = bnstr(e->x), *y = bnstr(e->y);
	char as[2], bs[2];
	as[0] = '+'; as[1] = '\0';
	bs[0] = '\0'; bs[1] = '\0';
	if ((e->a)->sign == 1) { as[0] = '-'; }
	if ((e->b)->sign == 1) { bs[0] = '-'; }
	if (d == 1) { printf("  %s%s*(y^2) = x^3 %s %s*(x^2) + x (mod %s)\n", bs, b, as, a, p); }
	printf("%s", s);
	printf("(x, y) = (%s, %s)", x, y);
	printf("%s", t);
	free(a); free(b); free(p);
	free(x); free(y);
}

ect *etinit(ecc *e)
{
	ect *t = malloc(1 * sizeof(ect));
	int ss = max(1, (e->b)->size);
	ss = max(((e->a)->size * 2) + 2, ((e->p)->size * 2) + 2);
	ss = max(((e->x)->size * 2) + 2, ((e->y)->size * 2) + 2);
	t->i = bninit(ss); t->s = bninit(ss); t->xr = bninit(ss); t->yr = bninit(ss);
	t->t = bninit(ss); t->u = bninit(ss); t->v = bninit(ss);
	int tt = ((ss * 2) + 2);
	t->w = bninit(tt); t->h = bninit(tt); t->g = bninit(tt + 4);
	return t;
}

void etfree(ect *t)
{
	bnfree(t->i); bnfree(t->s); bnfree(t->xr); bnfree(t->yr);
	bnfree(t->t); bnfree(t->u); bnfree(t->v);
	bnfree(t->w); bnfree(t->h); bnfree(t->g);
	free(t);
}

// modular multiplicative inverse

void egcd(bnum *a, bnum *b, bnum *g)
{
	int size = ((a->size + b->size) * 3);
	// s = 0; news = 1
	bnum *s = bninit(size);
	bnum *news = bninit(size); news->nums[0] = 1;
	// r = b; newr = a
	bnum *r = bninit(size); bncopy(b, r);
	bnum *newr = bninit(size); bncopy(a, newr);
	// init some temp vars
	bnum *prev = bninit(size), *quot = bninit(size), *temp = bninit(size);
	while ((r->leng > 1) || (r->nums[0] > 0))
	{
		// quot = (newr / r)
		if ((r->leng == 1) && (r->nums[0] < 3))
		{
			bncopy(newr, quot);
			if (r->nums[0] > 1) { bnrshift(quot, 1); }
		}
		else { bndiv(newr, r, quot, temp); }
		// prev = s
		bncopy(s, prev);
		// s = (news - (quot * prev))
		bnzero(temp); bnmul(quot, prev, temp);
		bnsub(news, temp, s, 0);
		// news = prev
		bncopy(prev, news);
		// prev = r
		bncopy(r, prev);
		// r = (newr - (quot * prev))
		bnzero(temp); bnmul(quot, prev, temp);
		bnsub(newr, temp, r, 0);
		// newr = prev
		bncopy(prev, newr);
	}
	if (news->sign == 1)
	{
		// news = news + b
		bnadd(news, b, news, 0);
	}
	bncopy(news, g);
	bnfree(s); bnfree(news);
	bnfree(r); bnfree(newr);
	bnfree(prev); bnfree(quot); bnfree(temp);
}

// modular square root

int sqrtmod(bnum *a, bnum *p, bnum *r)
{
	bnum *o = bninit(1);
	o->nums[0] = 1; o->leng = 1; o->sign = 0;
	
	// legendre symbol
	// define if a is a quadratic residue modulo odd prime
	
	// g = (p - 1) / 2
	
	// p - 1
	bnum *qq = bndup(p);
	bnsub(qq, o, qq, 1);
	// (p - 1) / 2
	bnum *g = bndup(qq);
	bnrshift(g, 1);
	
	// l = pow(a, g, p)
	
	// pow(a, g, p)
	bnum *l = bninit(max(max(a->size, g->size), p->size) * 3);
	bnpowmod(a, g, p, l);
	if (bncmp(l, qq) == 0)
	{
		bnfree(o); bnfree(qq); bnfree(g); bnfree(l);
		return -1;
	}
	
	// factor p - 1 on the form q * (2 ^ s) (with Q odd)
	// q = p - 1; s = 0
	bnum *q = bndup(qq);
	bnum *s = bninit(p->size);
	while ((q->nums[0] % 2) == 0)
	{
		// s += 1; q /= 2
		bnadd(s, o, s, 1);
		bnrshift(q, 1);
	}
	
	// select a z which is a quadratic non resudue modulo p
	// z = 1
	bnum *z = bninit(p->size);
	z->nums[0] = 1; z->leng = 1; z->sign = 0;
	while (1)
	{
		// while (lsym(z, p) != -1)
		bnpowmod(z, g, p, l);
		if (bncmp(l, qq) == 0) { break; }
		// z += 1
		bnadd(z, o, z, 1);
	}
	// c = pow(z, q, p)
	bnum *c = bninit(max(max(z->size, q->size), p->size) * 3);
	bnpowmod(z, q, p, c);
	
	// search for a solution
	// f = ((q + 1) / 2)
	bnum *f = bndup(q);
	bnadd(f, o, f, 1); bnrshift(f, 1);
	// x = pow(a, f, p)
	bnpowmod(a, f, p, r);
	// t = pow(a, q, p)
	bnum *t = bninit(max(max(a->size, q->size), p->size) * 3);
	bnpowmod(a, q, p, t);
	// m = s
	bnum *m = bninit(p->size), *i = bninit(p->size), *e = bninit(p->size);
	bncopy(s, m);
	// u = 2
	bnum *u = bninit(1);
	u->nums[0] = 2; u->leng = 1; u->sign = 0;
	bnum *b = bninit(p->size * 4), *v = bninit(p->size * 4), *w = bninit(p->size * 4);
	while ((t->leng > 1) || (t->nums[0] != 1))
	{
		// find the lowest i such that t ^ (2 ^ i) = 1
		// i = 1; e = 2
		i->nums[0] = 1; i->leng = 1; i->sign = 0;
		e->nums[0] = 2; e->leng = 1; e->sign = 0;
		while (bncmp(i, m) < 0)
		{
			bnpowmod(t, e, p, l);
			if ((l->leng == 1) && (l->nums[0] == 1)) { break; }
			bnlshift(e, 1);
			bnadd(i, o, i, 1);
		}
		// update next value to iterate
		// (m - i - 1)
		bnsub(m, i, v, 0);
		bnsub(v, o, v, 0);
		// 2 ^ (m - i - 1)
		bnpowmod(u, v, p, l);
		// b = (c ^ (2 ^ (m - i - 1))) % p
		bnpowmod(c, l, p, b);
		// x = ((x * b) % p)
		bnzero(v); bnmul(r, b, v);
		bndiv(v, p, w, r);
		// b = (b * b) % p
		bnzero(v); bnmul(b, b, v);
		bndiv(v, p, w, b);
		// t = ((t * b) % p)
		bnzero(v); bnmul(t, b, v);
		bndiv(v, p, w, t);
		// c = b; m = i
		bncopy(b, c);
		bncopy(i, m);
	}
	
	bnfree(o); bnfree(qq); bnfree(g); bnfree(l);
	bnfree(q); bnfree(s); bnfree(z); bnfree(c);
	bnfree(f); bnfree(t); bnfree(m);
	bnfree(i); bnfree(e); bnfree(b);
	bnfree(u); bnfree(v); bnfree(w);
	
	// r = [x, p - x]
	
	return 0;
}

// montgomery curve arithmetic

void nmod(bnum *a, bnum *b)
{
	if (a->sign == 1) { bnadd(b, a, a, 0); }
}

void pdub(ecc *p, ecc *r, ect *t)
{
	// printf("2P=\n");
	
	// l = 3*x^2 + 2*a*x + 1 / 2*b*y
	
	// x^2
	bnzero(t->w); bnmul(p->x, p->x, t->w); bndiv(t->w, p->p, t->t, t->v);
	// 3*x^2
	bnadd(t->v, t->v, t->w, 1); bnadd(t->w, t->v, t->w, 1);
	// 2*a*x
	bnzero(t->h); bnmul(p->a, p->x, t->h); bnlshift(t->h, 1);
	bndiv(t->h, p->p, t->t, t->u); nmod(t->u, p->p);
	// 3*x^2 + 2*a*x + 1
	bnadd(t->w, t->u, t->g, 1);
	int x, o = 1;
	for (x = 0; (x < (t->g)->leng) && (o == 1); ++x)
	{
		o = 0; if ((t->g)->nums[x] == 0xffffffff) { o = 1; } (t->g)->nums[x] += 1;
	}
	if (o == 1) { (t->g)->nums[x] = 1; (t->g)->leng += 1; }
	bndiv(t->g, p->p, t->t, t->yr);
	// 1 / 2*b*y
	bnzero(t->w); bnmul(p->b, p->y, t->w); bnlshift(t->w, 1);
	bndiv(t->w, p->p, t->t, t->u); nmod(t->u, p->p); egcd(t->u, p->p, t->i);
	// 3*x^2 + 2*a*x + 1 / 2*b*y
	bnzero(t->w); bnmul(t->yr, t->i, t->w); bndiv(t->w, p->p, t->t, t->s);
	
	// xr = b*l^2 - a - 2*x
	
	// l^2
	bnzero(t->g); bnmul(t->s, t->s, t->g);
	// b*l^2 - a
	bnzero(t->w); bnmul(p->b, t->g, t->w);
	bnsub(t->w, p->a, t->w, 0);
	// 2*x
	bnzero(t->h); bncopy(p->x, t->h); bnlshift(t->h, 1);
	// b*l^2 - a - 2*x
	bnsub(t->w, t->h, t->w, 0);
	bndiv(t->w, p->p, t->t, t->xr); nmod(t->xr, p->p);
	
	// yr = ((3*x + a) * l) - b*l^3 - y
	
	// (3*x + a) * l
	bnadd(t->h, p->x, t->w, 1); bnadd(t->w, p->a, t->w, 0);
	bndiv(t->w, p->p, t->t, t->u); nmod(t->u, p->p);
	bnzero(t->w); bnmul(t->u, t->s, t->w);
	// l^3
	bncopy(t->g, t->h);
	bnzero(t->g); bnmul(t->h, t->s, t->g); bndiv(t->g, p->p, t->t, t->u);
	// b*l^3
	bnzero(t->h); bnmul(p->b, t->u, t->h);
	bndiv(t->h, p->p, t->t, t->u); nmod(t->u, p->p);
	// ((3*x + a) * l) - b*l^3 - y
	bnsub(t->w, t->u, t->w, 0); bnsub(t->w, p->y, t->w, 0);
	bndiv(t->w, p->p, t->t, t->yr); nmod(t->yr, p->p);
	
	(t->xr)->leng = (p->p)->size; bncopy(t->xr, r->x);
	(t->yr)->leng = (p->p)->size; bncopy(t->yr, r->y);
}

void padd(ecc *p, ecc *q, ecc *r, ect *t)
{
	// printf("P+Q=\n");
	
	// l = (Qy - Py) / (Qx - Px)
	
	// Qy - Py
	bnsub(q->y, p->y, t->yr, 0);
	// Qx - Px
	bnsub(q->x, p->x, t->xr, 0);
	bndiv(t->xr, p->p, t->t, t->u); nmod(t->u, p->p);
	// 1 / (Qx - Px)
	egcd(t->u, p->p, t->i);
	// (Qy - Py) / (Qx - Px)
	bnzero(t->w); bnmul(t->yr, t->i, t->w);
	bndiv(t->w, p->p, t->t, t->s);
	
	// xr = b*l^2 - a - Px - Qx
	
	// b*l^2 - a - Px - Qx
	bnzero(t->w); bnmul(t->s, t->s, t->w);
	bnzero(t->g); bnmul(p->b, t->w, t->g);
	bnsub(t->g, p->a, t->g, 0);
	bnsub(t->g, p->x, t->g, 0);
	bnsub(t->g, q->x, t->g, 0);
	bndiv(t->g, p->p, t->t, t->xr); nmod(t->xr, p->p);
	
	// yr = ((2*Px + Qx + a) * l) - b*l^3 - Py
	
	// 2*Px + Qx + a
	bnadd(p->x, p->x, t->t, 1);
	bnadd(t->t, q->x, t->t, 1);
	bnadd(t->t, p->a, t->t, 0);
	bndiv(t->t, p->p, t->u, t->v); nmod(t->v, p->p);
	// (2*Px + Qx + a) * l
	bnzero(t->w); bnmul(t->v, t->s, t->w); bndiv(t->w, p->p, t->t, t->u);
	// b*l^3
	bnzero(t->w); bnmul(t->s, t->s, t->w);
	bnzero(t->g); bnmul(t->w, t->s, t->g); bndiv(t->g, p->p, t->t, t->v);
	bnzero(t->w); bnmul(t->v, p->b, t->w);
	// ((2*Px + Qx + a) * l) - b*l^3 - Py
	bnsub(t->u, t->w, t->v, 0);
	bnsub(t->v, p->y, t->v, 0);
	bndiv(t->v, p->p, t->t, t->yr); nmod(t->yr, p->p);
	
	(t->xr)->leng = (p->p)->size; bncopy(t->xr, r->x);
	(t->yr)->leng = (p->p)->size; bncopy(t->yr, r->y);
}

void pmul(bnum *m, ecc *p, ecc *r)
{
	int init = 0;
	bnum *mul = bndup(m);
	ecc *b = ecdup(p);
	ect *t = etinit(p);
	while ((mul->leng > 1) || (mul->nums[0] > 0))
	{
		if ((mul->nums[0] % 2) == 1)
		{
			if (init == 0)
			{
				bnfree(r->x); r->x = bndup(b->x);
				bnfree(r->y); r->y = bndup(b->y);
			}
			else
			{
				padd(r, b, r, t);
			}
			init = 1;
		}
		pdub(b, b, t);
		bnrshift(mul, 1);
	}
	bnfree(mul);
	ecfree(b);
	etfree(t);
}

Curve25519 ECDH (Using bn.c)

bn.c – A Basic Big Number/Integer Calculator

$ time ./bn.out
35052111338673026690212423937053328511880760811579981620642802346685810623109850235943049080973386241113784040794704193978215378499765413083646438784740952306932534945195080183861574225226218879827232453912820596886440377536082465681750074417459151485407445862511023472235560823053497791518928820272257787786
^
89489425009274444368228545921773093919669586065884257445497854456487674839629818390934941973262879616797970608917283679875499331574161113854088813275488110588247193077582527278437906504015680623423550067240042466665654232383502922215493623289472138866445818789127946123407807725702626644091036502372545139713
%
145906768007583323230186939349070635292401872375357164399581871019873438799005358938369571402670149802121818086292467422828157022922076746906543401224889672472407926969987100581290103199317858753663710862357656510507883714297115637342788911463535102712032765166518411726859837988672111837205085526346618740053
=
1976620216402300889624482718775150

real	0m0.783s
user	0m0.774s
sys	0m0.005s
struct bigint {
	int sign, leng, size;
	unsigned int *nums;
};

#define bnum struct bigint

int min(int a, int b)
{
	if (a < b) { return a; }
	return b;
}

int max(int a, int b)
{
	if (a > b) { return a; }
	return b;
}

int oshift(char **a, int b, char c)
{
	// right shift string a ("234") and insert overflow char c ('1')
	int x, n = (b + 1);
	char *s = *a;
	s = realloc(s, n * sizeof(char));
	for (x = (n - 2); x > 0; --x) { s[x] = s[x - 1]; }
	s[0] = c; s[n - 1] = '\0';
	*a = s;
	return n;
}

char *bnstr(bnum *bint)
{
	int x, y, w, z, o, n;
	int r = 2, t = 2;
	char *result = malloc(r * sizeof(char));
	char *twos = malloc(t * sizeof(char));
	// result = 0
	result[0] = '0'; result[1] = '\0';
	// twos = 1
	twos[0] = '1'; twos[1] = '\0';
	// loop through the bn binary
	for (x = 0; x < bint->leng; ++x)
	{
		for (y = 0; y < 32; ++y)
		{
			// if bn binary[n] == 1 then add the 2^n to result
			if (((bint->nums[x] >> y) & 0x1) == 1)
			{
				o = 0; w = (t - 2); z = (r - 2);
				// if the length of twos is bigger than length of result then realloc result
				if (t > r)
				{
					result = realloc(result, t * sizeof(char));
					result[t - 1] = '\0';
					r = t;
				}
				// add twos to result
				while ((w > -1) || (z > -1))
				{
					int a = 0; if (w > -1) { a = (twos[w] - '0'); }
					int b = 0; if (z > -1) { b = (result[z] - '0'); }
					n = (a + b + o);
					result[w] = ('0' + (n % 10));
					o = (n / 10);
					--w; --z;
				}
				if (o > 0) { r = oshift(&result, r, '0' + o); }
			}
			// multiply twos * 2
			o = 0; w = (t - 2);
			while (w > -1)
			{
				n = (o + ((twos[w] - '0') * 2));
				twos[w] = ('0' + (n % 10));
				o = (n / 10);
				--w;
			}
			if (o > 0) { t = oshift(&twos, t, '0' + o); }
		}
	}
	free(twos);
	return result;
}

void bnout(char *s, bnum *p, char *t)
{
	char *o = bnstr(p);
	printf("%s[%d][%d][%d]=[%s]%s", s, p->sign, p->leng, p->size, o, t);
	free(o);
}

void bnzero(bnum *a)
{
	a->sign = 0;
	bzero(a->nums, a->size * sizeof(unsigned int));
	a->leng = 1;
}

void bncopy(bnum *src, bnum *dst)
{
	dst->sign = src->sign;
	bcopy(src->nums, dst->nums, src->leng * sizeof(unsigned int));
	dst->leng = src->leng;
}

void bnfree(bnum *bint)
{
	//todo:zero out all values
	free(bint->nums);
	free(bint);
}

void bndfree(bnum **bint)
{
	bnfree(bint[0]);
	bnfree(bint[1]);
	free(bint);
}

int bncmp(bnum *a, bnum *b)
{
	int x = (a->leng - 1), y = (b->leng - 1);
	while ((x > -1) && (a->nums[x] < 1)) { --x; }
	while ((y > -1) && (b->nums[y] < 1)) { --y; }
	if (x > y) { return 1; }
	if (y > x) { return -1; }
	while (x > -1)
	{
		if (a->nums[x] > b->nums[x]) { return 1; }
		if (b->nums[x] > a->nums[x]) { return -1; }
		--x;
	}
	return 0;
}

int bnhigh(bnum *a)
{
	int x, y, f = 0, hib = -1;
	for (x = (a->leng - 1); (x > -1) && (f == 0); --x)
	{
		for (y = 31; (y > -1) && (f == 0); --y)
		{
			if (((a->nums[x] >> y) & 0x1) == 1)
			{
				hib = ((x * 32) + y);
				f = 1;
			}
		}
	}
	return hib;
}

void bnrshift(bnum *a, int s)
{
	int x, leng = 0, i = (s / 32);
	s = (s % 32);
	for (x = 0; x < (a->leng - i); ++x)
	{
		if (i > 0)
		{
			a->nums[x] = a->nums[x + i];
		}
		if (s > 0)
		{
			if (x > 0)
			{
				a->nums[x - 1] = ((a->nums[x] << (32 - s)) | a->nums[x - 1]);
				if (a->nums[x - 1] > 0) { leng = x; }
			}
			a->nums[x] = (a->nums[x] >> s);
		}
		if (a->nums[x] > 0) { leng = (x + 1); }
	}
	if (leng < 1)
	{
		a->nums[0] = 0;
		leng = 1;
	}
	a->leng = leng;
}

void bnlshift(bnum *a, int s)
{
	int x, o = 0, i = (s / 32);
	unsigned int over = (a->nums[a->leng - 1] >> (32 - (s % 32)));
	s = (s % 32);
	// check to see if the left shift will have any carry over bits
	if ((s > 0) && (over > 0)) { o = 1; }
	// pre adjust the size of the bn to hold the left shift bits
	if (a->size < (a->leng + o + i))
	{
		a->size = (a->leng + o + i);
		a->nums = realloc(a->nums, a->size * sizeof(unsigned int));
	}
	a->leng += (o + i);
	// shift the bits from left to right (last to first) minus the overflow spot
	for (x = (a->leng - o - 1); x > -1; --x)
	{
		if (i > 0)
		{
			if ((x - i) > -1) { a->nums[x] = a->nums[x - i]; }
			else { a->nums[x] = 0; }
		}
		if (s > 0)
		{
			if ((x + 1) < (a->leng - o)) { a->nums[x + 1] = (a->nums[x + 1] | (a->nums[x] >> (32 - s))); }
			a->nums[x] = (a->nums[x] << s);
		}
	}
	// if we have some overflow bits then set them last now
	if (o == 1) { a->nums[a->leng - 1] = over; }
}

bnum *bninit(int ssiz)
{
	int x;
	bnum *a = malloc(1 * sizeof(bnum));
	a->sign = 0; a->leng = 1, a->size = ssiz;
	a->nums = malloc(ssiz * sizeof(unsigned int));
	for (x = 0; x < ssiz; ++x) { a->nums[x] = 0; }
	return a;
}

bnum *bndup(bnum *a)
{
	int x;
	bnum *r = malloc(1 * sizeof(bnum));
	r->sign = a->sign; r->leng = a->leng; r->size = a->size;
	r->nums = malloc(a->size * sizeof(unsigned int));
	for (x = 0; x < a->size; ++x) { r->nums[x] = a->nums[x]; }
	return r;
}

bnum *bndec(char *decstr)
{
	int x, y, r, n;
	int i = 0;
	unsigned long m, l = strlen(decstr);
	char numstr[l + 1], outstr[l + 1];
	// result = 0
	bnum *result = bninit(1);
	// copy the input decimal string into a temp num str
	strncpy(numstr, decstr, l);
	numstr[l] = '\0';
	m = l;
	// while there are numbers left to be divided by 2
	while ((m > 1) || (numstr[0] > '0'))
	{
		// if we are shifting above 32 bits then append a new bit block
		if (i > 31)
		{
			result->leng += 1; result->size += 1;
			result->nums = realloc(result->nums, result->size * sizeof(unsigned int));
			result->nums[result->leng - 1] = 0;
			i = 0;
		}
		// divide the number by 2 and store the result in a temp string
		r = 0;
		outstr[0] = '\0';
		for (x = 0, y = 0; (x < m) && (y < l); ++x, ++y)
		{
			// get the last remainder and the current digit
			n = ((r * 10) + (numstr[x] - '0'));
			// if the digit num is less than the divider 2 then add on the next digit
			if ((n < 2) && ((x + 1) < m) && ((y + 1) < l))
			{
				n = ((n * 10) + (numstr[x + 1] - '0'));
				if (y > 0) { outstr[y] = '0'; ++y; }
				++x;
			}
			// store the division and the remainder
			outstr[y] = ('0' + (n / 2));
			outstr[y + 1] = '\0';
			r = (n % 2);
		}
		// save the binary result
		result->nums[result->leng - 1] = ((r << i) | result->nums[result->leng - 1]);
		++i;
		// copy the divided result into the temp num str
		strncpy(numstr, outstr, l);
		numstr[y] = '\0';
		m = strlen(numstr);
	}
	return result;
}

int bnsub(bnum *, bnum *, bnum *, int);

int bnadd(bnum *a, bnum *b, bnum *r, int s)
{
	int x = 0, y = 0, z = 0, g = 0;
	unsigned int over = 0;
	if (s == 0)
	{
		if ((a->sign == 0) && (b->sign == 1))
		{
			b->sign = 0;
			bnsub(a, b, r, 0); g = r->sign;
			b->sign = 1;
			r->sign = g;
			return 0;
		}
		else if ((a->sign == 1) && (b->sign == 0))
		{
			a->sign = 0;
			bnsub(b, a, r, 0); g = r->sign;
			a->sign = 1;
			r->sign = g;
			return 0;
		}
	}
	while ((x < a->leng) || (y < b->leng))
	{
		unsigned int c = 0; if (x < a->leng) { c = a->nums[x]; }
		unsigned int d = 0; if (y < b->leng) { d = b->nums[y]; }
		unsigned int lo = ((c & 0xffff) + (d & 0xffff) + (over & 0xffff));
		unsigned int hi = (((c >> 16) & 0xffff) + ((d >> 16) & 0xffff) + ((over >> 16) & 0xffff) + ((lo >> 16) & 0xffff));
		over = ((hi >> 16) & 0xffff);
		r->nums[z] = (((hi << 16) & 0xffff0000) | (lo & 0xffff));
		++x; ++y; ++z;
	}
	if (over > 0) { r->nums[z] = over; ++z; }
	r->sign = (a->sign | b->sign);
	r->leng = z;
	return 1;
}

int bnsub(bnum *a, bnum *b, bnum *r, int s)
{
	int x = 0, y = 0;
	int n = 1, g = 0;
	bnum *t = a, *m = b, *temp;
	if (s == 0)
	{
		if ((a->sign == 0) && (b->sign == 1))
		{
			b->sign = 0;
			bnadd(a, b, r, 0); g = r->sign;
			b->sign = 1;
			r->sign = g;
			return 0;
		}
		else if ((a->sign == 1) && (b->sign == 0))
		{
			a->sign = 0;
			bnadd(a, b, r, 0);
			a->sign = 1;
			r->sign = 1;
			return 0;
		}
		else if ((a->sign == 1) && (b->sign == 1))
		{
			t = b; m = a;
		}
		s = bncmp(t, m);
		if (s < 0)
		{
			g = 1;
			temp = t;
			t = m;
			m = temp;
		}
		else if (s == 0)
		{
			r->sign = 0; r->leng = 1;
			r->nums[0] = 0;
			return 2;
		}
	}
	int indx = -1;
	unsigned int over = 0;
	while ((x < t->leng) || (y < m->leng))
	{
		unsigned int c = 0; if (x < t->leng) { c = t->nums[x]; }
		unsigned int d = 0; if (y < m->leng) { d = m->nums[y]; }
		if (x == indx) { c = over; indx = -1; }
		if (c < d)
		{
			int i = x, z;
			for (z = (x + 1); z < t->leng; ++z)
			{
				if (t->nums[z] > 0)
				{
					unsigned int hic = ((1 << 16) + (c >> 16)), loc = (c & 0xffff);
					unsigned int hid = (d >> 16)              , lod = (d & 0xffff);
					if (loc < lod) { hic -= 1; loc += (1 << 16); }
					r->nums[i] = (((hic - hid) << 16) + (loc - lod));
					indx = z; over = (t->nums[z] - 1);
					if (r->nums[i] > 0) { n = max(n, (i + 1)); }
					break;
				}
				++x; ++y;
				if (y < m->leng) { r->nums[z] = (0xffffffff - m->nums[y]); }
				else { r->nums[z] = 0xffffffff; }
				if (r->nums[z] > 0) { n = max(n, (z + 1)); }
			}
		}
		else { r->nums[x] = (c - d); }
		if (r->nums[x] > 0) { n = max(n, (x + 1)); }
		++x; ++y;
	}
	r->sign = g;
	r->leng = n;
	return 1;
}

void bnmul(bnum *a, bnum *b, bnum *r)
{
	int x, y;
	bnum *dub = bninit(r->size);
	bncopy(b, dub);
	for (x = 0; x < a->leng; ++x)
	{
		for (y = 0; y < 32; ++y)
		{
			if (((a->nums[x] >> y) & 0x1) == 1)
			{
				bnadd(r, dub, r, 1);
			}
			bnadd(dub, dub, dub, 1);
		}
	}
	r->sign = (a->sign ^ b->sign);
	bnfree(dub);
}

void bndiv(bnum *a, bnum *b, bnum *r, bnum *m)
{
	int s = 0, d = 0, c = bncmp(a, b);
	bnum *t = bninit(max(a->size, b->size) * 2);
	bncopy(a, t);
	r->nums[0] = 0; r->leng = 1;
	m->nums[0] = 0; m->leng = 1;
	if (c < 0)
	{
		//printf("return <\n");
		r->nums[0] = 0; r->leng = 1;
		bncopy(a, m);
	}
	else if (c == 0)
	{
		//printf("return ==\n");
		r->nums[0] = 1; r->leng = 1;
		m->nums[0] = 0; m->leng = 1;
	}
	else if ((b->leng == 1) && (b->nums[0] == 0))
	{
		//printf("return /0!\n");
	}
	else if (c > 0)
	{
		int hir = 0;
		
		//printf("> right shift the number to match the divider\n");
		int hia = bnhigh(a), hib = bnhigh(b), hid = (hia - hib - 1);
		bnrshift(t, hid + 1);
		c = bncmp(t, b);
		if (c < 0) { s = 1; }
		
		while (1)
		{
			if (s == 1)
			{
				//printf("equal bit length but number < divider\n");
				int abyte = (hid / 32), abit = (hid % 32);
				int bitv = ((a->nums[abyte] & (1 << abit)) >> abit);
				bnlshift(t, 1); t->nums[0] |= bitv;
				--hid;
				if (d > 0) { bnlshift(r, 1); ++hir; }
			}
			s = 0;
			
			//printf("substract num - div and shift 1 into result\n");
			bnsub(t, b, t, 1);
			bnlshift(r, 1); r->nums[0] |= 1; ++hir;
			
			int hit = bnhigh(t);
			d = 0;
			while ((hit < hib) && (hid > -1))
			{
				int abyte = (hid / 32), abit = (hid % 32);
				int diff = min(hib - hit, 32);
				if ((hid + 1) >= diff)
				{
					//printf("large: shift another bit into the number to match the divider\n");
					hid -= (diff - 1);
					int bbyte = (hid / 32), bbit = (hid % 32);
					--hid;
					bnlshift(t, diff);
					t->nums[0] = (t->nums[0] | ((a->nums[abyte] << (31 - abit)) >> (32 - diff)));
					if (abyte != bbyte) { t->nums[0] = (t->nums[0] | (a->nums[bbyte] >> bbit)); }
					d += diff;
				}
				else
				{
					//printf("small: shift another bit into the number to match the divider\n");
					int bitv = ((a->nums[abyte] & (1 << abit)) >> abit);
					bnlshift(t, 1); t->nums[0] |= bitv;
					--hid;
					++d;
				}
				hit = bnhigh(t);
			}
			if (d > 1) { bnlshift(r, d - 1); hir += (d - 1); }
			
			//printf("check resulting number and break if done dividing else repeat\n");
			c = bncmp(t, b);
			if (c < 0)
			{
				if (hid < 0)
				{
					//printf("exiting <\n");
					if (d > 0) { bnlshift(r, 1); ++hir; }
					bncopy(t, m);
					break;
				}
				s = 1;
			}
			else if (c == 0)
			{
				if (hid < 0)
				{
					//printf("exiting ==\n");
					bnlshift(r, 1); r->nums[0] |= 1; ++hir;
					break;
				}
			}
		}
	}
	r->sign = (a->sign ^ b->sign);
	m->sign = r->sign;
	bnfree(t);
}

void bnpowmod(bnum *b, bnum *e, bnum *m, bnum *r)
{
	int s = max(max(b->size, e->size), max(m->size, r->size));
	bnum *t = bninit(s * 3), *u = bninit(s * 3);
	bnum *base = bninit(s * 3), *exp = bninit(s * 3);
	bncopy(b, base); bncopy(e, exp);
	r->nums[0] = 1; r->leng = 1;
	while ((exp->leng > 1) || (exp->nums[0] > 0))
	{
		if ((exp->nums[0] % 2) == 1)
		{
			// r = r * base
			bnzero(t); bnmul(r, base, t);
			// r = r % m
			bndiv(t, m, u, r);
		}
		// exp = exp / 2
		bnrshift(exp, 1);
		// b = b * b
		bnzero(t); bnmul(base, base, t);
		// b = b % m
		bndiv(t, m, u, base);
	}
	// free the rest
	bnfree(base); bnfree(exp);
	bnfree(t); bnfree(u);
}

bn.c – A Basic Big Number/Integer Calculator

Helping people study – my recurring job in life (Python, Classes, LinkedLists, QuickSort, BinarySearch)

$ python study.py 
Unsorted:[
  {'last': 'Chiappetta', 'user': 'johnny', 'key': 'abcxyz', 'first': 'Jonathan'}
  {'last': 'Dada', 'user': 'zdada', 'key': 'iowa', 'first': 'Zain'}
  {'last': 'Xray', 'user': 'yx', 'key': 'xy', 'first': 'Yvan'}
  {'last': 'Bob', 'user': 'abob', 'key': 'zyxcba', 'first': 'Alex'}
  {'last': 'Bob', 'user': 'bchar', 'key': 'hmmm', 'first': 'Charlie'}
]
Sorted:[
  {'last': 'Bob', 'user': 'abob', 'key': 'zyxcba', 'first': 'Alex'}
  {'last': 'Bob', 'user': 'bchar', 'key': 'hmmm', 'first': 'Charlie'}
  {'last': 'Chiappetta', 'user': 'johnny', 'key': 'abcxyz', 'first': 'Jonathan'}
  {'last': 'Xray', 'user': 'yx', 'key': 'xy', 'first': 'Yvan'}
  {'last': 'Dada', 'user': 'zdada', 'key': 'iowa', 'first': 'Zain'}
]
('Search=Dave:', None)
('Search=Jon:', {'last': 'Chiappetta', 'user': 'johnny', 'key': 'abcxyz', 'first': 'Jonathan'})
('Search=J Chi:', {'last': 'Chiappetta', 'user': 'johnny', 'key': 'abcxyz', 'first': 'Jonathan'})
('Search=J Chip:', None)
class contact:
	def __init__(self, firstName, lastName, username, pubKey):
		self.data = {"first":firstName, "last":lastName, "user":username, "key":pubKey}
		self.next = None
	
	def getData(self):
		return self.data
	
	def setData(self, dataContact):
		self.data = dataContact
	
	def getNext(self):
		return self.next
	
	def setNext(self, nextContact):
		self.next = nextContact

class contactList:
	def __init__(self):
		self.head = None
	
	def append(self, contact):
		if (self.head == None):
			self.head = contact
		else:
			temp = self.head
			while (temp.getNext() != None):
				temp = temp.getNext()
			temp.setNext(contact)
	
	def get(self):
		contacts = ""
		temp = self.head
		while (temp != None):
			contacts += ("  " + str(temp.getData()) + "\n")
			temp = temp.getNext()
		return ("[\n" + contacts + "]")
	
	def leng(self):
		indx = 0
		temp = self.head
		while (temp != None):
			indx += 1
			temp = temp.getNext()
		return indx
	
	def getin(self, gind):
		indx = 0
		temp = self.head
		while (temp != None):
			if (indx == gind):
				return temp
			temp = temp.getNext()
			indx += 1
		return None
	
	def swap(self, a, b):
		ac = self.getin(a)
		bc = self.getin(b)
		temp = ac.getData()
		ac.setData(bc.getData())
		bc.setData(temp)
	
	def sort(self, beg=-1, end=-1):
		if (beg < 0):
			beg = 0
		if (end < 0):
			end = self.leng()
		if ((end - beg) < 2):
			return 0
		# todo: use head && next instead of index lookup
		pivot = beg
		p = self.getin(pivot)
		z = p.getData()
		indx = (pivot + 1)
		hi = -1
		while (indx < end):
			a = self.getin(indx)
			b = a.getData()
			if ((b["first"] + b["last"]) < (z["first"] + z["last"])):
				if (hi > -1):
					self.swap(hi, indx)
					hi += 1
			else:
				if (hi < 0):
					hi = indx
			indx += 1
		if (hi < 0):
			hi = (end - 1)
		elif (hi > 0):
			hi = (hi - 1)
		self.swap(pivot, hi)
		self.sort(beg, hi)
		self.sort(hi + 1, end)
	
	def search(self, fullname, beg=-1, end=-1):
		if (beg < 0):
			beg = 0
		if (end < 0):
			end = self.leng()
		if ((end - beg) < 1):
			return None
		fulllist = fullname.split(" ")
		fulllist.append("")
		first = fulllist[0]; flen = len(first)
		last = fulllist[1]; llen = len(last)
		mid = (((end - beg) / 2) + beg)
		mc = self.getin(mid)
		md = mc.getData()
		fname = md["first"][:flen]
		lname = md["last"][:llen]
		if ((fname == first) and (lname == last)):
			return mc
		if ((fname > first) or (lname > last)):
			end = (mid - 1)
			mid = -1
		elif ((fname < first) or (lname < last)):
			beg = (mid + 1)
			mid = -1
		if (mid != -1):
			return None
		return self.search(fullname, beg=beg, end=end)

def main():
	contactBook = contactList()
	
	newContact = contact("Jonathan", "Chiappetta", "johnny", "abcxyz")
	contactBook.append(newContact)
	
	newContact = contact("Zain", "Dada", "zdada", "iowa")
	contactBook.append(newContact)
	
	newContact = contact("Yvan", "Xray", "yx", "xy")
	contactBook.append(newContact)
	
	newContact = contact("Alex", "Bob", "abob", "zyxcba")
	contactBook.append(newContact)
	
	newContact = contact("Charlie", "Bob", "bchar", "hmmm")
	contactBook.append(newContact)
	
	print("Unsorted:" + contactBook.get())
	
	contactBook.sort()
	print("Sorted:" + contactBook.get())
	
	print("Search=Dave:", contactBook.search("Charlie"))
	print("Search=Jon:", contactBook.search("Jon").getData())
	print("Search=J Chi:", contactBook.search("J Chi").getData())
	print("Search=J Chip:", contactBook.search("J Chip"))

if (__name__ == "__main__"):
	main()

Helping people study – my recurring job in life (Python, Classes, LinkedLists, QuickSort, BinarySearch)

AES 256 CTR Mode Hash Function

Proof of concept, do not use, just for theory!

With the recent news of SHA1 everybody should be switching to SHA2 or SHA3 by now. I tried turning AES 256 in CTR mode into a 256 bit hash function by XORing the encrypted outputs together. For example, splitting your message into 32 byte blocks and using it as the keys (m0, m1, …, mN):

hash(m) = [AES256(nonce || counter0, m0) || AES256(nonce || counter1, m0)]
XOR [AES256(nonce || counter2, m1) || AES256(nonce || counter3, m1)]
...
XOR [AES256(nonce || counterN, mN) || AES256(nonce || counterO, mN)]
('test', '8ea2b7ca516745bfeafc49904b496089')
-
('', '09975b45dc8ecebd1519328dbec1c54d66b7fa7a2a4b762368497f81d864819b')
('a', 'c458ace20dc6458d6e589dbf0e560cbcad5b482e5934a86b6e98202a6cd5a6d5')
('abc', '304dfabb945406b40d53160080d078a1a0e5f8330263f578725ecfbccbb2c0ad')
('cba', '76cdfb5e58e06b5bf191656986aa35e405d3f582f307ea3847fe2a48136bf34e')
('the quick brown fox jumps over the lazy dog', '205639b3097f25a1c3a7bee4a3a66c4c3786129dd92c57fdddafc81568ab66f5')
('the quick brown fox jumps over the lazy eog', '3a7a025b5eb99f93caffd09331786cf6a15e1cb5bf41ae68342ea6b2208cf539')
import sys

def subbytes(matrix):
	sbox = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
		0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
		0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
		0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
		0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
		0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
		0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
		0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
		0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
		0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
		0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
		0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
		0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
		0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
		0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
		0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
	]
	for i in range(0, len(matrix)):
		matrix[i] = sbox[matrix[i]]
	return matrix

def transform(matrix):
	t = []
	y = 0
	for x in range(0, 16):
		z = (x % 4)
		if ((x > 0) and (z == 0)):
			y += 1
		t.append(matrix[(z * 4) + y])
	return t

def shiftrows(matrix):
	val = matrix.pop(4)
	matrix.insert(7, val)
	# end row 1
	val = matrix.pop(8)
	matrix.insert(11, val)
	val = matrix.pop(8)
	matrix.insert(11, val)
	# end row 2
	val = matrix.pop(15)
	matrix.insert(12, val)
	# end row 3
	return matrix

def gmul(a, b):
	p = 0
	for c in range(0, 8):
		if ((b & 1) != 0):
			p = (p ^ a)
		hi_bit_set = (a & 0x80)
		a = ((a << 1) & 0xff)
		if (hi_bit_set != 0):
			a = (a ^ 0x1b)
		b = (b >> 1)
	return p

def mixcols(s):
	t = []
	for c in range(0, 16):
		t.append(0)
	for c in range(0, 4):
		t[c +  0] = (gmul(0x02, s[c + 0]) ^ gmul(0x03, s[c + 4]) ^ s[c + 8] ^ s[c + 12]);
		t[c +  4] = (s[c + 0] ^ gmul(0x02, s[c + 4]) ^ gmul(0x03, s[c + 8]) ^ s[c + 12]);
		t[c +  8] = (s[c + 0] ^ s[c + 4] ^ gmul(0x02, s[c + 8]) ^ gmul(0x03, s[c + 12]));
		t[c + 12] = (gmul(0x03, s[c + 0]) ^ s[c + 4] ^ s[c + 8] ^ gmul(0x02, s[c + 12]));
	return t

def addkey(matrix, keyval):
	for i in range(0, 16):
		matrix[i] = (matrix[i] ^ keyval[i])
	return matrix

def rcon(ind):
	c = 1
	if (ind == 0):
		return 0
	while (ind != 1):
		c = gmul(c, 2)
		ind -= 1
	return c

def keycore(subkey, i):
	val = subkey.pop(0)
	subkey.append(val)
	subkey = subbytes(subkey)
	subkey[0] = (subkey[0] ^ rcon(i))
	return subkey

def keyexp(matrix):
	c = 32
	i = 1
	t = [0, 0, 0, 0]
	while (c < 240):
		for a in range(0, 4):
			t[a] = matrix[a + c - 4]
		if ((c % 32) == 0):
			t = keycore(t, i)
			i += 1
		if ((c % 32) == 16):
			t = subbytes(t)
		for a in range(0, 4):
			if (c >= len(matrix)):
				matrix.append(0)
			matrix[c] = (matrix[c - 32] ^ t[a])
			c += 1
	return matrix

def hexout(matrix):
	o = ""
	for d in matrix:
		h = hex(d)
		h = str(h)
		h = h[2:]
		if (len(h) < 2):
			h = ("0" + h)
		o += h
	return o

def aescoree(msg, key):
	out = []
	
	t = []
	i = 0
	l = len(key)
	for x in range(0, 32):
		t.append(0)
		if (i < l):
			t[x] = ord(key[i])
			i += 1
	keyval = keyexp(t)
	
	i = 0
	l = len(msg)
	while (i < l):
		input = []
		for x in range(0, 16):
			input.append(0)
		for x in range(0, 16):
			if (i < l):
				input[x] = ord(msg[i])
				i += 1
		
		for r in range(0, 15):
			if (r == 0):
				input = addkey(input, keyval[r*16:])
			
			if (r > 0):
				input = subbytes(input)
				input = transform(input)
				input = shiftrows(input)
				
				if (r < 14):
					input = mixcols(input)
				
				input = transform(input)
			
			if ((r > 0) and (r < 15)):
				input = addkey(input, keyval[r*16:])
		
		for o in input:
			out.append(o)
	
	return out

def aesctr_hash(message):
	x = 0
	l = len(message)
	n = 0
	h = []
	c = "aesctrhash31337!"
	d = 0
	for e in c:
		d = ((d << 8) + ord(e))
	while ((x == 0) or (x < l)):
		u = ((d + n) & 0xffffffffffffffffffffffffffffffff); n += 1
		v = ((d + n) & 0xffffffffffffffffffffffffffffffff); n += 1
		r = ""
		s = ""
		while (u > 0):
			r = (chr(u & 0xff) + r)
			u = (u >> 8)
		while (v > 0):
			s = (chr(v & 0xff) + s)
			v = (v >> 8)
		m = message[x:x+32]
		a = aescoree(r, m)
		b = aescoree(s, m)
		t = (a + b)
		if (x == 0):
			h = t
		else:
			for i in range(0, 32):
				h[i] = (h[i] ^ t[i])
		x += 32
	print(message, hexout(h))
	return h

print("test", hexout(aescoree("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f")))
print("-")
aesctr_hash("")
aesctr_hash("a")
aesctr_hash("abc")
aesctr_hash("cba")
aesctr_hash("the quick brown fox jumps over the lazy dog")
aesctr_hash("the quick brown fox jumps over the lazy eog")

AES 256 CTR Mode Hash Function

The Monty Hall Problem

I was watching MythBusters and they visited this problem which at Seneca we discussed briefly as well (I might have done this one before but thought I’d repost it anyway):

  1. There are 3 doors, 2 are losers and 1 is a winner
  2. You pick a door (2/3 chance it’s a loser)
  3. They show you the other loser door (leaving the winner door [1/3 chance] and the loser door [2/3 chance])
  4. You decide to switch your door (since it was 2/3 likely you originally chose a losing door and they took the other losing door away, switching increases your odds since the other door is likely to be the winner door)
  5. You win! (still a small chance of losing though)

$ python monty.py 0 100 1

('Doors:', {1: 'win', 2: 'lose', 3: 'lose'})
('Pick:', 1, 'win')
('Show:', 2, 'lose')
('Final:', 1, 'win')

('Doors:', {1: 'lose', 2: 'lose', 3: 'win'})
('Pick:', 2, 'lose')
('Show:', 1, 'lose')
('Final:', 2, 'lose')

('Doors:', {1: 'lose', 2: 'lose', 3: 'win'})
('Pick:', 1, 'lose')
('Show:', 2, 'lose')
('Final:', 1, 'lose')


('Wins:', 32, '/', 100, '=', 0.32)

$ python monty.py 1 100 1

('Doors:', {1: 'lose', 2: 'win', 3: 'lose'})
('Pick:', 2, 'win')
('Show:', 1, 'lose')
('Switching:', 2, 'to', 3)
('Final:', 3, 'lose')

('Doors:', {1: 'lose', 2: 'win', 3: 'lose'})
('Pick:', 3, 'lose')
('Show:', 1, 'lose')
('Switching:', 3, 'to', 2)
('Final:', 2, 'win')

('Doors:', {1: 'lose', 2: 'win', 3: 'lose'})
('Pick:', 1, 'lose')
('Show:', 3, 'lose')
('Switching:', 1, 'to', 2)
('Final:', 2, 'win')


('Wins:', 74, '/', 100, '=', 0.74)
import random
import sys
try:
	switch = int(sys.argv[1])
except:
	switch = 0
try:
	rounds = int(sys.argv[2])
except:
	rounds = 0
try:
	debug = int(sys.argv[3])
except:
	debug = 0
wins = 0
for x in range(0, rounds):
	doors = ["lose", "lose", "win"]
	random.shuffle(doors)
	doors = {1:doors[0], 2:doors[1], 3:doors[2]}
	if (debug != 0):
		print("Doors:", doors)
	pick = random.randint(1, 3)
	if (debug != 0):
		print("Pick:", pick, doors[pick])
	show = 1
	for k in doors.keys():
		if ((k != pick) and (doors[k] == "lose")):
			show = k
			break
	if (debug != 0):
		print("Show:", show, doors[show])
	if (switch != 0):
		for k in doors.keys():
			if ((k != pick) and (k != show)):
				other = k
				break
		print("Switching:", pick, "to", other)
		pick = other
	print("Final:", pick, doors[pick])
	if (doors[pick] != "lose"):
		wins += 1
	if (debug != 0):
		print("")
print("")
print("Wins:", wins, "/", rounds, "=", float(wins) / float(rounds))

The Monty Hall Problem