added MPIN Two go/cgo wrapper code
diff --git a/go/src/github.com/miracl/amcl-cgo/crypto.go b/go/src/github.com/miracl/amcl-cgo/crypto.go
index 37e9801..38ac3ad 100644
--- a/go/src/github.com/miracl/amcl-cgo/crypto.go
+++ b/go/src/github.com/miracl/amcl-cgo/crypto.go
@@ -647,3 +647,131 @@
 	}
 	fmt.Printf("\n")
 }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+func MPIN_SERVER_1_WRAP(date int, ID []byte) (HID, HTID []byte) {
+	// Form Octets
+	IDStr := string(ID)
+	IDOct := GetOctet(IDStr)
+	defer OCT_free(&IDOct)
+
+	HIDOct := GetOctetZero(G1S)
+	defer OCT_free(&HIDOct)
+	HTIDOct := GetOctetZero(G1S)
+	defer OCT_free(&HTIDOct)
+
+	C.MPIN_SERVER_1(C.int(date), &IDOct, &HIDOct, &HTIDOct)
+
+	// Convert octet to bytes
+	HID = OCT_toBytes(&HIDOct)
+	HTID = OCT_toBytes(&HTIDOct)
+
+	return
+}
+
+/* Implement step 2 of MPin protocol on server side */
+func MPIN_SERVER_2_WRAP(date int, HID []byte, HTID []byte, Y []byte, SS []byte, U []byte, UT []byte, V []byte) (errorCode int, E, F []byte) {
+	// Form Octets
+	HIDStr := string(HID)
+	HIDOct := GetOctet(HIDStr)
+	defer OCT_free(&HIDOct)
+	HTIDStr := string(HTID)
+	HTIDOct := GetOctet(HTIDStr)
+	defer OCT_free(&HTIDOct)
+	YStr := string(Y)
+	YOct := GetOctet(YStr)
+	defer OCT_free(&YOct)
+	SSStr := string(SS)
+	SSOct := GetOctet(SSStr)
+	defer OCT_free(&SSOct)
+	UStr := string(U)
+	UOct := GetOctet(UStr)
+	defer OCT_free(&UOct)
+	UTStr := string(UT)
+	UTOct := GetOctet(UTStr)
+	defer OCT_free(&UTOct)
+	VStr := string(V)
+	VOct := GetOctet(VStr)
+	defer OCT_free(&VOct)
+
+	EOct := GetOctetZero(GTS)
+	defer OCT_free(&EOct)
+	FOct := GetOctetZero(GTS)
+	defer OCT_free(&FOct)
+	rtn := C.MPIN_SERVER_2(C.int(date), &HIDOct, &HTIDOct, &YOct, &SSOct, &UOct, &UTOct, &VOct, &EOct, &FOct)
+
+	errorCode = int(rtn)
+	E = OCT_toBytes(&EOct)
+	F = OCT_toBytes(&FOct)
+
+	return
+}
+
+/* Implement step 1 on client side of MPin protocol. Use GO code to generate random X */
+func MPIN_CLIENT_1_WRAP(date int, ID []byte, RNG *amcl.RAND, X []byte, PIN int, TOKEN []byte, TP []byte) (errorCode int, XOut, SEC, U, UT []byte) {
+	amcl.MPIN_RANDOM_GENERATE(RNG, X[:])
+	errorCode, XOut, SEC, U, UT = MPIN_CLIENT_1_C(date, ID[:], nil, X[:], PIN, TOKEN[:], TP[:])
+	return
+}
+
+/* Implement step 1 on client side of MPin protocol
+   When rng=nil the X value is externally generated
+*/
+func MPIN_CLIENT_1_C(date int, ID []byte, rng *C.csprng, X []byte, PIN int, TOKEN []byte, TP []byte) (errorCode int, XOut, SEC, U, UT []byte) {
+	// Form Octets
+	IDStr := string(ID)
+	IDOct := GetOctet(IDStr)
+	defer OCT_free(&IDOct)
+
+	XStr := string(X)
+	XOct := GetOctet(XStr)
+	defer OCT_free(&XOct)
+
+	TOKENStr := string(TOKEN)
+	TOKENOct := GetOctet(TOKENStr)
+	defer OCT_free(&TOKENOct)
+
+	TPStr := string(TP)
+	TPOct := GetOctet(TPStr)
+	defer OCT_free(&TPOct)
+
+	SECOct := GetOctetZero(G1S)
+	defer OCT_free(&SECOct)
+	UOct := GetOctetZero(G1S)
+	defer OCT_free(&UOct)
+	UTOct := GetOctetZero(G1S)
+	defer OCT_free(&UTOct)
+
+	rtn := C.MPIN_CLIENT_1(C.int(date), &IDOct, rng, &XOct, C.int(PIN), &TOKENOct, &SECOct, &UOct, &UTOct, &TPOct)
+
+	errorCode = int(rtn)
+	// Convert octet to bytes
+	XOut = OCT_toBytes(&XOct)
+	SEC = OCT_toBytes(&SECOct)
+	U = OCT_toBytes(&UOct)
+	UT = OCT_toBytes(&UTOct)
+
+	return
+}
+
+/* Implement step 2 on client side of MPin protocol */
+func MPIN_CLIENT_2_WRAP(X []byte, Y []byte, SEC []byte) (errorCode int, V []byte) {
+	// Form Octets
+	XStr := string(X)
+	XOct := GetOctet(XStr)
+	defer OCT_free(&XOct)
+	YStr := string(Y)
+	YOct := GetOctet(YStr)
+	defer OCT_free(&YOct)
+	SECStr := string(SEC)
+	SECOct := GetOctet(SECStr)
+	defer OCT_free(&SECOct)
+
+	rtn := C.MPIN_CLIENT_2(&XOct, &YOct, &SECOct)
+
+	errorCode = int(rtn)
+	// Convert octet to bytes
+	V = OCT_toBytes(&SECOct)
+
+	return
+}
diff --git a/go/src/github.com/miracl/amcl-cgo/crypto_test.go b/go/src/github.com/miracl/amcl-cgo/crypto_test.go
index ebbacbb..454e943 100644
--- a/go/src/github.com/miracl/amcl-cgo/crypto_test.go
+++ b/go/src/github.com/miracl/amcl-cgo/crypto_test.go
@@ -766,3 +766,325 @@
 	got = hex.EncodeToString(AES_KEY_CLIENT[:])
 	assert.Equal(t, want, got, "Should be equal")
 }
