package rsa
import (
"encoding/binary"
"fmt"
"math/big"
"math/rand"
"github.com/TheAlgorithms/Go/math/gcd"
"github.com/TheAlgorithms/Go/math/lcm"
"github.com/TheAlgorithms/Go/math/modular"
"github.com/TheAlgorithms/Go/math/prime"
)
type rsa struct {
publicKey uint64
privateKey uint64
modulus uint64
}
func New() *rsa {
p, q := randomPrime()
modulus := p * q
totient := uint64(lcm.Lcm(int64(p-1), int64(q-1)))
publicKey := uint64(2)
for publicKey < totient {
if gcd.Recursive(int64(publicKey), int64(totient)) == 1 {
break
}
publicKey++
}
inv, _ := modular.Inverse(int64(publicKey), int64(totient))
privateKey := uint64(inv)
return &rsa{
publicKey: publicKey,
privateKey: privateKey,
modulus: modulus,
}
}
func (rsa *rsa) EncryptString(data string) string {
var nums []byte
for _, char := range data {
slice := make([]byte, 8)
binary.BigEndian.PutUint64(
slice,
encryptDecryptInt(rsa.publicKey, rsa.modulus, uint64(char)),
)
nums = append(nums, slice...)
}
return string(nums)
}
func (rsa *rsa) DecryptString(data string) string {
result := ""
middle := []byte(data)
for i := 0; i < len(middle); i += 8 {
if i+8 > len(middle) {
break
}
slice := middle[i : i+8]
num := binary.BigEndian.Uint64(slice)
result += fmt.Sprintf("%c", encryptDecryptInt(rsa.privateKey, rsa.modulus, num))
}
return result
}
func (rsa *rsa) GetPublicKey() (uint64, uint64) {
return rsa.publicKey, rsa.modulus
}
func (rsa *rsa) GetPrivateKey() uint64 {
return rsa.privateKey
}
func encryptDecryptInt(e, n, data uint64) uint64 {
pow := new(big.Int).Exp(big.NewInt(int64(data)), big.NewInt(int64(e)), big.NewInt(int64(n)))
return pow.Uint64()
}
func randomPrime() (uint64, uint64) {
sieve := prime.SieveEratosthenes(1000)
sieve = sieve[10:]
index1 := rand.Intn(len(sieve))
index2 := rand.Intn(len(sieve))
for index1 == index2 {
index2 = rand.Intn(len(sieve))
}
return uint64(sieve[index1]), uint64(sieve[index2])
}