Big Integers and Some Crypto Homomorphic Magic

In software, we normally use integers that have 32-bit or 64-bit values. But in cryptography, we can be using 1,024, 2,048 and even…

Photo by Tyler Easton on Unsplash

Big Integers and Some Crypto Homomorphic Magic

In software, we normally use integers that have 32-bit or 64-bit values. But in cryptography, we can be using 1,024, 2,048 and even 4,096-bit values. So, how do we do that? Well, we use Big Integers, and which can be of any size. In this article, we will implement a Paillier homomorphic method using Big Integers. A 2,048-bit value, for example, has this many digits:

32,317,006,071,311,007,300,714,876,688,669,951,960,444,102,669,715,
484,032,130,345,427,524,655,138,867,890,893,197,201,411,522,
913,463,688,717,960,921,898,019,494,119,559,150,490,921,095,088,152,386,448,283,120,630,877,367,300,996,091,750,197,750,389,652,
106,796,057,638,384,067,568,276,792,218,642,619,756,161,838,094,338,476,170,470,581,645,852,036,305,042,887,575,891,541,065,808,
607,552,399,123,930,385,521,914,333,389,668,342,420,684,974,786,564,569,494,856,176,035,326,322,058,077,805,659,331,026,192,708,
460,314,150,258,592,864,177,116,725,943,603,718,461,857,357,598,351,152,301,645,904,403,697,613,233,287,231,227,125,684,710,820,
209,725,157,101,726,931,323,469,678,542,580,656,697,935,045,997,268,352,998,638,215,525,166,389,437,335,543,602,135,433,229,604,
645,318,478,604,952,148,193,555,853,611,059,596,230,656

In Golang, we assign a value to a Big Integer in many ways, such as assigning an integer, byte values, or from an integer string. For integer casting, we can use the format of:

a:=big.NewInt(2)

Note that the value of a will be automatically assigned as a big.Int data type as we have used the assignment operation (:=). For string assignment:

pub.N, _ = new(big.Int).SetString("2220390286", 10)

To multiply two values (a and b) to give c:

c := new(big.Int).Mul(a, b)

To add two values (a and c) to give c:

c := new(big.Int).Add(a, b)

In order to perform a modulo operation of res=a.b (mod n), we have:

c := new(big.Int).Mul(a, b)
res:=new(big.Int).Mod(c, n)

Another typical operator is the inverse modulus of a value (z=v^{-1} (mod n)):

z := new(big.Int).ModInverse(v, n)

Paillier

Adding and scalar multiply

Subtract

Code

The basic data structures for the secret key, public key and ciphertext is:

type (
PublicKey struct {
N *big.Int
N2 *big.Int
}
	SecretKey struct {
PublicKey
Lambda *big.Int
Totient *big.Int
U *big.Int
}
Ciphertext *big.Int
)

The adding of ciphertext values is achieved through a multiplication of our big integer ciphertext values c=ab (mod n²):

func add(pk PublicKey, a Ciphertext, b Ciphertext) Ciphertext {
	c := new(big.Int).Mul(a, b)
return (new(big.Int).Mod(c, pk.N2))
}

When we multiply two Paillier ciphertext’s, we actually use an exponential function, and where Mult(a,c)−>c.a (mod n)

func mul(a, c, m *big.Int) *big.Int {
	z := new(big.Int).Exp(c, a, m)
return (z)
}

When we need to multiply two big integers we do it using a mod operator:

func Mul(x, y, m *big.Int) *big.Int {
	z := new(big.Int).Mul(x, y)
	return z.Mod(z, m)
}

The inverse mod is used in the subtraction operation, and is implemented as:

func Inv(x, m *big.Int) *big.Int {
	z := new(big.Int).ModInverse(x, m)
return (z)
}

To encrypt the values, we create a random nonce (r) and implemented as:

func encrypt(pk PublicKey, msg *big.Int) *big.Int {
	r, _ := core.Rand(pk.N)
	a := new(big.Int).Add(pk.N, core.One)
a.Exp(a, msg, pk.N2)
b := new(big.Int).Exp(r, pk.N, pk.N2) // b = r^N (mod N^2)
	// ciphertext = a*b = (N+1)^m * r^N  (mod N^2)
c := Mul(a, b, pk.N2)
	return c
}
func L(n, x *big.Int) *big.Int {
// (x - 1) / n
b := new(big.Int).Sub(x, big.NewInt(1))
return b.Div(b, n)
}

To decrypt:

func decrypt(sk SecretKey, c *big.Int) *big.Int {
	// a ≡ c^{lambda(N)}		mod N^2
a := new(big.Int).Exp(c, sk.Lambda, sk.N2)
	// l = L(ɑ, N)
ell := L(sk.N, a)
	// m ≡ lu = L(ɑ)*u = L(c^{λ(N)})*u	mod N
m := Mul(ell, sk.U, sk.N)
	return m
}

Coding

The code is [here]:

package main
import (
"fmt"
"math/big"
"os"
	"github.com/coinbase/kryptology/pkg/core"
)
type (
PublicKey struct {
N *big.Int
N2 *big.Int
}
	SecretKey struct {
PublicKey
Lambda *big.Int
Totient *big.Int
U *big.Int
}
Ciphertext *big.Int
)
func add(pk PublicKey, a Ciphertext, b Ciphertext) Ciphertext {
	c := new(big.Int).Mul(a, b)
return (new(big.Int).Mod(c, pk.N2))
}
func encrypt(pk PublicKey, msg *big.Int) *big.Int {
	r, _ := core.Rand(pk.N)
	a := new(big.Int).Add(pk.N, core.One)
a.Exp(a, msg, pk.N2)
b := new(big.Int).Exp(r, pk.N, pk.N2) // b = r^N (mod N^2)
	// ciphertext = a*b = (N+1)^m * r^N  (mod N^2)
c := Mul(a, b, pk.N2)
	return c
}
func L(n, x *big.Int) *big.Int {
// (x - 1) / n
b := new(big.Int).Sub(x, big.NewInt(1))
return b.Div(b, n)
}
func decrypt(sk SecretKey, c *big.Int) *big.Int {
	// a ≡ c^{lambda(N)}		mod N^2
a := new(big.Int).Exp(c, sk.Lambda, sk.N2)
	// l = L(ɑ, N)
ell := L(sk.N, a)
	// m ≡ lu = L(ɑ)*u = L(c^{λ(N)})*u	mod N
m := Mul(ell, sk.U, sk.N)
	return m
}
func Mul(x, y, m *big.Int) *big.Int {
	z := new(big.Int).Mul(x, y)
	return z.Mod(z, m)
}
func Inv(x, m *big.Int) *big.Int {
	z := new(big.Int).ModInverse(x, m)
return (z)
}
func mul(a, c, m *big.Int) *big.Int {
	z := new(big.Int).Exp(c, a, m)
return (z)
}
func setupKeys(sec *SecretKey, pub *PublicKey) {
pub.N, _ = new(big.Int).SetString("22203902867524505059996239340306362808852805402888214954381553003002718752808306965243974655390219346481612755387890570991182385566928749760445875916800573782909761881261515602762049819293013811136510263722491329215251675663091154175860620927146517652389408089110716148633480085801107700968384078929774277970426932561081560231010426294975678729992804063220974701278229766883426991469078323539488917623430196595127834729964807458110080684240115196595760172158113810254192728271785178985307185853395355962836026777351498860874006114137632167254987479651229489157192247478252351962954320801263428208801271515398015887801", 10)
pub.N2 = new(big.Int).Mul(pub.N, pub.N)
	sec.N = pub.N
sec.N2 = pub.N2
sec.Lambda, _ = new(big.Int).SetString("11101951433762252529998119670153181404426402701444107477190776501501359376404153482621987327695109673240806377693945285495591192783464374880222937958400286891454880940630757801381024909646506905568255131861245664607625837831545577087930310463573258826194704044555358074316740042900553850484192039464887138985064438949068643503538028104882092520753872226183177268421975002892541203962036580811698912148170624870917841537346985483337432253649017635885024033744586145456966646545432316660152135614842196027111652507352356170725302821683928442675667004336523805058723372589095589316741830468728743532156406225121890756346", 10)
sec.Totient, _ = new(big.Int).SetString("22203902867524505059996239340306362808852805402888214954381553003002718752808306965243974655390219346481612755387890570991182385566928749760445875916800573782909761881261515602762049819293013811136510263722491329215251675663091154175860620927146517652389408089110716148633480085801107700968384078929774277970128877898137287007076056209764185041507744452366354536843950005785082407924073161623397824296341249741835683074693970966674864507298035271770048067489172290913933293090864633320304271229684392054223305014704712341450605643367856885351334008673047610117446745178191178633483660937457487064312812450243781512692", 10)
sec.U, _ = new(big.Int).SetString("11108720355657041776647490476262423100273444107890028034525926371450220865341816894255404548947647952186614924131107824022774134150177636593890919577479093776330856863330832621779073688637362125712752218152358601679371477743308049274665882845748928958872854339383876978533793119837835146167726752137945969833510158025058440018805812646143848801020667307117190186323497007267362203459968413761903710129427082211518450427328378521338965975284389455704146581178957343955351641425309976491396722227064973929033480821132777267246266490713852090985090755453080857556125803448778856380844874040092451545474091429770838805", 10)
}
func main() {
	var sec SecretKey
var pub PublicKey
	//	pub, sec, _ := paillier.NewKeys()
setupKeys(&sec, &pub)
	v1 := "223"
v2 := "224"
argCount := len(os.Args[1:])
	if argCount > 0 {
v1 = os.Args[1]
}
if argCount > 1 {
v2 = os.Args[2]
}
	val1, _ := new(big.Int).SetString(v1, 10)
val2, _ := new(big.Int).SetString(v2, 10)
	cipher1 := encrypt(pub, val1)
	cipher2 := encrypt(pub, val2)
	// Adding
cipher3 := add(pub, cipher1, cipher2)
	decrypted1 := decrypt(sec, cipher3)
	// Subtraction
inv_cipher2 := Inv(cipher2, pub.N2)
	cipher4 := add(pub, cipher1, inv_cipher2)
	decrypted2 := decrypt(sec, cipher4)
	// Multiply
cipher5 := mul(val1, cipher2, pub.N2)
	decrypted3 := decrypt(sec, cipher5)
	fmt.Printf("\na=%s, b=%s\n", val1, val2)
	fmt.Printf("\nEnc(a)=%v, Enc(b)=%v\n", cipher1, cipher2)
	fmt.Printf("\n%s + %s = %s\n", val1, val2, decrypted1)
fmt.Printf("\n%s * %s = %s\n", val1, val2, decrypted3)
	if decrypted2.Cmp(new(big.Int).Div(pub.N, big.NewInt(2))) > 0 {
// m = m - n
fmt.Printf("%s - %s = %s\n", val1, val2, new(big.Int).Sub(decrypted2, pub.N))
} else {
fmt.Printf("%s - %s = %s\n", val1, val2, decrypted2)
}
}

