Completely Confused

Hi, I have taken one previous course in C++ which is all the experience I have. My new class assigned this as a first assignment. I am not asking for the answer but I am just asking if someone can help me understand what I need to do, this is nothing like my previous assignments. I apologize in advance for how long the post is, a lot of it is pseudocode.


Overview
The purpose of this exercise is to introduce several fundamental programming concepts through the implementation/use of various encryption algorithms/transforms. Students will first design a front end interface which provides flexibility to support a variety of different algorithms. These algorithms will range from a contrived shift cipher to an AES-256 block cipher (a standard currently approved to secure TOP SECRET government data). Finally, the student will be tasked with implementing an RSA asymmetric encryption system and integrating that into the common project/interface.
First Cryptographic Algorithm
The first cryptographic algorithm to be implemented is a trivial shift cipher, (also known as a Caesar Cipher due to its use by the famous Roman emperor Julius Caesar). The details of this algorithm are available here: https://en.wikipedia.org/wiki/Caesar_cipher. While this transform provides virtually no confidentiality to the user, it will serve as an easily debugged transform to ensure that the interface functions as expected and can serve as a robust home for much more complicated transforms. Unlike the traditional cipher, the produced implementation must work on all possible data streams, not just alpha numeric strings.
Programming Concepts
To correctly complete this programming task, the student must possess an understanding of basic Linux system operation, inheritance, polymorphism, interfaces, abstract classes, dynamic linking, functional objects/lambdas, and basic memory management.
_____________________________________________________________________________
GIVEN CODE:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#ifndef CRYPTO_HPP
#define CRYPTO_HPP

#include <functional>
#include <memory>

/**
 * @enum Algorithm
 *
 * The purpose of this enumeration is to inform the
 * static factory method which crypto transform should be
 * used for when creating a crypto object.
 */
enum class Algorithm
{
	eNONE,
	eCAESAR,
	eAES,
	eRSA,
};

/**
 * @class Crypto
 *
 * The purpose of this class is to serve as an abstract base class for many
 * cryptologic transforms.
 */
class Crypto
{
public:
	/**
	 * @brief Constructor
	 *
	 * This function creates/initializes this oject.
	 * The callbacks are used by the underlying
	 * algorithm to pass back processed data to the using logic.
	 *
	 * NOTE: There is no correlation between the amount of data passed into the algorithm
	 * and the amount returned from it.  Likewise, there is no required correlation between
	 * the number of times data is passed into the algorithm and the number of times callbacks
	 * are called.
	 *
	 * @param encryptCallback Callback function for returning encrypted data
	 * @param decryptCallback Callback function for returning decrypted data
	 */
	Crypto(std::function<void(const uint8_t *data, uint32_t len)> encryptCallback,
		std::function<void(const uint8_t *data, uint32_t len)> decryptCallback);
	/**
	 * @brief Generate new crypto keys
	 *
	 * This function must be implemented by all derived classes.
	 * It signals the underlying algorithm that it should generate
	 * and store keys needed for that algorithm.
	 */
	virtual void genKeys() = 0;
	/**
	 * @brief Get the keys and return them in a standardized form
	 *
	 * This function must be implemented by all derived classes.
	 * It will allocate memory, and then populate that memory with
	 * keys in a way that can be generically stored/passed by using
	 * programs.
	 *
	 * @param pubKey Pointer to array of bytes containing public key
	 * @param pubLen Lenght of the public key
	 * @param priKey Pointer to array of bytes containing private key
	 * @param priLen Lenght of the private key
	 * @return Indication of valid keys present in response
	 */
	virtual bool getKeys(uint8_t **pubKey, uint32_t &pubLen,
		uint8_t **priKey, uint32_t &priLen) = 0;
	/**
	 * @brief Set the keys to be used in underlying transform
	 *
	 * This function must be implemented by all derived classes.
	 * It will pass a representation of needed keys to derived
	 * algorithms.
	 *
	 * @param pubKey Pointer to bytes containing public key
	 * @param pubLen Lenght of the public key
	 * @param priKey Pointer to bytes containing private key
	 * @param priLen Lenght of the private key
	 */
	virtual void setKeys(const uint8_t *pubKey, uint32_t pubLen,
		const uint8_t *priKey, uint32_t priLen) = 0;
	/**
	 * @brief Destroy keys and free resources
	 *
	 * This function must be implemented by all derived classes.
	 * It will inform the derived algorithm that it should "forget"
	 * any created keys and free resources associated with them.
	 * If keys are not present, it should have no action.
	 */
	virtual void destroyKeys() = 0;
	/**
	 * @brief Encrypt raw data buffer (PT to CT)
	 *
	 * This function will pass a data buffer for encyrption
	 * to the underlying algorithm.  There should be no restrictions
	 * on the lenght of data that is passed to the algorithm.
	 *
	 * @param data Buffer of data to be encrypted
	 * @param len Length of data buffer
	 * @return bool Success or failure (transform accepted data)
	 */
	virtual bool encrypt(const uint8_t *data, uint32_t len) = 0;
	/**
	 * @brief Decrypt raw data buffer (CT to PT)
	 *
	 * This function will pass a data buffer for decyrption
	 * to the underlying algorithm.  There should be no restrictions
	 * on the lenght of data that is passed to the algorithm.
	 *
	 * @param data Buffer of data to be decrypted
	 * @param len Length of data buffer
	 * @return bool Success or failure (transform accepted data)
	 */
	virtual bool decrypt(const uint8_t *data, uint32_t len) = 0;
	/**
	 * @brief Create a Crypto object using the correct transform
	 *
	 * @param encryptCallback Callback function for returning encrypted data
	 * @param decryptCallback Callback function for returning decrypted data
	 * @param algorithm Enum indicationg which transform should be used
	 * @return shared_ptr to newly constructed heap object
	 */
	static std::shared_ptr<Crypto> cryptoFactory(std::function<void(const uint8_t *data, uint32_t len)> encryptCallback,
		std::function<void(const uint8_t *data, uint32_t len)> decryptCallback,
		Algorithm algorithm);
protected:
	/** @brief Encrypt callback function */
	std::function<void(const uint8_t *data, uint32_t len)> m_encryptCallback;
	/** @brief Decrypt callback function */
	std::function<void(const uint8_t *data, uint32_t len)> m_decryptCallback;
};

