I am trying to write a C++ program for my Computer Machine Organization class in which I perform a memory dump in hex on some address stored in memory. I don't really understand what a memory dump is, and am pretty new to writing C++. My questions are:
How can I create a method that takes two arguments in which they specify address in memory?
How can I further modify those arguments to specify a word address that is exactly 4 bytes long?
How can I then convert those addresses into hex values?
I know that this is a lot, but thank you for any suggestions.
For anyone who needs it, here is my code so far:
#include <stdio.h>
// Create something to do the methods on
char array[3] = {'a', 'b', 'c'};
void mdump(char start, char end){
// Create pointers to get the address of the starting and ending characters
char* pointer1 = (char *)& start;
char* pointer2 = (char *)& end;
// Check to see if starting pointer is in lower memory than ending pointer
if(pointer1 < pointer2){
printf("Passed");
}
else{
printf("Failed");
}
// Modify both the arguments so that each of them are exactly 4 bytes
// Create a header for the dump
// Iterate through the addresses, from start pointer to end pointer, and produce lines of hex values
// Declare a struct to format the values
// Add code that creates printable ASCII characters for each memory location (print "cntrl-xx" for values 0-31, or map them into a blank)
// Print the values in decimal and in ASCII form
}
int main(){
mdump(array[0], array[2]);
return 0;
}
How to write a Hex dump tool while learning C++:
Start with something simple:
#include <iostream>
int main()
{
char test[32] = "My sample data";
// output character
std::cout << test[0] << '\n';
}
Output:
M
Live demo on coliru
Print the hex-value instead of the character:
#include <iostream>
int main()
{
char test[32] = "My sample data";
// output a character as hex-code
std::cout << std::hex << test[0] << '\n'; // Uh oh -> still a character
std::cout << std::hex << (unsigned)(unsigned char)test[0] << '\n';
}
Output:
M
4d
Live demo on coliru
Note:
The stream output operator for char is intended to print a character (of course). There is another stream output operator for unsigned which fits better. To achieve that it's used, the char has to be converted to unsigned.
But be prepared: The C++ standard doesn't mandate whether char is signed or unsigned—this decision is left to the compiler vendor. To be on the safe side, the 'char' is first converted to 'unsigned char' then converted to unsigned.
Print the address of the variable with the character:
#include <iostream>
int main()
{
char test[32] = "My sample data";
// output an address
std::cout << &test[0] << '\n'; // Uh oh -> wrong output stream operator
std::cout << (const void*)&test[0] << '\n';
}
Output:
My sample data
0x7ffd3baf9b70
Live demo on coliru
Note:
There is one stream output operator for const char* which is intended to print a (zero-terminated) string. This is not what is intended. Hence, the (ugly) trick with the cast to const void* is necessary which triggers another stream output operator which fits better.
What if the data is not a 2 digit hex?
#include <iomanip>
#include <iostream>
int main()
{
// output character as 2 digit hex-code
std::cout << (unsigned)(unsigned char)'\x6' << '\n'; // Uh oh -> output not with two digits
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< (unsigned)(unsigned char)'\x6' << '\n';
}
Output:
6
06
Live demo on coliru
Note:
There are I/O manipulators which can be used to modify the formatting of (some) stream output operators.
Now, put it all together (in loops) et voilà: a hex-dump.
#include <iomanip>
#include <iostream>
int main()
{
char test[32] = "My sample data";
// output an address
std::cout << (const void*)&test[0] << ':';
// output the contents
for (char c : test) {
std::cout << ' '
<< std::hex << std::setw(2) << std::setfill('0')
<< (unsigned)(unsigned char)c;
}
std::cout << '\n';
}
Output:
0x7ffd345d9820: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Live demo on coliru
Make it nice:
#include <algorithm>
#include <iomanip>
#include <iostream>
int main()
{
char test[32] = "My sample data";
// hex dump
const size_t len = sizeof test;
for (size_t i = 0; i < len; i += 16) {
// output an address
std::cout << (const void*)&test[i] << ':';
// output the contents
for (size_t j = 0, n = std::min<size_t>(len - i, 16); j < n; ++j) {
std::cout << ' '
<< std::hex << std::setw(2) << std::setfill('0')
<< (unsigned)(unsigned char)test[i + j];
}
std::cout << '\n';
}
}
Output:
0x7fffd341f2b0: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00
0x7fffd341f2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Live demo on coliru
Make it a function:
#include <algorithm>
#include <iomanip>
#include <iostream>
void hexdump(const char* data, size_t len)
{
// hex dump
for (size_t i = 0; i < len; i += 16) {
// output an address
std::cout << (const void*)&data[i] << ':';
// output the contents
for (size_t j = 0, n = std::min<size_t>(len - i, 16); j < n; ++j) {
std::cout << ' '
<< std::hex << std::setw(2) << std::setfill('0')
<< (unsigned)(unsigned char)data[i + j];
}
std::cout << '\n';
}
}
int main()
{
char test[32] = "My sample data";
std::cout << "dump test:\n";
hexdump(test, sizeof test);
std::cout << "dump 4 bytes of test:\n";
hexdump(test, 4);
std::cout << "dump an int:\n";
int n = 123;
hexdump((const char*)&n, sizeof n);
}
Output:
dump test:
0x7ffe900f4ea0: 4d 79 20 73 61 6d 70 6c 65 20 64 61 74 61 00 00
0x7ffe900f4eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dump 4 bytes of test:
0x7ffe900f4ea0: 4d 79 20 73
dump an int:
0x7ffe900f4e9c: 7b 00 00 00
Live demo on coliru
Note:
(const char*)&n may look a bit adventurous. In fact, conversion of pointers is always something which should be at best not necessary. However, for the dump tool this is the easiest way to access the bytes of arbitrary data. (This is one of the rare cases which is explicitly allowed by the standard.)
An even nicer hexdump can be found in
SO: How would I create a hex dump utility in C++?
(which I recommended OP beforehand).
I have written some code that loads some files containing a list of words (one word pr line). each word is added to a multiset. later I try to search the multiset with multiset.find("aWord"). where I look for the word and substrings of the word in the multiset.
This code works fine if I compile it with qt on a windows system.
But don't work if i compile it in qt on my mac !
my goal is to make it work from qt on my mac.
I am woking on macbook Air (13" early 2018) with a
macOS Majave version 10.14.4 instalation
Buil version 18E226
local 18.5.0 Darwin Kernel Version 18.5.0: Mon Mar 11 20:40:32 PDT
2019; root:xnu-4903.251.3~3/RELEASE_X86_64 x86_64
Using a qt installation:
QTKit:
Version: 7.7.3
Obtained from: Apple
Last Modified: 13/04/2019 12.11
Kind: Intel
64-Bit (Intel): Yes
Get Info String: QTKit 7.7.3, Copyright 2003-2012, Apple Inc.
Location: /System/Library/Frameworks/QTKit.framework
Private: No
And xcode installation:
Xcode 10.2
Build version 10E125
I have tried to print out:
every strings that i am searching for
and every string i should find in the multiset as hex format
and concluded that some of the letters do not match.
in there hex value. despite i think my whole system run utf-8 and the file also is utf-8 encoded.
Dictionary.h
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <iostream>
#include <vector>
#include <set>
class Dictionary
{
public:
Dictionary();
void SearchForAllPossibleWordsIn(std::string searchString);
private:
std::multiset<std::string, std::less<std::string>> mDictionary;
void Initialize(std::string folder);
void InitializeLanguage(std::string folder, std::string languageFileName);
};
#endif // DICTIONARY_H
Dictionary.cpp
#include "Dictionary.h"
#include <vector>
#include <set>
#include <iostream>
#include <fstream>
#include <exception>
Dictionary::Dictionary()
{
Initialize("../Lektion10Projekt15-1/");
}
void Dictionary::Initialize(std::string folder)
{
InitializeLanguage(folder,"da-utf8.wl");
}
void Dictionary::InitializeLanguage(std::string folder, std::string languageFileName)
{
std::ifstream ifs;
ifs.open(folder+languageFileName,std::ios_base::in);
if (ifs.fail()) {
std::cerr <<"Error! Class: Dictionary. Function: InitializeLanguage(...). return: ifs.fail to load file '" + languageFileName + "'" << std::endl;
}else {
std::string word;
while (!ifs.eof()) {
std::getline(ifs,word);
mDictionary.insert(word);
}
}
ifs.close();
}
void Dictionary::SearchForAllPossibleWordsIn(std::string searchString)
{
std::vector<std::string> result;
for (unsigned int a = 0 ; a <= searchString.length(); ++a) {
for (unsigned int b = 1; b <= searchString.length()-a; ++b) {
std::string substring = searchString.substr(a,b);
if (mDictionary.find(substring) != mDictionary.end())
{
result.push_back(substring);
}
}
}
if (!result.empty()) {
for (unsigned int i = 0; i < result.size() ;++i) {
std::cout << result[i] << std::endl;
}
}
}
main.cpp
#include <iostream>
#include "Dictionary.h"
int main()
{
Dictionary myDictionary;
myDictionary.SearchForAllPossibleWordsIn("byggearbejderen");
return 0;
}
I have tried to change the following line in main.cpp
myDictionary.SearchForAllPossibleWordsIn("byggearbejderen");
to (OBS: the first word in the word list is byggearbejderen)
std::ifstream ifs;
ifs.open("../Lektion10Projekt15-1/da-utf8.wl",std::ios::in);
if (ifs.fail()) {
std::cerr <<"Error!" << std::endl;
}else {
std::getline(ifs,searchword);
}
ifs.close();
myDictionary.SearchForAllPossibleWordsIn(searchword);
And then in the main.cpp add som print out with the expected string and substring in hex value.
std::cout << " cout as hex test:" << std::endl;
myDictionary.SearchForAllPossibleWordsIn(searchword);
std::cout << "Suposet search resul for ''bygearbejderen''" << std::endl;
for (char const elt: "byggearbejderen")
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
std::cout << "byggearbejderen" << std::endl;
for (char const elt: "arbejderen")
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
std::cout << "arbejderen" << std::endl;
for (char const elt: "ren")
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
std::cout << "ren" << std::endl;
for (char const elt: "en")
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
std::cout << "en" << std::endl;
for (char const elt: "n")
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
std::cout << "n" << std::endl;
And also added the same print in the outprint of result in Dictonary.cpp
std::cout << "result of seartchword as hex" << std::endl;
if (!result.empty()) {
for (unsigned int i = 0; i < result.size() ;++i)
{
for (char const elt: result[i] )
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(elt) << " ";
}
std::cout << result[i] << std::endl;
}
}
which gave the following output:
result of seartchword as hex
ffffffef ffffffbb ffffffbf 62 79 67 67 65 61 72 62 65 6a 64 65 72 65 6e 0d byggearbejderen
61 72 62 65 6a 64 65 72 65 6e 0d arbejderen
72 65 6e 0d ren
65 6e 0d en
6e 0d n
Suposet search resul for ''bygearbejderen''
62 79 67 67 65 61 72 62 65 6a 64 65 72 65 6e 00 byggearbejderen
61 72 62 65 6a 64 65 72 65 6e 00 arbejderen
72 65 6e 00 ren
65 6e 00 en
6e 00 n
where I notice that some values were different.
I don't know why this is the case when i am on a macOS but not the case on windows. I do not know if there are any settings of encoding in my environment I need to change or set correct.
I would like i my main.cpp looked liked this:
#include <iostream>
#include "Dictionary.h"
int main()
{
Dictionary myDictionary;
myDictionary.SearchForAllPossibleWordsIn("byggearbejderen");
return 0;
}
resulting in the following output:
byggearbejderen
arbejderen
ren
en
n
Line endings for text files are different on Windows than they are on a Mac. Windows uses both CR/LF characters (ASCII codes 13 and 10, respectively). Old Macs used the CR character alone, Linux systems use just the LF. If you create a text file on Windows, then copy it to your Mac, the line endings might not be handled correctly.
If you look at the last character in your output, you'll see it is a 0d, which would be the CR character. I don't know how you generated that output, but it is possible that the getline on the Mac is treating that as a normal character, and including it in the string that has been read in.
The simplest solution is to either process that text file beforehand to get the line endings correct, or strip the CR off the end of the words after they are read in.
I'm running some updates through Undefined Behavior Sanitizer. The sanitizer is producing a message I don't quite understand:
kalyna.cpp:1326:61: runtime error: load of address 0x0000016262c0 with insufficient space for an object of type 'const uint32_t'
0x0000016262c0: note: pointer points here
20 8b c1 1f a9 f7 f9 5c 53 c4 cf d2 2f 3f 52 be 84 ed 96 1b b8 7a b2 85 e0 96 7d 5d 70 ee 06 07
^
The code in question attempts to make cache timing attacks harder by touching addresses within the range of a cache line. Line 1326 is the line with reinterpret_cast:
// In KalynaTab namespace
uint64_t S[4][256] = {
...
};
...
// In library's namespace
const int cacheLineSize = GetCacheLineSize();
volatile uint32_t _u = 0;
uint32_t u = _u;
for (unsigned int i=0; i<256; i+=cacheLineSize)
u &= *reinterpret_cast<const uint32_t*>(KalynaTab::S+i);
Why is the santizier claiming a uint32_t u does not have sufficient space to hold an uint32_t?
Or maybe, am I parsing the error message correctly? Is that what the sanitzier is complaining about? If I am parsing it incorrectly, then what is the sanitzer complaining about?
$ lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
$ gcc --version
gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
The identifier S does not convert to a pointer of the type you think it does. As a result, your pointer arithmetic is throwing you way out of range of your data, and is best shown by example:
#include <iostream>
#include <cstdint>
uint64_t S[4][256];
int main()
{
std::cout << static_cast<void*>(S+0) << '\n';
std::cout << static_cast<void*>(S+1) << '\n';
std::cout << static_cast<void*>(S+2) << '\n';
std::cout << static_cast<void*>(S+3) << '\n';
std::cout << '\n';
std::cout << static_cast<void*>(*S+0) << '\n';
std::cout << static_cast<void*>(*S+1) << '\n';
std::cout << static_cast<void*>(*S+2) << '\n';
std::cout << static_cast<void*>(*S+3) << '\n';
}
Output (obviously platform dependent)
0x1000020b0
0x1000028b0
0x1000030b0
0x1000038b0
0x1000020b0
0x1000020b8
0x1000020c0
0x1000020c8
Note the stride of the first sequence of numbers 0x800 per inferior row. That makes sense since each row is made up of 0x100 entries of 8 bytes each (the uint64_t elements). The type of the pointer being used in the pointer arithmetic is uint64_t (*)[256].
Now note the stride of the second sequence, which peers into only S[0]. The stride is 8 bytes, one for each slot. The type of the converted pointer in this calculation is uint64_t *.
In short, your pointer arithmetic is assuming S converts to uint64_t*, and it doesn't. Like all array-to-pointer conversions, it converts to a pointer-to-first-element, including the type of said-same. The element type in the array of arrays is uint64_t[256], so the converted pointer type is uint64_t (*)[256].
The server needs to send a std::vector<float> to a Qt application over a TCP socket. I am using Qt 5.7.
On the server side, using boost::asio:
std::vector<float> message_ = {1.2, 8.5};
asio::async_write(socket_, asio::buffer<float>(message_),
[this, self](std::error_code ec, std::size_t)
This works and I manage to get it back on my client using boost::asio's read_some(). As both Qt and asio have their own event manager, I want to avoid using asio in my Qt app.
So on the client side I have (which does not work):
client.h:
#define FLOATSIZE 4
QTcpSocket *m_socket;
QDataStream m_in;
QString *m_string;
QByteArray m_buff;
client.cpp (constructor):
m_in.setDevice(m_socket);
m_in.setFloatingPointPrecision(QDataStream::SinglePrecision);
// m_in.setByteOrder(QDataStream::LittleEndian);
client.cpp (read function, which is connected via QObject::connect(m_socket, &QIODevice::readyRead, this, &mywidget::ask2read); ):
uint availbytes = m_socket->bytesAvailable(); // which is 8, so that seems good
while (availbytes >= FLOATSIZE)
{
nbytes = m_in.readRawData(m_buff.data(), FLOATSIZE);
bool conv_ok = false;
const float f = m_buff.toFloat(&conv_ok);
availbytes = m_socket->bytesAvailable();
m_buff.clear();
}
The m_buff.toFloat() call returns 0.0 which is a fail according to the Qt doc. I have tried to change the float precision, little or big endian, but I can not manage to get my std::vector<float> back. Any hints?
Edit: everything runs on the same PC/compiler.
Edit: see my answer for a solution and sehe's for more detail on what is going on
I managed to resolve the issue, by editing the Qt side (client), to read the socket:
uint availbytes = m_socket->bytesAvailable();
while (availbytes >= 4)
{
char buffer[FLOATSIZE];
nbytes = m_in.readRawData(buffer, FLOATSIZE);
float f = bytes2float(buffer);
availbytes = m_socket->bytesAvailable();
}
I use those two conversion functions, bytes2float and bytes2int:
float bytes2float(char* buffer)
{
union {
float f;
uchar b[4];
} u;
u.b[3] = buffer[3];
u.b[2] = buffer[2];
u.b[1] = buffer[1];
u.b[0] = buffer[0];
return u.f;
}
and:
int bytes2int(char* buffer)
{
int a = int((unsigned char)(buffer[3]) << 24 |
(unsigned char)(buffer[2]) << 16 |
(unsigned char)(buffer[1]) << 8 |
(unsigned char)(buffer[0]));
return a;
}
I also found that function to display bytes, which is useful to see what is going on behind the scene (from https://stackoverflow.com/a/16063757/7272199):
template <typename T>
void print_bytes(const T& input, std::ostream& os = std::cout)
{
const unsigned char* p = reinterpret_cast<const unsigned char*>(&input);
os << std::hex << std::showbase;
os << "[";
for (unsigned int i=0; i<sizeof(T); ++i)
os << static_cast<int>(*(p++)) << " ";
os << "]" << std::endl;;
}
Re. your answer: Which side is this on? Also, are your platforms not the same (OS/architecture?). I had assumed from the question that both processes run on the same PC and compiler etc.
For one thing, you can see that ASIO does not do anything related to endianness.
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace asio = boost::asio;
#include <iostream>
void print_bytes(unsigned char const* b, unsigned char const* e)
{
std::cout << std::hex << std::setfill('0') << "[ ";
while (b!=e)
std::cout << std::setw(2) << static_cast<int>(*b++) << " ";
std::cout << "]\n";
}
template <typename T> void print_bytes(const T& input) {
using namespace std;
print_bytes(reinterpret_cast<unsigned char const*>(std::addressof(*begin(input))),
reinterpret_cast<unsigned char const*>(std::addressof(*end(input))));
}
int main() {
float const fs[] { 1.2, 8.5 };
std::cout << "fs: "; print_bytes(fs);
{
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(fs));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
{
std::vector<char> binary(2*sizeof(float));
asio::buffer_copy(asio::buffer(binary), asio::buffer(fs));
std::cout << "binary: "; print_bytes(binary);
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(binary));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
}
Prints
fs: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
binary: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
Theory
I suspect the Qt side ruins things. Since the naming of the function readRawData certainly implies a lack of endianness awareness, I'd guess your system's endianness wreaks havoc (https://stackoverflow.com/a/2945192/85371, also the comment).
Suggestion
In that case, consider using Boost Endian.
I think it's a bad idea to use high level send method server side (you try to send a c++ vector) and low level client side.
I'm quite sure there is an endianness problem somewhere.
Anyway try to do this client side:
char buffer[FLOATSIZE];
bytes = m_in.readRawData(buffer, FLOATSIZE);
if (bytes != FLOATSIZE)
return ERROR;
const float f = (float)(ntohl(*((int32_t *)buffer)));
If boost::asio uses the network byte order for the floats (as it should), this will work.
I wan to encrypt /decrypt data using ECIES , I am using cryptopp for this.
AutoSeededRandomPool prng;
//get private key generated
ECIES<ECP>::Decryptor d0(prng, ASN1::secp256r1());
PrintPrivateKey(d0.GetKey());
//get public key
ECIES<ECP>::Encryptor e0(d0);
PrintPublicKey(e0.GetKey());
//encrypt the message
string em0; // encrypted message
StringSource ss1 (message, true, new PK_EncryptorFilter(prng, e0, new StringSink(em0) ) );
//decrypt the message
string dm0; // decrypted message
StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, d1, new StringSink(dm0) ) );
Everything else is fine but I want to do the above same thing using already generated 'private key' and not randomly generated 'private key' unlike the case above. How can I do this?
I have tried the following code but it just simply crashes
AutoSeededRandomPool prng;
std::string privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
CryptoPP::HexDecoder decoder;
decoder.Put((byte*)privatekeyString.data(), privatekeyString.size());
decoder.MessageEnd();
ECIES<ECP> ::Decryptor d0;
d0.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());
crash point
//load private key
d0.AccessKey().Load(decoder);
PrintPrivateKey(d0.GetKey());
//get public key
ECIES<ECP>::Encryptor e0(d0);
PrintPublicKey(e0.GetKey());
string em0; // encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, e0, new StringSink(em0) ) );
cout<<"encrypted msg: "<<em0<<" and its length: "<<em0.length()<<endl;
string dm0; // decrypted message
StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, d0, new StringSink(dm0) ) );
cout <<"decrypted msg: "<< dm0<<" and its length: "<<dm0.length() << endl;
Edit 2
In response to #jww answer I managed to decode the message with the private key as:
try
{
AutoSeededRandomPool prng;
std::string exponent="AsIAECwYD55qTnovWLW+hrwXlHg=";
StringSource ss(exponent, true /*pumpAll*/, new CryptoPP::HexDecoder);
Integer x;
x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);
// cout << "Exponent: " << std::hex << x << endl;
ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().Initialize(ASN1::secp128r1(), x);
bool valid = decryptor.AccessKey().Validate(prng, 3);
if(!valid)
{
cout<<"Exponent is not valid for P-128"<<endl;
return;
}
// throw Exception(CryptoPP::Exception::OTHER_ERROR, "Exponent is not valid for P-256");
// Or: decryptor.AccessKey().ThrowIfInvalid(prng, 3);
cout << "Exponent is valid for P-128" << endl;
PrintPrivateKey(decryptor.GetKey());
//get public key
ECIES<ECP>::Encryptor encryptor(decryptor);
PrintPublicKey(encryptor.GetKey());
string em0; // encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(em0) ) );
cout<<"encrypted msg: "<<em0<<" and its length: "<<em0.length()<<endl;
string dm0; // decrypted message
StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, decryptor, new StringSink(dm0) ) );
cout <<"decrypted msg: "<< dm0<<" and its length: "<<dm0.length() << endl;
}
catch(const CryptoPP::Exception& ex)
{
std::cerr << ex.what() << endl;
}
But when I try to encrypt the message using public key I got error
CryptoPP::CryptoMaterial::InvalidMaterial: CryptoMaterial: this object contains invalid values
Here is my code:
std::string public_point="AsIAEFjzIcX+Kvhe8AmLoGUc8aYAEAwf5ecREGZ2u4RLxQuav/A=";
StringSource ss(public_point, true, new CryptoPP::HexDecoder);
ECIES<ECP>::Encryptor encryptor;
encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());
ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "X: " << std::hex << point.x << endl;
cout << "Y: " << std::hex << point.y << endl;
encryptor.AccessKey().SetPublicElement(point);
encryptor.AccessKey().ThrowIfInvalid(prng, 3);
PrintPublicKey(encryptor.GetKey());
string em0; // encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(em0) ) );
cout<<"encrypted msg: "<<em0<<" and its length: "<<em0.length()<<endl;
The problem I am having is you don't appear to know what you have, and the some of the parameters you are using are wrong when taken with the other parameters. So its pretty much a stab in the dark.
First, you should wrap the disk operations in a try/catch. I/O can always cause problems, so be sure to catch exceptions related to the iostream stuff. You should also catch the Crypto++ Exception related to key loading. That will handle the "crash" with no information.
So your code might look something like:
try
{
// Read key from disk, load it into Crypto++ object
}
catch(const Exception& ex)
{
cerr << "Caught Crypto++ exception " << ex.what() << endl;
}
catch(const std::runtime_error& ex)
{
cerr << "Caught C++ runtime error " << ex.what() << endl;
}
Second, this looks like a private exponent, and not a private key:
std::string privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
And its too big to be in P-128. Maybe you should do something like:
try
{
AutoSeededRandomPool prng;
std::string exponent="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
StringSource ss(exponent, true /*pumpAll*/, new HexDecoder);
Integer x;
x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);
// cout << "Exponent: " << std::hex << x << endl;
ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().Initialize(ASN1::secp256r1(), x);
bool valid = decryptor.AccessKey().Validate(prng, 3);
if(!valid)
throw Exception(Exception::OTHER_ERROR, "Exponent is not valid for P-256");
// Or: decryptor.AccessKey().ThrowIfInvalid(prng, 3);
cout << "Exponent is valid for P-256" << endl;
}
catch(const Exception& ex)
{
cerr << ex.what() << endl;
}
Or, you can:
ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp256r1());
decryptor.AccessKey().SetPrivateExponent(x);
If you add the following to the program above:
// Encode key, use OID versus domain paramters
string encoded;
HexEncoder encoder(new StringSink(encoded));
decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(encoder);
cout << "Private key: " << encoded << endl;
You will get the following for the private key:
$ ./ecies-test.exe
Exponent: 2c200102c180f9e6a4e7a2f58b5be86bc179478h
Private key: 3041020100301306072A8648CE3D020106082A8648CE3D030107042730250201010
42000000000000000000000000002C200102C180F9E6A4E7A2F58B5BE86BC179478
As you can see, the key is not "02C200102C180F9E6A4E7A2F58B5BE86BC179478".
The 12 leading 0's look suspicious to me. Though the exponent validates, you should verify the exponent and the field. The closest fit I could find is the curve secp160r2 (of course, curves like secp192k1 and secp224k1 work too).
The private key above is the hex encoding of ecies.priv.der shown below.
Third, this could be a public point in compressed form due to the leading 02.
std::string privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
If that is the case, then you are supposed to be able to do this, but I can't get it to decode the point (see Minimizing Key Size for Persistence on the wiki). x and y are 0 after the operation; maybe the problem is with the field:
std::string public_point="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
StringSource ss(public_point, true, new HexDecoder);
ECIES<ECP>::Encryptor encryptor;
encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());
ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "X: " << std::hex << point.x << endl;
cout << "Y: " << std::hex << point.y << endl;
encryptor.AccessKey().SetPublicElement(point);
encryptor.AccessKey().ThrowIfInvalid(prng, 3);
Fourth, you should probably save the entire key, and not just the exponent. Here's a program for you that shows you how to save and load the keys. It also shows you how to perform encryption and decryption in one-liners.
/////////////////////////////////////////////////
// Part one - generate keys
ECIES<ECP>::Decryptor decryptor(prng, ASN1::secp256r1());
ECIES<ECP>::Encryptor encryptor(decryptor);
/////////////////////////////////////////////////
// Part two - save keys
FileSink fs1("ecies.priv.der", true /*binary*/);
decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(fs1);
FileSink fs2("ecies.pub.der", true /*binary*/);
encryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
encryptor.GetKey().Save(fs2);
/////////////////////////////////////////////////
// Part three - encrypt/decrypt
string message, encrypted, recovered;
if(argc >= 2 && argv[1] != NULL)
message = argv[1];
else
message = "Attack at dawn!";
StringSource ss1 (message, true /*pumpAll*/, new PK_EncryptorFilter(prng, encryptor, new StringSink(encrypted)));
StringSource ss2 (encrypted, true /*pumpAll*/, new FileSink("ecies.encrypted.bin", true /*binary*/));
StringSource ss3 (encrypted, true /*pumpAll*/, new PK_DecryptorFilter(prng, decryptor, new StringSink(recovered)));
cout << recovered << endl;
Here is what a private key looks like from the test program above. Notice it has the field encoded into the structure so you don't have to guess at it (P-256 versus P-128 versus P-521).
$ dumpasn1 ecies.priv.der
0 65: SEQUENCE {
2 1: INTEGER 0
5 19: SEQUENCE {
7 7: OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
16 8: OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
: }
26 39: OCTET STRING, encapsulates {
28 37: SEQUENCE {
30 1: INTEGER 1
33 32: OCTET STRING
: 00 00 00 00 00 00 00 00 00 00 00 00 02 C2 00 10
: 2C 18 0F 9E 6A 4E 7A 2F 58 B5 BE 86 BC 17 94 78
: }
: }
: }
And the public key:
$ dumpasn1 ecies.pub.der
0 89: SEQUENCE {
2 19: SEQUENCE {
4 7: OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
13 8: OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
: }
23 66: BIT STRING
: 04 08 9B D2 1C 3A DC 08 8B 1F F1 D0 F4 97 A0 87
: FE 4F 78 EA E2 B8 30 B8 E7 06 37 68 27 4C 71 CD
: 63 C3 E2 90 66 64 2B 1C F6 79 00 36 AF 72 4C 61
: 69 FA E9 06 00 9A 15 32 0B 85 B5 88 B2 C5 88 46
: 5E
: }
Crypto++ has a wiki page on ECIES. See Elliptic Curve Integrated Encryption Scheme. They also have Bouncy Castle interop workarounds.
You can also PEM encode the keys, but you need a patch to do it because its not part of the library. For the patch, see PEM Pack on the Crypto++ wiki.
I'm going to add another answer to show you how to serialize private exponents and public points in case you had trouble with the public points. It also shows you how to Save the PrivateKeyInfo and SubjectPublicKeyInfo.
Its produces output similar to below. You will need a patch for the Base64URLEncoder. its not part of the library.
$ ./ecies-test.exe
Private exponent
Hex: 57E91FA3EF48706D07E56D8CB566204A4416B833EFB9687D75A37D572EC42277
Base64: V+kfo+9IcG0H5W2MtWYgSkQWuDPvuWh9daN9Vy7EInc=
Base64 (URL safe): V-kfo-9IcG0H5W2MtWYgSkQWuDPvuWh9daN9Vy7EInc=
Pubic point
Hex: 037142DE6143B6AD44C74135FE71222AC1406F541E53CB635112DE4928EC94763C
Base64: A3FC3mFDtq1Ex0E1/nEiKsFAb1QeU8tjURLeSSjslHY8
Base64 (URL safe): A3FC3mFDtq1Ex0E1_nEiKsFAb1QeU8tjURLeSSjslHY8
Private key (PrivateKeyInfo)
3059301306072A8648CE3D020106082A8648CE3D030107034200047142DE6143B6AD44C74135FE71
222AC1406F541E53CB635112DE4928EC94763CFA903D9282691AE47A2D718297465EF44E905A89ED
2D4553ED1DF906A6E2383B
Public key (SubjectPublicKeyInfo)
3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042057E91FA3EF
48706D07E56D8CB566204A4416B833EFB9687D75A37D572EC42277
With the private exponent and public point above, the following works just fine:
string pub_point("A7EDDUXAA4/6kOZ8H+firJ95YtKZvDrPFmyVoisyBfuW");
StringSource ss(pub_point, true, new Base64Decoder);
ECIES<ECP>::Encryptor encryptor;
encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp256r1());
ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
encryptor.AccessKey().SetPublicElement(point);
encryptor.AccessKey().ThrowIfInvalid(prng, 3);
ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().Initialize(prng, ASN1::secp256r1());
const Integer& priv_exp = decryptor.GetKey().GetPrivateExponent();
SecByteBlock x(priv_exp.MinEncodedSize());
priv_exp.Encode(x, x.size());
string s1, s2, s3;
HexEncoder f1(new StringSink(s1));
Base64Encoder f2(new StringSink(s2));
Base64URLEncoder f3(new StringSink(s3));
ChannelSwitch cs1;
cs1.AddDefaultRoute(f1);
cs1.AddDefaultRoute(f2);
cs1.AddDefaultRoute(f3);
ArraySource as1(x, x.size(), true /*pumpAll*/, new Redirector(cs1));
cout << "Private exponent" << endl;
cout << " Hex: " << s1 << endl;
cout << " Base64: " << s2 << endl;
cout << " Base64 (URL safe): " << s3 << endl;
//////////////////////////////////////////
ECIES<ECP>::Encryptor encryptor(decryptor);
ECP::Point pub_point = encryptor.GetKey().GetGroupParameters().ExponentiateBase(priv_exp);
SecByteBlock y(encryptor.GetKey().GetGroupParameters().GetCurve().EncodedPointSize(true /*compressed*/));
encryptor.GetKey().GetGroupParameters().GetCurve().EncodePoint(y, pub_point, true /*compressed*/);
string s4, s5, s6;
HexEncoder f4(new StringSink(s4));
Base64Encoder f5(new StringSink(s5));
Base64URLEncoder f6(new StringSink(s6));
ChannelSwitch cs2;
cs2.AddDefaultRoute(f4);
cs2.AddDefaultRoute(f5);
cs2.AddDefaultRoute(f6);
ArraySource as2(y, y.size(), true /*pumpAll*/, new Redirector(cs2));
cout << "Pubic point" << endl;
cout << " Hex: " << s4 << endl;
cout << " Base64: " << s5 << endl;
cout << " Base64 (URL safe): " << s6 << endl;
//////////////////////////////////////////
string s10, s11;
HexEncoder hex1(new StringSink(s10));
HexEncoder hex2(new StringSink(s11));
encryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
encryptor.GetKey().Save(hex1);
decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(hex2);
cout << "Private key" << endl;
cout << s10 << endl;
cout << "Public key" << endl;
cout << s11 << endl;
As jww suggested I have successfully completed encryption and decryption.
Below are the code snippets if anyone wants.
Decryption
string decrypt(std::string encryptedMessage , std::string privateKeyExponent)
{
string decryptedMessage;
try
{
AutoSeededRandomPool prng;
//since the 'privateKeyExponent' is in base-64 format use Base64Decoder
StringSource ss(privateKeyExponent, true /*pumpAll*/, new CryptoPP::Base64Decoder);
Integer x;
x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);
ECIES<ECP>::Decryptor decryptor;
//curve used is secp256k1
//make decryptor's access key using decoded private exponent's value
decryptor.AccessKey().Initialize(ASN1::secp256k1(), x);
//check whether decryptor's access key is valid or not
bool valid = decryptor.AccessKey().Validate(prng, 3);
if(!valid)
decryptor.AccessKey().ThrowIfInvalid(prng, 3);
cout << "Exponent is valid for P-256k1" << endl;
//decrypt the message using private key
StringSource ss2 (encryptedMessage, true, new PK_DecryptorFilter(prng, decryptor, new StringSink(decryptedMessage) ) );
cout <<"decrypted msg: "<< decryptedMessage<<" and its length: "<<decryptedMessage.length() << endl;
}
catch(const CryptoPP::Exception& ex)
{
std::cerr << ex.what() << endl;
}
return decryptedMessage;
}
Encryption
string encrypt(std::string message , std::string compressedPublicKeyPoint)
{
string encryptedMessage;
try
{
AutoSeededRandomPool prng;
//public key is a point consisting of "public key point x" and "public key point y"
//compressed public key also known as "public-point" formed using point-compression of public key
//since the key is in base-64 format use Base64Decoder
StringSource ss(compressedPublicKeyPoint, true, new CryptoPP::Base64Decoder);
ECIES<ECP>::Encryptor encryptor;
//curve used is secp256k1
encryptor.AccessKey().AccessGroupParameters()
.Initialize(ASN1::secp256k1());
//get point on the used curve
ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "X: " << std::hex << point.x << endl;
cout << "Y: " << std::hex << point.y << endl;
//set encryptor's public element
encryptor.AccessKey().SetPublicElement(point);
//check whether the encryptor's access key thus formed is valid or not
encryptor.AccessKey().ThrowIfInvalid(prng, 3);
// encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(encryptedMessage) ) );
cout<<"encrypted msg: "<<encryptedMessage<<" and its length: "<<encryptedMessage.length()<<endl;
}
catch(const CryptoPP::Exception& ex)
{
std::cerr << ex.what() << endl;
}
return encryptedMessage;
}