A sample run is [here]:

a=222, b=111
Enc(a)=480255181920800962319428195982635686810088385894694335732539232950598802361655578994299473947015823706085641632265918920177271050935899445772018657379409917340875585795573456622928011108763896998310685496049348635361148476665982292583742521662892246510134496910480985230953608457374596556061690681311372035369557473453948571498287540563940498719457037028423196712497593060402292462158152772365096268526544613966053669489002176306136618535389523393860906046429497082997574262450762786531668654114069457095304276136300381237935928488169886100022455673772985830484439920173220489954345908504730126607166660669616837735331132127595030555268828556190724381620586303349525363142824926041003132666363188063124075469384544768925327914082583686275806308821004655253016048332888033533857446684327464728753575758198032289396001937677420421304213675660776816637709884428865855227219223951888611467829806312828381700124791746116297756951418091223465584531827315766709832802225806892686198418948109322860207792492578901690557854559299736849385819889486069503912193322571436752435739403077721769655830857694261861731325926243758449255964430155072325622086559490250784450951299715352393589113643733050357885271539548238790965835016548892755681491217, Enc(b)=398205057005000805455412925833532994717615194020655376461633849479054914228469584412356182413073212222229225897995778310285802001620224215672510848280476816560246933598044438129822536195476471730535502177390452652250248208595245774868663210615963412562038409604119717517734532923327183494024965696228498538639558086901827119637452112502647978827481995866363495625837621647623546620410500086811780489366824575093690909960314240825269897595801111438023076383121529088191656996666295497574395828609506976835480393272407009883660101408839186236877371208967820363921744294119034984338166756418022949781351180172422781584254651807677632253283134440525408418041260953938375203226980802190306068235344987804712672397597944426532987481662630428128948672757884566423914969203583167323575075335034411157347418887682930435526342638719818198078786738032388166189020743344542420574023931557754426969505264962187883070184429059626960626546411313341199020568060067512561944206262699174908858448584688032887324374515251373912574173526824962194722293602736606882903604170107467326730803713106312324982828292317687693528819441737056014833346488071437353342607272253728630007825092500186949850603988095382717600780136544280866381494593592289423116839401
222 + 111 = 333
222 * 111 = 24642
222 - 111 = 111

Conclusions

And there you go … get into Big Integers … and create a world of numbers without any bounds. Here are some more Golang examples:

https://asecuritysite.com/golang