viernes, 31 de octubre de 2014

Socket-based Repository

Implement a socket-based key exchange that employs RSA-based digital signatures.


This socket based repository of public keys employs the RSA algorithm to provide access only to the registered users. They can request the public key of another person and then encrypt it, first applying the digital signature, and finally encrypt it with the receiver's public key.

Each time the client establishes a connection with the server, the server is challenged, that is the client sends a value to the server and the server modifies it, then, this modified value is encrypted with the server's private key (digital signature) and sent to the client. The client must also modify the value. If the decrypted value matches it, the connection is accepted, that is one-way authentication. When the client is already registered and the requested action (a query or modify a key) requires a connection with the server, the client is also challenged, that is two-way authentication.

Main functions and classes

The function istheserver(clientobject) verifies the authenticity of the server:
def istheserver(cl):
   e = 670655
   n = 4560161
   x = randint(2, n)
   efxs = int(cl.ssendrecv(str(x)))
   fxs = modExp(efxs, e, n) # Decrypt fxs                                  
   fxc = (x**2 + 25) % n # f(x) Challenge                                  
   print "Fx Server:", fxs
   print "Fx Client:", fxc
   if fxs == fxc:
      print "This is the server."
      return True
   else:
      print "This is not the server."
      return False


The function istheclient(clientobject, purpose) verifies the authenticity of the client:
def istheclient(cl, p):                                                    
   email = raw_input("Enter your email:")                                  
   if not exists(email):                                                   
      print "First of all try 'client.py -k' option."                      
      return False                                                         
   else:
      f = open(email + '/id_rsa', 'r')
      privatekey = f.readline().split(";")
      d = int(privatekey[0])
      n = int(privatekey[1])
      x = int(cl.ssendrecv("c" + p + ";" + email))
      if x != 0: # If client exists                                        
         fx = (x**3 + 2) % n # f(x) Challenge                              
         efx = modExp(fx, d, n) # Signature                                
         print efx
         isit = cl.ssendrecv(str(efx))
         if isit[0] == '1': # Si aprobo este cliente                       
            return True
         else: #Si no aprobo                                               
            return False
      else:
         print "Email not registered."
         return False

The sockets in the client side are manipulated with the methods of this class:
class client():
   def __init__(self):
      self._s = socket.socket() # Socket object                            
      self._host = socket.gethostname() #The server ip, localhost for testing                                                                        
      self._port = 9000 # Defines the port                                 

   def sconnect(self):
      self._s.connect((self._host, self._port)) # Tries to connect         

   def ssendrecv(self, option): # Send a string and receive an answer      
      self._s.send(option)
      self._received = self._s.recv(1024)
      print self._received
      return self._received

   def sclose(self):
      self._s.close() # Close the connection

The server script

The server accepts multiple socket based connections using the threading module.
class c_thread(threading.Thread):
   def __init__(self, client, ip):
      threading.Thread.__init__(self)
      self._client = client
      self._ip = ip

   def run(self):
      try:
         self._rcvd = self._client.recv(1024)
         print self._rcvd
         self._client.send(serverchallenge(self._rcvd))

         self._action = self._client.recv(1024)
         if self._action[0] == 'r':
            self._client.send(register(self._action))

         elif self._action[0] == 'c' and c_thread.clientchallenge(self):
            if self._action[1] == 'q':
               emailr = self._client.recv(1024)
               self._client.send(request(emailr))
            elif self._action[1] == 'm':
               l = self._action.split(";")
               info = self._client.recv(1024)
               self._client.send(modify(l[1], info))

The run() method determines the action to be performed.

The client script

Easy of use

