Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the implementation of bisection method #766

Closed
Closed
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Keep GitHub Actions up to date with GitHub's Dependabot...
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
version: 2
updates:
- package-ecosystem: github-actions
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly
7 changes: 7 additions & 0 deletions cache/lru.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
// lru.go
// description : Least Recently Used (LRU) cache
// details : A Least Recently Used (LRU) cache is a type of cache algorithm used to manage memory within a computer. The LRU algorithm is designed to remove the least recently used items first when the cache reaches its limit.
// time complexity : O(1)
// space complexity : O(n)
// ref : https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)

package cache

import (
Expand Down
2 changes: 2 additions & 0 deletions checksum/crc8.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// details:
// A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks
// and storage devices to detect accidental changes to raw data.
// time complexity: O(n)
// space complexity: O(1)
// See more [CRC](https://en.wikipedia.org/wiki/Cyclic_redundancy_check)
// author(s) [red_byte](https://github.com/i-redbyte)
// see crc8_test.go
Expand Down
2 changes: 2 additions & 0 deletions checksum/luhn.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// lunh.go
// description: Luhn algorithm
// details: is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, etc [Lunh](https://en.wikipedia.org/wiki/Luhn_algorithm)
// time complexity: O(n)
// space complexity: O(1)
// author(s) [red_byte](https://github.com/i-redbyte)
// see lunh_test.go

Expand Down
4 changes: 4 additions & 0 deletions cipher/caesar/caesar.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Package caesar is the shift cipher
// description: Caesar cipher
// details : Caesar cipher is a type of substitution cipher in which each letter in the plaintext is shifted a certain number of places down the alphabet.

Check failure on line 3 in cipher/caesar/caesar.go

View workflow job for this annotation

GitHub Actions / Code style and tests

File is not `gofmt`-ed with `-s` (gofmt)
// time complexity: O(n)
// space complexity: O(n)
// ref: https://en.wikipedia.org/wiki/Caesar_cipher
package caesar

Expand Down
4 changes: 4 additions & 0 deletions cipher/diffiehellman/diffiehellmankeyexchange.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Package diffiehellman implements Diffie-Hellman Key Exchange Algorithm
// description: Diffie-Hellman key exchange
// details : Diffie-Hellman key exchange is a method of securely exchanging cryptographic keys over a public channel by combining private keys of two parties to generate a shared secret key.
// time complexity: O(log(n))
// space complexity: O(1)
// for more information watch : https://www.youtube.com/watch?v=NmM9HA2MQGI
package diffiehellman

Expand Down
196 changes: 196 additions & 0 deletions cipher/dsa/dsa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
dsa.go
description: DSA encryption and decryption including key generation
details: [DSA wiki](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm)
author(s): [ddaniel27](https://github.com/ddaniel27)
*/
package dsa

import (
"crypto/rand"
"io"
"math/big"
)

const (
numMRTests = 64 // Number of Miller-Rabin tests
L = 1024 // Number of bits in p
N = 160 // Number of bits in q
)

type (
// parameters represents the DSA parameters
parameters struct {
P, Q, G *big.Int
}

// dsa represents the DSA
dsa struct {
parameters
pubKey *big.Int // public key (y)
privKey *big.Int // private key (x)
}
)

// New creates a new DSA instance
func New() *dsa {
d := new(dsa)
d.dsaParameterGeneration()
d.keyGen()
return d
}

// Parameter generation for DSA
// 1. FIPS 186-4 specifies that the L and N values must be (1024, 160), (2048, 224), or (3072, 256)
// 2. Choose a N-bit prime q
// 3. Choose a L-bit prime p such that p-1 is a multiple of q
// 4. Choose an integer h randomly from the range [2, p-2]
// 5. Compute g = h^((p-1)/q) mod p
// 6. Return (p, q, g)
func (dsa *dsa) dsaParameterGeneration() {
var err error
p, q, bigInt := new(big.Int), new(big.Int), new(big.Int)
one, g, h := big.NewInt(1), big.NewInt(1), big.NewInt(2)
pBytes := make([]byte, L/8)

// GPLoop is a label for the loop
// We use this loop to change the prime q if we don't find a prime p
GPLoop:
for {
// 2. Choose a N-bit prime q
q, err = rand.Prime(rand.Reader, N)
if err != nil {
panic(err)
}

for i := 0; i < 4*L; i++ {
// 3. Choose a L-bit prime p such that p-1 is a multiple of q
// In this case we generate a random number of L bits
if _, err := io.ReadFull(rand.Reader, pBytes); err != nil {
panic(err)
}

// This are the minimum conditions for p being a possible prime
pBytes[len(pBytes)-1] |= 1 // p is odd
pBytes[0] |= 0x80 // p has the highest bit set
p.SetBytes(pBytes)

// Instead of using (p-1)%q == 0
// We ensure that p-1 is a multiple of q and validates if p is prime
bigInt.Mod(p, q)
bigInt.Sub(bigInt, one)
p.Sub(p, bigInt)
if p.BitLen() < L || !p.ProbablyPrime(numMRTests) { // Check if p is prime and has L bits
continue
}

dsa.P = p
dsa.Q = q
break GPLoop
}
}

// 4. Choose an integer h randomly from the range [2, p-2]. Commonly, h = 2
// 5. Compute g = h^((p-1)/q) mod p. In case g == 1, increment h until g != 1
pm1 := new(big.Int).Sub(p, one)

for g.Cmp(one) == 0 {
g.Exp(h, new(big.Int).Div(pm1, q), p)
h.Add(h, one)
}

dsa.G = g
}

// keyGen is key generation for DSA
// 1. Choose a random integer x from the range [1, q-1]
// 2. Compute y = g^x mod p
func (dsa *dsa) keyGen() {
// 1. Choose a random integer x from the range [1, q-1]
x, err := rand.Int(rand.Reader, new(big.Int).Sub(dsa.Q, big.NewInt(1)))
if err != nil {
panic(err)
}

dsa.privKey = x

// 2. Compute y = g^x mod p
dsa.pubKey = new(big.Int).Exp(dsa.G, x, dsa.P)
}

// Sign is signature generation for DSA
// 1. Choose a random integer k from the range [1, q-1]
// 2. Compute r = (g^k mod p) mod q
// 3. Compute s = (k^-1 * (H(m) + x*r)) mod q
func Sign(m []byte, p, q, g, x *big.Int) (r, s *big.Int) {
// 1. Choose a random integer k from the range [1, q-1]
k, err := rand.Int(rand.Reader, new(big.Int).Sub(q, big.NewInt(1)))
if err != nil {
panic(err)
}

// 2. Compute r = (g^k mod p) mod q
r = new(big.Int).Exp(g, k, p)
r.Mod(r, q)

// 3. Compute s = (k^-1 * (H(m) + x*r)) mod q
h := new(big.Int).SetBytes(m) // This should be the hash of the message
s = new(big.Int).ModInverse(k, q) // k^-1 mod q
s.Mul(
s,
new(big.Int).Add( // (H(m) + x*r)
h,
new(big.Int).Mul(x, r),
),
)
s.Mod(s, q) // mod q

return r, s
}

// Verify is signature verification for DSA
// 1. Compute w = s^-1 mod q
// 2. Compute u1 = (H(m) * w) mod q
// 3. Compute u2 = (r * w) mod q
// 4. Compute v = ((g^u1 * y^u2) mod p) mod q
// 5. If v == r, the signature is valid
func Verify(m []byte, r, s, p, q, g, y *big.Int) bool {
// 1. Compute w = s^-1 mod q
w := new(big.Int).ModInverse(s, q)

// 2. Compute u1 = (H(m) * w) mod q
h := new(big.Int).SetBytes(m) // This should be the hash of the message
u1 := new(big.Int).Mul(h, w)
u1.Mod(u1, q)

// 3. Compute u2 = (r * w) mod q
u2 := new(big.Int).Mul(r, w)
u2.Mod(u2, q)

// 4. Compute v = ((g^u1 * y^u2) mod p) mod q
v := new(big.Int).Exp(g, u1, p)
v.Mul(
v,
new(big.Int).Exp(y, u2, p),
)
v.Mod(v, p)
v.Mod(v, q)

// 5. If v == r, the signature is valid
return v.Cmp(r) == 0
}

// GetPublicKey returns the public key (y)
func (dsa *dsa) GetPublicKey() *big.Int {
return dsa.pubKey
}

// GetParameters returns the DSA parameters (p, q, g)
func (dsa *dsa) GetParameters() parameters {
return dsa.parameters
}

// GetPrivateKey returns the private Key (x)
func (dsa *dsa) GetPrivateKey() *big.Int {
return dsa.privKey
}
114 changes: 114 additions & 0 deletions cipher/dsa/dsa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package dsa_test

import (
"math/big"
"testing"

"github.com/TheAlgorithms/Go/cipher/dsa"
)

func TestDSA(t *testing.T) {
tests := []struct {
name string
message string
alter bool
want bool
}{
{
name: "valid signature",
message: "Hello, world!",
alter: false,
want: true,
},
{
name: "invalid signature",
message: "Hello, world!",
alter: true,
want: false,
},
}
// Generate a DSA key pair
dsaInstance := dsa.New()
pubKey := dsaInstance.GetPublicKey()
params := dsaInstance.GetParameters()
privKey := dsaInstance.GetPrivateKey()

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

// Sign the message
r, s := dsa.Sign(
[]byte(tt.message),
params.P,
params.Q,
params.G,
privKey,
)

if tt.alter {
// Alter the signature
r.Add(r, big.NewInt(1))
}

// Verify the signature
if got := dsa.Verify(
[]byte(tt.message),
r,
s,
params.P,
params.Q,
params.G,
pubKey,
); got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}

/* ------------------- BENCHMARKS ------------------- */
func BenchmarkDSANew(b *testing.B) {
for i := 0; i < b.N; i++ {
dsa.New()
}
}

func BenchmarkDSASign(b *testing.B) {
dsaInstance := dsa.New()
params := dsaInstance.GetParameters()
privKey := dsaInstance.GetPrivateKey()
for i := 0; i < b.N; i++ {
dsa.Sign(
[]byte("Hello, World!"),
params.P,
params.Q,
params.G,
privKey,
)
}
}

func BenchmarkDSAVerify(b *testing.B) {
dsaInstance := dsa.New()
pubKey := dsaInstance.GetPublicKey()
params := dsaInstance.GetParameters()
privKey := dsaInstance.GetPrivateKey()
r, s := dsa.Sign(
[]byte("Hello, World!"),
params.P,
params.Q,
params.G,
privKey,
)
for i := 0; i < b.N; i++ {
dsa.Verify(
[]byte("Hello, World!"),
r,
s,
params.P,
params.Q,
params.G,
pubKey,
)
}
}
4 changes: 4 additions & 0 deletions cipher/polybius/polybius.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Package polybius is encrypting method with polybius square
// description: Polybius square
// details : The Polybius algorithm is a simple algorithm that is used to encode a message by converting each letter to a pair of numbers.
// time complexity: O(n)
// space complexity: O(n)
// ref: https://en.wikipedia.org/wiki/Polybius_square#Hybrid_Polybius_Playfair_Cipher
package polybius

Expand Down
6 changes: 6 additions & 0 deletions cipher/railfence/railfence.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// railfence.go
// description: Rail Fence Cipher
// details: The rail fence cipher is a an encryption algorithm that uses a rail fence pattern to encode a message. it is a type of transposition cipher that rearranges the characters of the plaintext to form the ciphertext.
// time complexity: O(n)
// space complexity: O(n)
// ref: https://en.wikipedia.org/wiki/Rail_fence_cipher
package railfence

import (
Expand Down
Loading
Loading