+
+func TestTwoPassGoodPIN(t *testing.T) {
+	want := 0
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1234
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := amclgo.NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestTwoPassBadPIN(t *testing.T) {
+	want := -19
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1235
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := amclgo.NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestTwoPassBadToken(t *testing.T) {
+	want := -19
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1234
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := amclgo.NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, _ = MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	// Send UT as V to model bad token
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], UT[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestRandomTwoPass(t *testing.T) {
+	want := 0
+
+	for i := 0; i < nIter; i++ {
+
+		// Seed value for Random Number Generator (RNG)
+		seed := make([]byte, 16)
+		rand.Read(seed)
+		rng := amclgo.NewRAND()
+		rng.Seed(len(seed), seed)
+
+		// Epoch time in days
+		date := MPIN_today()
+
+		// PIN variable to create token
+		PIN1 := mathrand.Intn(10000)
+		// PIN variable to authenticate
+		PIN2 := PIN1
+
+		// Assign the End-User a random ID
+		ID := make([]byte, 16)
+		rand.Read(ID)
+
+		// Generate Master Secret Share 1
+		_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Generate Master Secret Share 2
+		_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Either Client or TA calculates Hash(ID)
+		HCID := MPIN_HASH_ID(ID)
+
+		// Generate server secret share 1
+		_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+		// Generate server secret share 2
+		_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+		// Combine server secret shares
+		_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+		// Generate client secret share 1
+		_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+		// Generate client secret share 2
+		_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+		// Combine client secret shares
+		CS := make([]byte, G1S)
+		_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+		// Generate time permit share 1
+		_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+		// Generate time permit share 2
+		_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+		// Combine time permit shares
+		_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+		// Create token
+		_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+		// Client Pass 1
+		var X [EGS]byte
+		_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+		// Server Pass 1
+		HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+		_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Client Pass 2
+		_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+		// Server Pass 2
+		got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+		assert.Equal(t, want, got, "Should be equal")
+
+	}
+}
diff --git a/go/src/github.com/miracl/amcl-go/crypto.go b/go/src/github.com/miracl/amcl-go/crypto.go
index 8e34f9c..e2b8bbb 100644
--- a/go/src/github.com/miracl/amcl-go/crypto.go
+++ b/go/src/github.com/miracl/amcl-go/crypto.go
@@ -189,3 +189,34 @@
 	}
 	return dst[:], 0
 }
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+func MPIN_SERVER_1_WRAP(date int, ID []byte) ([]byte, []byte) {
+	var HID [G1S]byte
+	var HTID [G1S]byte
+	MPIN_SERVER_1(date, ID, HID[:], HTID[:])
+	return HID[:], HTID[:]
+}
+
+/* Implement step 2 of MPin protocol on server side */
+func MPIN_SERVER_2_WRAP(date int, HID []byte, HTID []byte, Y []byte, SS []byte, U []byte, UT []byte, V []byte) (int, []byte, []byte) {
+	var E [12 * EFS]byte
+	var F [12 * EFS]byte
+	errorCode := MPIN_SERVER_2(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:], E[:], F[:])
+	return errorCode, E[:], F[:]
+}
+
+/* Implement step 1 on client side of MPin protocol */
+func MPIN_CLIENT_1_WRAP(date int, ID []byte, rng *RAND, X []byte, PIN int, TOKEN []byte, TP []byte) (int, []byte, []byte, []byte, []byte) {
+	var SEC [G1S]byte
+	var U [G1S]byte
+	var UT [G1S]byte
+	errorCode := MPIN_CLIENT_1(date, ID[:], rng, X[:], PIN, TOKEN[:], SEC[:], U[:], UT[:], TP[:])
+	return errorCode, X[:], SEC[:], U[:], UT[:]
+}
+
+/* Implement step 2 on client side of MPin protocol */
+func MPIN_CLIENT_2_WRAP(X []byte, Y []byte, SEC []byte) (int, []byte) {
+	errorCode := MPIN_CLIENT_2(X[:], Y[:], SEC[:])
+	return errorCode, SEC[:]
+}
diff --git a/go/src/github.com/miracl/amcl-go/crypto_test.go b/go/src/github.com/miracl/amcl-go/crypto_test.go
index 7fc169a..710204e 100644
--- a/go/src/github.com/miracl/amcl-go/crypto_test.go
+++ b/go/src/github.com/miracl/amcl-go/crypto_test.go
@@ -29,7 +29,7 @@
 	"github.com/stretchr/testify/assert"
 )
 