The client script is supposed to be easy of use, when the user runs this script without arguments the help is displayed. The help is also displayed using -h.

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -h
Options:
   -k Runs the RSA algorithm to create a Public Key and a Private Key.
   -r Register a user in the repository.
   -q Request someone's public key.
   -c Modify a public key in the repository.
   -e Encrypt a message (digital signature, receiver's public key).
   -d Decrypt a message.
   -m Convert a decrypted string to a readable message. You must get the string using this script. This option is used in -d.

Subscription 

The user is registered in the repository using ./client -r. The prompt requests the email (identifier) and the public key (e and n). The server is challenged in this option.

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -r
2376580
Fx Server: 2685748
Fx Client: 2685748
This is the server.
Email:jaime
Public Key:
e:5345
n:23454
Registered!


The key can be created before the user is registered. Using -k the RSA algorithm creates the keys for a given user.

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -k
Your email: victor
Public Key: (67967, 355613L)
Private Key: (144215L, 355613L)
In the public key the first value is e and the second is n.
Don't share your private key. The first value is d. The second is n.

Then, when the option -r is used, the prompt doesn't ask for a public key, automatically it is read from the id_rsa.pub file in the directory of the user, named as the given email.

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -r
1522324
Fx Server: 164055
Fx Client: 164055
This is the server.
Email:victor
Registered!

Query 

The query starts with the argument -q. The server and client are challenged. The prompt asks for the email of the user who requests and the script reads the private key of the user in the file “user/id_rsa”. If the tests are passed, the server sends the public key of the requested user and this information is stored in a hidden directory “.user” in a file named “.id_rsa.pub”, as a client-side cache.
Basically, the option -q calls this function:
def query():
   cl = client()
   cl.sconnect()
   if istheserver(cl) and istheclient(cl, 'q'):
      emailr = raw_input("Email requested:")
      user = cl.ssendrecv(emailr)
      if user[0] == '0':
         print "Email is not registered."
      else:
         saveuser(user)
   else:
      print "Failed."
   cl.sclose()

Example:

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -q
2183813
Fx Server: 495314
Fx Client: 495314
This is the server.
Enter your email:victor.riosm@live.com
155575
263713
1:The user is valid.
Email requested:leonardo
leonardo;295067;433793

If the requesting user doesn't exists: 

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -q
776381
Fx Server: 3868154
Fx Client: 3868154
This is the server.
Enter your email:leon
First of all try 'client.py -k' option.
Failed.

If the requested user does not exists:

 victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -q
2714392
Fx Server: 4086458
Fx Client: 4086458
This is the server.
Enter your email:victor.riosm@live.com
82302
148638
1:The user is valid.
Email requested:leon
0
Email is not registered.

Encryption 

The encryption uses digital signatures and public key encryption. The sender's private key is used for the digital signature and the receiver's public key is used to encrypt the message. If the receiver's public key had been requested previously then the prompt won't ask for it. All the ASCII characters can be encrypted.

This is the part of the code that encrypts the message, also applies the digital signature:
   message = raw_input("Enter the message to send >> ")
   nmessage = ''
   for char in message:
      nmessage += str(ord(char)).zfill(3)
   encrypted = ''
   block = 3
   stringnumber = ''
   i = 0
   for char in nmessage:
      stringnumber += char
      i += 1
      if i == block:
         number = int(stringnumber)
         stringnumber = ''
         i = 0
         signature = modExp(number, ds, ns)
         encrypted += str(modExp(signature, er, nr)).zfill(8)
   try:
      number = int(stringnumber)
      signature = modExp(number, ds, ns)
      encrypted += str(modExp(signature, er, nr)).zfill(8)
   except:
      print 'Encrypted'
   print "Encrypted message:", encrypted


Usage:

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -e
Enter your email >> leonardo
Enter the receiver email >> juan
Enter the message to send >> Leonardo used digital signatures and then encrypted this message using Juan's public key :)
Encrypted
Encrypted message: 00246575003086040015291200644751003507420052428500201583001529120048878800422626002626190030860400201583004887880020158300128454006464010012845400531802003507420038172500488788002626190012845400646401006447510035074200531802004226260052428500308604002626190048878800350742006447510020158300488788005318020041644200308604006447510048878800308604006447510039480200524285006500280048529200531802003086040020158300488788005318020041644200128454002626190048878800232354003086040026261900262619003507420064640100308604004887880042262600262619001284540064475100646401004887880047461700422626003507420064475100349729002626190048878800485292004226260010520200381725001284540039480200488788004068350030860400650028004887880016435900009845


Decryption 

The decryption first takes the encrypted message and decrypts it using the receiver's private key. Then the sender's public key is used to verify the digital signature. This key can be read directly from the file created in a query.

The code for decrypting the ciphertext:

   decrypted = ''
   block = 8 # the number of characters in the encrypted string
   stringnumber = ''
   signature = ''
   i = 0
   try:
      for char in encrypted:
         stringnumber += char
         i += 1
         if i == block:
            number = int(stringnumber)
            stringnumber = ''
            i = 0
            signature = modExp(number, dr, nr)
            decrypted += str(modExp(signature, es, ns)).zfill(3)
      try:
         number = int(stringnumber)
         signature = modExp(number, dr, nr)
         decrypted += str(modExp(signature, es, ns)).zfill(3)
      except:
         print 'Decrypted'
      print "Decrypted:", decrypted
      message = getmessage(decrypted)
      return message
   except:
      return "Could not be decrypted"


Usage:

victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -d
Ciphertext >> 00246575003086040015291200644751003507420052428500201583001529120048878800422626002626190030860400201583004887880020158300128454006464010012845400531802003507420038172500488788002626190012845400646401006447510035074200531802004226260052428500308604002626190048878800350742006447510020158300488788005318020041644200308604006447510048878800308604006447510039480200524285006500280048529200531802003086040020158300488788005318020041644200128454002626190048878800232354003086040026261900262619003507420064640100308604004887880042262600262619001284540064475100646401004887880047461700422626003507420064475100349729002626190048878800485292004226260010520200381725001284540039480200488788004068350030860400650028004887880016435900009845
Enter your email >> juan
Enter sender's email >> leonardo
Decrypted
Decrypted: 076101111110097114100111032117115101100032100105103105116097108032115105103110097116117114101115032097110100032116104101110032101110099114121112116101100032116104105115032109101115115097103101032117115105110103032074117097110039115032112117098108105099032107101121032058041
Leonardo used digital signatures and then encrypted this message using Juan's public key :)

If the ciphertext is decrypted using an incorrect private key:
victor@victor-HP-G42-Notebook-PC:~/Documents/Homework/Cryptography/rsasockets$ ./client.py -d
Ciphertext>> 00246575003086040015291200644751003507420052428500201583001529120048878800422626002626190030860400201583004887880020158300128454006464010012845400531802003507420038172500488788002626190012845400646401006447510035074200531802004226260052428500308604002626190048878800350742006447510020158300488788005318020041644200308604006447510048878800308604006447510039480200524285006500280048529200531802003086040020158300488788005318020041644200128454002626190048878800232354003086040026261900262619003507420064640100308604004887880042262600262619001284540064475100646401004887880047461700422626003507420064475100349729002626190048878800485292004226260010520200381725001284540039480200488788004068350030860400650028004887880016435900009845
Enter your email >> usuario
Enter sender's email >> leonardo
Decrypted
Decrypted: 250380167016265627762053536352041292251712656271772342085651664831670162251711772342251713316883622013316882815913536353865811772341664833316883622017620535363528159120856520412916701616648317723435363576205225171177234281591191055167016762051772341670167620531237204129225642795628159116701622517117723428159119105533168816648317723421523716701616648316648335363536220116701617723420856516648333168876205362201177234458220856535363576205417320166483177234279562085651109403865813316883123717723427579516701622564177234161711345913
Could not be decrypted



Other functions 

The arguments -c and -m modifies a public key in the repository and converts a decrypted string in a readable message (ASCII), respectively.

Converting a message

A decrypted ciphertext is automatically converted to a readable message calling the next function:
def getmessage(decrypted): 
   # Converts a decrypted value to a readable message
   message = ''
   i = 0 
   block = 3
   caracter = ''
   for char in decrypted:
      caracter += char
      i += 1
      if i == block:
         i = 0
         message += chr(int(caracter))
         caracter = ''
   try:
      message += chr(int(caracter))
   except:
      'Decrypted'
   return message



Code

The scripts are in this directory.
Github repository

References   

1 comentario:

  1. + in English
    + one-way auth.
    + two-way auth.
    + subscription
    + query
    + ease of use
    + use cases / example runs
    + automated key mgmt
    + encryption
    + decryption

    Putting keys in the source code is a very bad practise.
    “Easy of use” -> “Ease of use” or “Easy to use”

    10 out of 10

    ResponderEliminar