#endif /* CRYPTO_HPP */ 

Last edited on
Search up C++ polymorphism.
you may not be ready for this depending on your one and only class.
Is this intended to be your 2nd programming class?!
I'm not sure if I like the fact that the class is trying to teach cryptographic algorithms while also trying to force in a bunch of unnecessary features like callbacks/factories combined with std::function, std::shared_ptr and polymorphism. The class should either focus on the cryptography, or focus on the advanced C++ features.

As poteto alluded to, the Crypto base class is meant as a polymorphic base class.
This means that you need to inherit from it and then override its virtual functions with your own implementations.

Your first assignment is just to implement the Caesar shift cipher, so make a class called CaesarShift or something like that, and have it inherit from Crypto.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Example program
#include <iostream>
#include <string>
#include <cstdint>
#include <memory>
#include <vector>

class Crypto {
  public:
    virtual bool encrypt(const uint8_t *data, uint32_t len) = 0;
    
    virtual ~Crypto() = default;
};

class CaesarShift : public Crypto {
  public:
    bool encrypt(const uint8_t *data, uint32_t len) override
    {
        // encrypting a copy of the data. not sure if that's what's intended.
        std::vector<uint8_t> data_vec(data, data + len);
        uint8_t key = 3; // TODO: use the setKeys method for setting keys
        std::cout << "In CaesarShift::encrypt\n";
        for (uint32_t i = 0; i < len; i++)
        {
            data_vec[i] += key; // not sure if this is supposed to deal with all bytes or just alphabetic characters
            // If it's only alphabetic characters you need to wrap it mod 26 most likely.
        }
        return true;
    }
};

int main()
{
    // You probably should be using the "factory" method with callbacks here
    std::unique_ptr<Crypto> caesar = std::make_unique<CaesarShift>();
    
    uint8_t data[5] = { 'h', 'e', 'l', 'l', 'o' };
    
    caesar->encrypt(data, 5);
}


It looks like you'll also need to use callbacks to rely the information back to the user?
Here's some examples of callbacks:
https://stackoverflow.com/questions/12338695/c11-styled-callbacks
https://www.wisol.ch/w/articles/2015-03-08-callbacks-in-cpp11/
Last edited on
Okay I am going to look more into Polymorphism, and yes my first course was just like general C++ which I understood far more than this stuff. But now this new professor is throwing things in like callbacks and whatever this is "(uint8_t **pubKey, uint32_t &pubLen,
uint8_t **priKey, uint32_t &priLen)." Is this just two unsigned ints?

Thanks for the example Ganado, it looks more like what I'm used to seeing so I might have a starting point. I will read through those examples and see what I can figure out.

Thanks for all the replies. If I'm still super stuck after class today I'll check back here, so far it's more instructive than my class. :)

Last edited on
Update: The professor sent out a testing file which looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "Crypto.hpp"
#include <iostream>
#include <string>
#include <sstream>


std::ostringstream ctOss;
std::ostringstream ptOss;

void ctCallback(const uint8_t *data, uint32_t len)
{
  ctOss.write((char *)data, len);
}

void ptCallback(const uint8_t *data, uint32_t len)
{
  ptOss.write((char *)data, len);
}