-const nIter int = 100
+const nIter int = 1000
 
 func TestCryptoGoodPIN(t *testing.T) {
 	want := 0
@@ -870,3 +870,325 @@
 	got := hex.EncodeToString(PIN[:])
 	assert.Equal(t, want, got, "Should be equal")
 }
+
+func TestCryptoTwoPassGoodPIN(t *testing.T) {
+	want := 0
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1234
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestCryptoTwoPassBadPIN(t *testing.T) {
+	want := -19
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1235
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestCryptoTwoPassBadToken(t *testing.T) {
+	want := -19
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+
+	// Epoch time in days
+	date := 16660
+
+	// PIN variable to create token
+	PIN1 := 1234
+	// PIN variable to authenticate
+	PIN2 := 1234
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Generate Master Secret Share 2
+	_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+	// Generate server secret share 2
+	_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+	// Combine server secret shares
+	_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+	// Generate client secret share 1
+	_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+	// Generate client secret share 2
+	_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+	// Combine client secret shares
+	CS := make([]byte, G1S)
+	_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+	// Generate time permit share 1
+	_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+	// Generate time permit share 2
+	_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+	// Combine time permit shares
+	_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+	// Create token
+	_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+	// Client Pass 1
+	var X [EGS]byte
+	_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+	// Server Pass 1
+	HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+	_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+	// Client Pass 2
+	_, _ = MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+	// Server Pass 2
+	// Send UT as V to model bad token
+	got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], UT[:])
+	assert.Equal(t, want, got, "Should be equal")
+}
+
+func TestCryptoRandomTwoPass(t *testing.T) {
+	want := 0
+
+	for i := 0; i < nIter; i++ {
+
+		// Seed value for Random Number Generator (RNG)
+		seed := make([]byte, 16)
+		rand.Read(seed)
+		rng := NewRAND()
+		rng.Seed(len(seed), seed)
+
+		// Epoch time in days
+		date := MPIN_today()
+
+		// PIN variable to create token
+		PIN1 := mathrand.Intn(10000)
+		// PIN variable to authenticate
+		PIN2 := PIN1
+
+		// Assign the End-User a random ID
+		ID := make([]byte, 16)
+		rand.Read(ID)
+
+		// Generate Master Secret Share 1
+		_, MS1 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Generate Master Secret Share 2
+		_, MS2 := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Either Client or TA calculates Hash(ID)
+		HCID := MPIN_HASH_ID(ID)
+
+		// Generate server secret share 1
+		_, SS1 := MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+
+		// Generate server secret share 2
+		_, SS2 := MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+
+		// Combine server secret shares
+		_, SS := MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+
+		// Generate client secret share 1
+		_, CS1 := MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+
+		// Generate client secret share 2
+		_, CS2 := MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+
+		// Combine client secret shares
+		CS := make([]byte, G1S)
+		_, CS = MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+
+		// Generate time permit share 1
+		_, TP1 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+
+		// Generate time permit share 2
+		_, TP2 := MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+
+		// Combine time permit shares
+		_, TP := MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+
+		// Create token
+		_, TOKEN := MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+
+		// Client Pass 1
+		var X [EGS]byte
+		_, _, SEC, U, UT := MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+
+		// Server Pass 1
+		HID, HTID := MPIN_SERVER_1_WRAP(date, ID)
+		_, Y := MPIN_RANDOM_GENERATE_WRAP(rng)
+
+		// Client Pass 2
+		_, V := MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+
+		// Server Pass 2
+		got, _, _ := MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+		assert.Equal(t, want, got, "Should be equal")
+
+	}
+}
diff --git a/go/src/github.com/miracl/examples-cgo/mpinTwoPass.go b/go/src/github.com/miracl/examples-cgo/mpinTwoPass.go
new file mode 100644
index 0000000..63b541b
--- /dev/null
+++ b/go/src/github.com/miracl/examples-cgo/mpinTwoPass.go
@@ -0,0 +1,227 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	amclcgo "github.com/miracl/amcl-cgo"
+	amclgo "github.com/miracl/amcl-go"
+)
+
+func main() {
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+	fmt.Printf("ID: ")
+	amclcgo.MPIN_printBinary(ID)
+	fmt.Printf("\n")
+
+	// Epoch time in days
+	date := amclcgo.MPIN_today()
+
+	// PIN variable to create token
+	PIN1 := -1
+	// PIN variable to authenticate
+	PIN2 := -1
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := amclgo.NewRAND()
+	rng.Seed(len(seed), seed)
+
+	// Generate Master Secret Share 1
+	rtn, MS1 := amclcgo.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("MS1: 0x")
+	amclcgo.MPIN_printBinary(MS1[:])
+
+	// Generate Master Secret Share 2
+	rtn, MS2 := amclcgo.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("MS2: 0x")
+	amclcgo.MPIN_printBinary(MS2[:])
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := amclcgo.MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	rtn, SS1 := amclcgo.MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_SERVER_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("SS1: 0x")
+	amclcgo.MPIN_printBinary(SS1[:])
+
+	// Generate server secret share 2
+	rtn, SS2 := amclcgo.MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_SERVER_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("SS2: 0x")
+	amclcgo.MPIN_printBinary(SS2[:])
+
+	// Combine server secret shares
+	rtn, SS := amclcgo.MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G2(SS1, SS2) Error:", rtn)
+		return
+	}
+	fmt.Printf("SS: 0x")
+	amclcgo.MPIN_printBinary(SS[:])
+
+	// Generate client secret share 1
+	rtn, CS1 := amclcgo.MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret Share CS1: 0x")
+	amclcgo.MPIN_printBinary(CS1[:])
+
+	// Generate client secret share 2
+	rtn, CS2 := amclcgo.MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret Share CS2: 0x")
+	amclcgo.MPIN_printBinary(CS2[:])
+
+	// Combine client secret shares
+	CS := make([]byte, amclcgo.G1S)
+	rtn, CS = amclcgo.MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G1 Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret CS: 0x")
+	amclcgo.MPIN_printBinary(CS[:])
+
+	// Generate time permit share 1
+	rtn, TP1 := amclcgo.MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_PERMIT Error:", rtn)
+		return
+	}
+	fmt.Printf("TP1: 0x")
+	amclcgo.MPIN_printBinary(TP1[:])
+
+	// Generate time permit share 2
+	rtn, TP2 := amclcgo.MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_PERMIT Error:", rtn)
+		return
+	}
+	fmt.Printf("TP2: 0x")
+	amclcgo.MPIN_printBinary(TP2[:])
+
+	// Combine time permit shares
+	rtn, TP := amclcgo.MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G1(TP1, TP2) Error:", rtn)
+		return
+	}
+
+	// Client extracts PIN1 from secret to create Token
+	for PIN1 < 0 {
+		fmt.Printf("Please enter PIN to create token: ")
+		fmt.Scan(&PIN1)
+	}
+
+	rtn, TOKEN := amclcgo.MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: EXTRACT_PIN rtn: %d\n", rtn)
+		return
+	}
+	fmt.Printf("Client Token TK: 0x")
+	amclcgo.MPIN_printBinary(TOKEN[:])
+
+	//////   Client   //////
+
+	for PIN2 < 0 {
+		fmt.Printf("Please enter PIN to authenticate: ")
+		fmt.Scan(&PIN2)
+	}
+
+	////// Client Pass 1 //////
+	// Send U and UT to server
+	var X [amclcgo.EGS]byte
+	fmt.Printf("X: 0x")
+	amclcgo.MPIN_printBinary(X[:])
+	rtn, XOut, SEC, U, UT := amclcgo.MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: CLIENT rtn: %d\n", rtn)
+		return
+	}
+	fmt.Printf("XOut: 0x")
+	amclcgo.MPIN_printBinary(XOut[:])
+
+	//////   Server Pass 1  //////
+	/* Calculate H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+	HID, HTID := amclcgo.MPIN_SERVER_1_WRAP(date, ID)
+
+	/* Send Y to Client */
+	rtn, Y := amclcgo.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("Y: 0x")
+	amclcgo.MPIN_printBinary(Y[:])
+
+	/* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+	rtn, V := amclcgo.MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: CLIENT_2 rtn: %d\n", rtn)
+	}
+
+	/* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+	/* If PIN error not required, set E and F = null */
+	rtn, _, _ = amclcgo.MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: MPIN_SERVER_2 rtn: %d\n", rtn)
+	}
+	fmt.Printf("HID: 0x")
+	amclcgo.MPIN_printBinary(HID[:])
+	fmt.Printf("HTID: 0x")
+	amclcgo.MPIN_printBinary(HTID[:])
+
+	if rtn != 0 {
+		fmt.Printf("Authentication failed Error Code %d\n", rtn)
+		return
+	} else {
+		fmt.Printf("Authenticated ID: %s \n", IDstr)
+	}
+}
diff --git a/go/src/github.com/miracl/examples-go/mpinTwoPass.go b/go/src/github.com/miracl/examples-go/mpinTwoPass.go
index f86968e..f2c3d33 100644
--- a/go/src/github.com/miracl/examples-go/mpinTwoPass.go
+++ b/go/src/github.com/miracl/examples-go/mpinTwoPass.go
@@ -164,7 +164,7 @@
 		fmt.Scan(&PIN2)
 	}
 
