Question
File Structure: I only want one file for the entire program, so you can put all classes together with main() in one .txt file. (For
File Structure: I only want one file for the entire program, so you can put all classes together with main() in one .txt file. (For Option B, you would have two main()s, in the same file, which is fine.) You are welcome to organize your code in multiple files, but in that case append them all into a single .txt with your original files clearly delimited with horizontal lines and labels.
OPTION A: InternetUser, Communicator and Two Helper Classes
Understand the Classes and Problem
Every internet user -- perhaps better thought of as an Internet connection -- has certain data associated with him/her/it. We will oversimplify this by symbolizing such data with only two fields: a name ("Aristotle") and a globally accessible IP address("139.12.85.191"). We could enhance these by adding other -- sometimes optional, sometimes needed -- data (such as a MAC address, port, local IP address, gateway, etc), but we keep things simple and use only these two for the assignment -- any more would not satisfy spec. Also, name is not technically a true datum for this emulation, but we use it to signify the the English description of an individual or process that is interacting with the Internet.
Most internet users, however, need more -- they want to communicate securely with other internet users. For example, Bob (bob, "139.12.85.191") might want to order something from Amazon (amazon, "54.239.26.128"), a transaction that requires Bob to transmit a credit card number to Amazon over the Internet. Today, this is done using a two-key (public key + private key) system called an RSA cryptosystem. You don't see it, but it's always there. Such a special internet user could be called a communicator. To use RSA encryption, every internet user would need a pair of keys, the public-private key pair, above and beyond their basic name/ip address data. Everyone gets to see everyone else's public key, but the private key is only known to the one user: Bob's connection knows his private key, and Amazon's connection knows its private key. But Bob will know Amazon's public key and Amazon will know Bob's public key.
We will not be encrypting actual messages this week, but perhaps we will do so in a later assignment. For those of you interested in a more complete explanation of RSA encyption, you can start here:
Wikipedia page for RSA (cryptosystem)
Using OOP vocabulary and tools, a communicator would then consist of a normal internet user plus at least one other item: a key pair, that contains two keys: the public key and the private key.
Enter inheritance.
Two Helper Classes: IntPair and EncryptionSupport
I will supply you with two support classes, as follows.
Class IntPair
This class consists of two integers bundled together for easy handling. Examples of IntPair objects are (32, 195), (-983, 0) and (1000, 212078). As you can see, there is a first integer and a second integer, which we'll call firstInt and secondInt. Everything is public and no filtering so that our classes can get to the data easily and quickly. Details will be found in the text file to be downloaded, below.
Class EncryptionSupport
This class has no member data, only four static methods that will be used by our Communication class methods. Since they are general enough, we encapsulate them in a separate class. For example one of its methods, isPrime( long x), determines whether or not the passed parameter, x, is a prime number, a useful method for many other classes. The methods of the class can be examined in the source, but they need not be fully absorbed or analyzed for the purpose of this assignment. You can simply call them and expect that they do the right thing relative to the methods that you will write.
Both classes and the #includes for the assignment can be downloaded here:
zipped RSA support files
If the above link doesn't work, copy and paste this: http://fgamedia.org/faculty/loceff/cs_courses/cs_2b/resources/RsaEncryptionSupport_2B.zip
The Base Class: InternetUser
Your base class is named InternetUser.
Public Static Class Constants
Define a full set of limits and defaults, like MAX_NAME_LENGTH, MIN_IP_LENGTH , and DEFAULT_IP for both min, max lengths and default data of each member. names should be between 2 and 50 characters, and IP addresses between 7 and 15 characters. The default name should reveal an undefined string, and the default IP address should be "0.0.0.0", also an indicator of an error or undefined address to the user.
Private Member Data
string name; string ip;
Public Methods
Default and 2-parameter constructors.
Mutator and Accessor for each member.
a toString() method that provides a nicely formatted return string for potential screen I/O.
Private Methods
private static validation helpers to filter client parameters. These will support your public methods. You should only test lengths here, since we don't really need to check the number of periods or size of each of the four ints in an IP address. We'll let that slide for this assignment. (If you don't know what an IP address looks like, now's a good time to Google it. We're using IP4, not IP6, format.)
Client Test of Class InternetUser (first part of main())
Instantiate two or more InternetUser objects, some using the default constructor and some using the parameter-taking constructor. Mutate one or more of the members and, after that, use the toString() to assist a screen output so we can see what your objects contain. Next, test one or more accessors. Finally, call two or more mutators and constructors, providing both legal and illegal arguments and checking the return values (thus demonstrating that the mutators do the right thing). Here is a sample run from my test (but you will have to imagine/deduce the source I used and create your own source for your testing, which will produce a different output).
Example Run of InternetUser Class Testing
/* --------------------------------------------------------- Base Class Testing *********************** Name: (undefined) IP Aaddr: 0.0.0.0 Name: dns IP Aaddr: 75.75.75.75 Name: bad IP Aaddr: 0.0.0.0 success as expected --------------------------------------------------------- */
The Derived Class: Communicator
Your derived class is named Communicator. Communicator uses the two members already present in the base class; the Communicator's name is the name in the base class, and the ip of Communicator is also the base class InternetUser's ip. Do not try to store a Communicator's name or ip as new members of the derived class or you will get -20 points -- not desirable by you or me. Duplicating base class data in derived class means you do not understand inheritance, thus the significance of this kind of mistake.
Overview of RSA Encryption Keys and Class Communicator
The Fundamental Data
As we already learned, to encrypt a message, each InternetUser has to also be a Communicator, and to do that, the object has to have more than just a name and an ip; it has to have two encryption keys, a public key and a private key. So the most fundamental aspect of this class are those keys. Each key is a pair of integers, or in our current regime, each key is an IntPair member object:
IntPair publicKey; IntPair privateKey;
Two examples of such pairs are
publicKey: (23, 703) privateKey: (214, 703)
and
publicKey: (29, 957779) privateKey: (726591, 957779)
Notice two things about the pair:
The second number in the pair is the same, and
the first number in the pair is a prime number.
While the sameness of the second number is normally considered a bad duplication of data, we'll allow it in RSA emulation since it is part of the formal definition of the two keys and we want to parallel the math as closely as possible.
These two keys will be generated for us with the help of our EncryptionSupport class, so you needn't get too wrapped up in the meaning ... yet. For now, I just want you to understand what the main data for this derived class is, and you now know that. So we move on.
Client-Supplied Constructor or Mutator Values
The client does not actually provide the two integers for these keys. Instead, the client has to only do one thing: select and supply two distinct prime numbers. Any two relatively large primes will do. We'll call the two prime numbers, supplied by the client, p and q, informally, or as members firstPrime and secondPrime. So p = firstPrime and q = secondPrime. I'll use p and q informally when talking about the two primes, but they are the same as the members firstPrime and secondPrime.
Our constructors and mutators have to check that the client-supplied p and q arguments are prime and distinct and, if so, the constructors must generate the two encryption keys for us using those primes as a foundation. Neither prime is actually part of either encryption key.
It's easy to pick two primes. It's a little harder to produce legal public and private keys, which is why we allow the client to give us two random primes and then use those in our methods of turning them into two encryption keys, one public and one private. You'll see the two client-supplied primes, p and q, in the parameter list of some public methods. You'll also see some code that generates the encryption keys using p and q as a starting point. Read on.
Static Class Constants
To the existing base class statics, add two more in this derived class:
ERROR_FLAG_NUM - public. Make this equal 0.
MAX_PQ - private. It means the maximum of p as well as the maximum of q (our two members firstPrime and secondPrime). Although it's conceptually constant, some compilers can't take it as a const because it needs to come from a sqrt()method call. If your compiler allows, make it a public or private const, otherwise make it a normal private, assign it a value inside the class, and be careful to never change it. The value to be assigned by your class is the (positive) square root of the largest long in your computer. That can be found by taking the C++ constant symbol, LONG_MAX defined inside the #include
Additional Private Member Data
We already talked about these two:
IntPair publicKey; IntPair privateKey;
In addition, we need several support long ints:
long firstPrime; long secondPrime; long n, phi, e, d;
These will be helpful intermediate variables during the computation of the keys. Normally, we don't allow mere intermediate variables to be awarded the status of class member (see your CS 2A notes) but these have very special meanings and are intrinsic to the process of key encryption.
Public Methods
Four constructors. Use constructor chaining. Here are the signatures:
Communicator(); Communicator( long firstPrime, long secondPrime ); Communicator( string name, string ip ); Communicator( string name, string ip, long firstPrime, long secondPrime );
Those constructors that do not take numeric arguments and constructors that provide illegal numbers as reported by the mutator return value (see requirements of p, q, above) will assign ERROR_FLAG_NUM to all numeric members. But if legal primes are reported by the mutator nothing more need be done: the mutator will set all private data based on the setting of the two valid primes.
One Mutator for both primes. This is the only simple mutator, and it takes both primes, p and q, because they must be compared with each other. If good values are detected it sets firstPrime and secondPrime and goes on to call computeBothEncrKeys() to set the keys so they are compatible with these two new valid primes just set. Other additional sub-class members members cannot be mutated directly and are not to be readable by clients. You have a method that can be used as a validator already in your EncryptionSupport class, so use that rather than writing a new one. There is more to this than just calling that method. The description above clearly states what that remaining test for p and q is, and can be done in the mutator.
Accessors - one for each key. Only the encryption keys (which are IntPair type) have accessors. Accessors always return the type of the member.
toString() - a method that returns a string of the entire object's private data in a readable form. Make proper use of method chaining.
Private Methods
bool computeBothEncrKeys() - This is the method that turns the primes into encryption keys. It checks to see that the member primes are valid, and returns false if they are not, leaving data unaffected. If they are it takes the following steps (this is how RSA encryption key generation is accomplished):
Generate n = p * q;
Generate phi = (n) which is the number of primes smaller than n. (n) is called "Euler's totient function," and in the special case where n is the product of two primes (but not generally), (n) is simply (p - 1) * (q - 1). [This n becomes the basis for our modular arithmetic coming next, that is, we are about to compute values "mod n," a concept that only those of you who have had upper-division algebra will know. You do not need to know the details since the EncryptionSupport methods will do the work. Read on.]
Compute e. Select a random prime number that is relatively small: between 19 and 541 that has the property that it is LESS THAN phi and it DOES NOT DIVIDE EVENLY into phi. getSmallRandomPrime() in your EncryptionSupport class will give you a prime in the correct range, but you have to make sure that the two latter conditions are met and keep at it until they are, or after a reasonable attempt, you fail and. If you succeed, return true, else return false. This becomes your member e.
Compute d as the mod n multiplicative inverse of the e just computed. This is done for you by the method inverseModN(e, n) in your EncryptionSupport class.
Set the public key IntPair to (e, n) and the private key IntPair to (d, n).
Return true.
Client Test of Class Communicator (second part of main())
Instantiate four or more Communicator objects, testing each of the four constructors. Give both base class and derived class bad data to some of these. Confirm the operation by showing the objects immediatly. Next, mutate one or more of the objects by calling the one and only mutator, and re-displaying the results. Finally, test your accessors.
Example Test of Communicator Class (done in same run as overall program run)
Derived Class Constructor Testing *************** ---------------- Name: (undefined) IP Aaddr: 0.0.0.0 (p, q) n, phi, e, d: (0, 0) 0, 0, 0, 0 public key(0, 0) private key(0, 0) ---------------- Name: yan kam IP Aaddr: 127.90.32.14 (p, q) n, phi, e, d: (907, 1087) 985909, 983916, 499, 480112 public key(499, 985909) private key(480112, 985909) (more supplied by student) Derived and Base Class Mutator Testing ********** ---------------- Name: giral IP Aaddr: 0.0.0.0 (p, q) n, phi, e, d: (809, 821) 664189, 662560, 101, 203860 public key(101, 664189) private key(203860, 664189) (more supplied by student) Derived Class Accessor Testing *************** (0, 0) (0, 0) (480112, 985909) (499, 985909)
(more supplied by student)
(other tests supplied by student) --------------------------------------------------------- */
OPTION B1 - A Stack of InternetUsers (4 Points Extra Credit)
Complete, for submission, OPTION A. Add StackNode and Stack classes, exactly as presented in the Modules. Next, derive IuNode and IuStack classes in the same manner as we derived FloatNode and FloatStack in the modules, but making adjustments, as needed, to have the IuStack work with InternetUsers. Change most methods named showXYZ() by converting them to toString() so that the client, not the methods, does the output. The new methods merely format the strings in preparation for output.
In your client, after you have done the things from OPTION A, design a second main() to create an IuStack. Then push() various InternetUsers onto the stack
Finally, in a loop, pop() everything off the IuStack and print it out as you pop(). Go beyond the end of the Stack so you can confirm that your code does not break when you pop() things off an empty Stack.
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started