int main()
{

  //None test
  ctOss.str("");
  ptOss.str("");
  auto pCrypto = Crypto::cryptoFactory(ctCallback, ptCallback, Algorithm::eNONE);
  pCrypto->genKeys();
  std::string test = "Testing NONE transform\n";
  pCrypto->encrypt((uint8_t *)test.c_str(), test.size()); 
  pCrypto->encrypt(nullptr, 0); 
  std::cout << "CT: " << ctOss.str() << std::endl;  
  pCrypto->decrypt((uint8_t *)(ctOss.str().c_str()), ctOss.str().size()); 
  std::cout << "PT: " << ptOss.str() << std::endl;  

#if 1
  //Caeser test
  ctOss.str("");
  ptOss.str("");
  pCrypto = Crypto::cryptoFactory(ctCallback, ptCallback, Algorithm::eCAESAR);
  pCrypto->genKeys();
  test = "Testing CAESER transform\n";
  pCrypto->encrypt((uint8_t *)test.c_str(), test.size()); 
  pCrypto->encrypt(nullptr, 0); 
  std::cout << "CT: " << ctOss.str() << std::endl;  
  pCrypto->decrypt((uint8_t *)(ctOss.str().c_str()), ctOss.str().size()); 
  std::cout << "PT: " << ptOss.str() << std::endl;  
#endif

}
I can't say I disagree with the "dive into the deeper end of the pool to see if you drown" approach, but it is likely to be brutal for most.

@Gandado and @poteto have you covered on the primary inquiry, but for your most recent, I'll focus on:

(uint8_t **pubKey, uint32_t &pubLen, uint8_t **priKey, uint32_t &priLen)

This isn't "just" two unsigned ints. It is, however, the signature of a strange mixture of very old "C" style and the C++ (due to the references).

The overall intent, it seems, is that pubKey and priKey ultimately represent two arrays of unsigned 8 bit characters, each with a length expressed by the related pubLen and priLen. So, no, it isn't just two unsigned ints, but two unsigned 8 bit arrays of undetermined length.

That said, the use of "**" (sometimes, in this context, said to be a pointer to a pointer) is intended to allow the function this declares to allocate the array to be returned. To do that they use the "C" style of "**", such that the function can assign the calling code's pointer when the array is allocated, where it is thus intended that the caller will provide the address of an uin8_t *, perhaps something like:

1
2
3
4
5
6
uint8_t * pubkey{};
uint32_t publen{};
uint8_t * prikey{};
uint32_t  prilen {};

f( &pubkey, publen, &prikey, prilen );


This assumes your signature if for the function "f".

Whatever "f" ultimately does, it can allocate and assign pubkey and prikey to new arrays, where their lengths are assigned to publen and prilen respectively.

It is very non-C++, even pre 21st century. You do occasionally find such material when using older libraries, which implies we don't have control or authorship of "f", but in this context it is a bit confusing to me because of the "&" used on the lengths. If this were genuinely a "C" function, those would be pointers to uint8_t types instead of references.

Even then, it would be simpler, conceptually, to use uint8_t *& for the array parameters, so as to avoid the double de-reference implication for the array assignments in the function, but that's a minor point.

The real flaw is that these parameter pairs aren't an object.

Which of these stated subject materials are not familiar to you?

basic Linux system operation
inheritance
polymorphism
interfaces
abstract classes
dynamic linking
functional objects/lambdas
basic memory management

...and how long do you have to complete the assignment?

As an early course assignment this sounds like the teacher wants a break, so he's dishing out something that would take a while, so he can nap through it.

That, or he's going to hope this causes several to give up and drop out of the course.

There's were I don't like the idea, because I'm not in favor of such things, but the practice of software development always has had an element of that "dive into the deep end of the pool", and it has never let up. We're frequently confronted with new, complicated, intertwined problems to solve, and getting everyone accustomed to that early on seems like a "boot camp" style exercise toward "the real thing".

_
Last edited on
@Niccolo,

You have a typo in line 4 of your code block.

To match the signature of the function, prilen should be a unit32_t, not a uint32_t*.

1
2
3
4
5
6
7
8
uint8_t * pubkey{};
uint32_t  publen{};
uint8_t * prikey{};
uint32_t  prilen {};

f( &pubkey, publen, &prikey, prilen );

	

Hi Niccolo, I think I see what you're saying about the uin8_t and uint32_t which has just been super confusing to look at. I definitely am not familiar with dynamic linking, objects/lambdas and memory management. I have a little experience with Polymorphism mainly from an old assignment "polymorphic points, circles and cylinders." I wouldn't describe myself as completely comfortable with abstract classes though.
The assignment is due on monday the 16th.

The professor seems like a nice and helpful person, but his coding style is just far more advanced than what my previous course prepared me for. I'm gonna keep messing around with the suggested concepts and see if I can get anything. I'll also talk to him after class today.
That sounds like a lot to cram between now and the 16th.

I'm not sure why dynamic linking would come up. That's the practice of writing code that is compiled into a container (a library), which can be loaded during runtime. It has almost nothing to do with C++ itself.

Is all of that list actually required on this assignment?
Topic archived. No new replies allowed.