-	/* Cleints first pass. Calculate U and UT */
+	/* Clients first pass. Calculate U and UT */
 	rtn = amcl.MPIN_CLIENT_1(date, ID, rng, X[:], PIN2, TOKEN[:], SEC[:], U[:], UT[:], TP[:])
 	if rtn != 0 {
 		fmt.Printf("FAILURE: CLIENT rtn: %d\n", rtn)
diff --git a/go/src/github.com/miracl/examples-go/mpinTwoPassWrap.go b/go/src/github.com/miracl/examples-go/mpinTwoPassWrap.go
new file mode 100644
index 0000000..6ef787d
--- /dev/null
+++ b/go/src/github.com/miracl/examples-go/mpinTwoPassWrap.go
@@ -0,0 +1,228 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	amcl "github.com/miracl/amcl-go"
+)
+
+func main() {
+	// Assign the End-User an ID
+	IDstr := "testUser@miracl.com"
+	ID := []byte(IDstr)
+	fmt.Printf("ID: ")
+	amcl.MPIN_printBinary(ID)
+	fmt.Printf("\n")
+
+	// Epoch time in days
+	date := amcl.MPIN_today()
+
+	// PIN variable to create token
+	PIN1 := -1
+	// PIN variable to authenticate
+	PIN2 := -1
+
+	// Seed value for Random Number Generator (RNG)
+	seedHex := "9e8b4178790cd57a5761c4a6f164ba72"
+	seed, err := hex.DecodeString(seedHex)
+	if err != nil {
+		fmt.Println("Error decoding seed value")
+		return
+	}
+	rng := amcl.NewRAND()
+	rng.Seed(len(seed), seed)
+
+	const EGS = amcl.MPIN_EGS
+	const EFS = amcl.MPIN_EFS
+	const G1S = 2*EFS + 1 /* Group 1 Size */
+	const G2S = 4 * EFS   /* Group 2 Size */
+	const EAS = amcl.MPIN_PAS
+
+	var X [EGS]byte
+
+	// Generate Master Secret Share 1
+	rtn, MS1 := amcl.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("MS1: 0x")
+	amcl.MPIN_printBinary(MS1[:])
+
+	// Generate Master Secret Share 2
+	rtn, MS2 := amcl.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("MS2: 0x")
+	amcl.MPIN_printBinary(MS2[:])
+
+	// Either Client or TA calculates Hash(ID)
+	HCID := amcl.MPIN_HASH_ID(ID)
+
+	// Generate server secret share 1
+	rtn, SS1 := amcl.MPIN_GET_SERVER_SECRET_WRAP(MS1[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_SERVER_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("SS1: 0x")
+	amcl.MPIN_printBinary(SS1[:])
+
+	// Generate server secret share 2
+	rtn, SS2 := amcl.MPIN_GET_SERVER_SECRET_WRAP(MS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_SERVER_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("SS2: 0x")
+	amcl.MPIN_printBinary(SS2[:])
+
+	// Combine server secret shares
+	rtn, SS := amcl.MPIN_RECOMBINE_G2_WRAP(SS1[:], SS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G2(SS1, SS2) Error:", rtn)
+		return
+	}
+	fmt.Printf("SS: 0x")
+	amcl.MPIN_printBinary(SS[:])
+
+	// Generate client secret share 1
+	rtn, CS1 := amcl.MPIN_GET_CLIENT_SECRET_WRAP(MS1[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret CS: 0x")
+	amcl.MPIN_printBinary(CS1[:])
+
+	// Generate client secret share 2
+	rtn, CS2 := amcl.MPIN_GET_CLIENT_SECRET_WRAP(MS2[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_SECRET Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret CS: 0x")
+	amcl.MPIN_printBinary(CS2[:])
+
+	// Combine client secret shares
+	rtn, CS := amcl.MPIN_RECOMBINE_G1_WRAP(CS1[:], CS2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G1 Error:", rtn)
+		return
+	}
+	fmt.Printf("Client Secret CS: 0x")
+	amcl.MPIN_printBinary(CS[:])
+
+	// Generate time permit share 1
+	rtn, TP1 := amcl.MPIN_GET_CLIENT_PERMIT_WRAP(date, MS1[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_PERMIT Error:", rtn)
+		return
+	}
+	fmt.Printf("TP1: 0x")
+	amcl.MPIN_printBinary(TP1[:])
+
+	// Generate time permit share 2
+	rtn, TP2 := amcl.MPIN_GET_CLIENT_PERMIT_WRAP(date, MS2[:], HCID)
+	if rtn != 0 {
+		fmt.Println("MPIN_GET_CLIENT_PERMIT Error:", rtn)
+		return
+	}
+	fmt.Printf("TP2: 0x")
+	amcl.MPIN_printBinary(TP2[:])
+
+	// Combine time permit shares
+	rtn, TP := amcl.MPIN_RECOMBINE_G1_WRAP(TP1[:], TP2[:])
+	if rtn != 0 {
+		fmt.Println("MPIN_RECOMBINE_G1(TP1, TP2) Error:", rtn)
+		return
+	}
+
+	// Client extracts PIN1 from secret to create Token
+	for PIN1 < 0 {
+		fmt.Printf("Please enter PIN to create token: ")
+		fmt.Scan(&PIN1)
+	}
+
+	rtn, TOKEN := amcl.MPIN_EXTRACT_PIN_WRAP(ID[:], PIN1, CS[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: EXTRACT_PIN rtn: %d\n", rtn)
+		return
+	}
+	fmt.Printf("Client Token TK: 0x")
+	amcl.MPIN_printBinary(TOKEN[:])
+
+	for PIN2 < 0 {
+		fmt.Printf("Please enter PIN to authenticate: ")
+		fmt.Scan(&PIN2)
+	}
+
+	/* Clients first pass. Calculate U and UT */
+	fmt.Printf("X: 0x")
+	amcl.MPIN_printBinary(X[:])
+	rtn, Xout, SEC, U, UT := amcl.MPIN_CLIENT_1_WRAP(date, ID, rng, X[:], PIN2, TOKEN[:], TP[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: CLIENT rtn: %d\n", rtn)
+		return
+	}
+	fmt.Printf("Xout: 0x")
+	amcl.MPIN_printBinary(Xout[:])
+
+	/* Server first pass. Calculate H(ID) and H(T|H(ID)) (if time permits enabled), and maps them to points on the curve HID and HTID resp. */
+	HID, HTID := amcl.MPIN_SERVER_1_WRAP(date, ID)
+
+	/* Server generates Random number Y and sends it to Client */
+	rtn, Y := amcl.MPIN_RANDOM_GENERATE_WRAP(rng)
+	if rtn != 0 {
+		fmt.Println("MPIN_RANDOM_GENERATE Error:", rtn)
+		return
+	}
+	fmt.Printf("Y: 0x")
+	amcl.MPIN_printBinary(Y[:])
+
+	/* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+	rtn, V := amcl.MPIN_CLIENT_2_WRAP(X[:], Y[:], SEC[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: CLIENT_2 rtn: %d\n", rtn)
+	}
+
+	/* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+	/* If PIN error not required, set E and F = null */
+	rtn, _, _ = amcl.MPIN_SERVER_2_WRAP(date, HID[:], HTID[:], Y[:], SS[:], U[:], UT[:], V[:])
+	if rtn != 0 {
+		fmt.Printf("FAILURE: MPIN_SERVER_2 rtn: %d\n", rtn)
+	}
+	fmt.Printf("HID: 0x")
+	amcl.MPIN_printBinary(HID[:])
+	fmt.Printf("HTID: 0x")
+	amcl.MPIN_printBinary(HTID[:])
+
+	if rtn == amcl.MPIN_BAD_PIN {
+		fmt.Printf("Authentication failed Error Code %d\n", rtn)
+		return
+	} else {
+		fmt.Printf("Authenticated ID: %s \n", IDstr)
+	}
+}