Skip to content

Commit 483431b

Browse files
authored
[IMPLEMENTATION] RSA algorithm (#727)
* [IMPLEMENTATION] RSA algorithm * [UPDATE] Intended keyGen algorithm with randomness * [FIX] comment fixed * [UPDATE] Implement suggestions * [FIX] Implement suggestions
1 parent cddedb6 commit 483431b

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

cipher/rsa/rsa2.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
rsa2.go
3+
description: RSA encryption and decryption including key generation
4+
details: [RSA wiki](https://en.wikipedia.org/wiki/RSA_(cryptosystem))
5+
author(s): [ddaniel27](https://github.com/ddaniel27)
6+
*/
7+
package rsa
8+
9+
import (
10+
"encoding/binary"
11+
"fmt"
12+
"math/big"
13+
"math/rand"
14+
15+
"github.com/TheAlgorithms/Go/math/gcd"
16+
"github.com/TheAlgorithms/Go/math/lcm"
17+
"github.com/TheAlgorithms/Go/math/modular"
18+
"github.com/TheAlgorithms/Go/math/prime"
19+
)
20+
21+
// rsa struct contains the public key, private key and modulus
22+
type rsa struct {
23+
publicKey uint64
24+
privateKey uint64
25+
modulus uint64
26+
}
27+
28+
// New initializes the RSA algorithm
29+
// returns the RSA object
30+
func New() *rsa {
31+
// The following code generates keys for RSA encryption/decryption
32+
// 1. Choose two large prime numbers, p and q and compute n = p * q
33+
p, q := randomPrime() // p and q stands for prime numbers
34+
modulus := p * q // n stands for common number
35+
36+
// 2. Compute the totient of n, lcm(p-1, q-1)
37+
totient := uint64(lcm.Lcm(int64(p-1), int64(q-1)))
38+
39+
// 3. Choose an integer e such that 1 < e < totient(n) and gcd(e, totient(n)) = 1
40+
publicKey := uint64(2) // e stands for encryption key (public key)
41+
for publicKey < totient {
42+
if gcd.Recursive(int64(publicKey), int64(totient)) == 1 {
43+
break
44+
}
45+
publicKey++
46+
}
47+
48+
// 4. Compute d such that d * e ≡ 1 (mod totient(n))
49+
inv, _ := modular.Inverse(int64(publicKey), int64(totient))
50+
privateKey := uint64(inv)
51+
52+
return &rsa{
53+
publicKey: publicKey,
54+
privateKey: privateKey,
55+
modulus: modulus,
56+
}
57+
}
58+
59+
// EncryptString encrypts the data using RSA algorithm
60+
// returns the encrypted string
61+
func (rsa *rsa) EncryptString(data string) string {
62+
var nums []byte
63+
64+
for _, char := range data {
65+
slice := make([]byte, 8)
66+
binary.BigEndian.PutUint64( // convert uint64 to byte slice
67+
slice,
68+
encryptDecryptInt(rsa.publicKey, rsa.modulus, uint64(char)), // encrypt each character
69+
)
70+
nums = append(nums, slice...)
71+
}
72+
73+
return string(nums)
74+
}
75+
76+
// DecryptString decrypts the data using RSA algorithm
77+
// returns the decrypted string
78+
func (rsa *rsa) DecryptString(data string) string {
79+
result := ""
80+
middle := []byte(data)
81+
82+
for i := 0; i < len(middle); i += 8 {
83+
if i+8 > len(middle) {
84+
break
85+
}
86+
87+
slice := middle[i : i+8]
88+
num := binary.BigEndian.Uint64(slice) // convert byte slice to uint64
89+
result += fmt.Sprintf("%c", encryptDecryptInt(rsa.privateKey, rsa.modulus, num))
90+
}
91+
92+
return result
93+
}
94+
95+
// GetPublicKey returns the public key and modulus
96+
func (rsa *rsa) GetPublicKey() (uint64, uint64) {
97+
return rsa.publicKey, rsa.modulus
98+
}
99+
100+
// GetPrivateKey returns the private key
101+
func (rsa *rsa) GetPrivateKey() uint64 {
102+
return rsa.privateKey
103+
}
104+
105+
// encryptDecryptInt encrypts or decrypts the data using RSA algorithm
106+
func encryptDecryptInt(e, n, data uint64) uint64 {
107+
pow := new(big.Int).Exp(big.NewInt(int64(data)), big.NewInt(int64(e)), big.NewInt(int64(n)))
108+
return pow.Uint64()
109+
}
110+
111+
// randomPrime returns two random prime numbers
112+
func randomPrime() (uint64, uint64) {
113+
sieve := prime.SieveEratosthenes(1000)
114+
sieve = sieve[10:] // remove first 10 prime numbers (small numbers)
115+
index1 := rand.Intn(len(sieve))
116+
index2 := rand.Intn(len(sieve))
117+
118+
for index1 == index2 {
119+
index2 = rand.Intn(len(sieve))
120+
}
121+
122+
return uint64(sieve[index1]), uint64(sieve[index2])
123+
}

cipher/rsa/rsa2_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package rsa_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/TheAlgorithms/Go/cipher/rsa"
7+
)
8+
9+
func TestRSA(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
message string
13+
}{
14+
{
15+
name: "Encrypt letter 'a' and decrypt it back",
16+
message: "a",
17+
},
18+
{
19+
name: "Encrypt 'Hello, World!' and decrypt it back",
20+
message: "Hello, World!",
21+
},
22+
}
23+
24+
for _, tt := range tests {
25+
t.Run(tt.name, func(t *testing.T) {
26+
rsa := rsa.New()
27+
encrypted := rsa.EncryptString(tt.message)
28+
decrypted := rsa.DecryptString(encrypted)
29+
if decrypted != tt.message {
30+
t.Errorf("expected %s, got %s", tt.message, decrypted)
31+
}
32+
})
33+
}
34+
}
35+
36+
func BenchmarkRSAEncryption(b *testing.B) {
37+
rsa := rsa.New()
38+
for i := 0; i < b.N; i++ {
39+
rsa.EncryptString("Hello, World!")
40+
}
41+
}
42+
43+
func BenchmarkRSADecryption(b *testing.B) {
44+
rsa := rsa.New()
45+
encrypted := rsa.EncryptString("Hello, World!")
46+
for i := 0; i < b.N; i++ {
47+
rsa.DecryptString(encrypted)
48+
}
49+
}

0 commit comments

Comments
 (0)