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

Note text encryption #231

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/filters/notesortfilterproxymodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ bool NoteSortFilterProxyModel::filterAcceptsRow(qint32 source_row, const QModelI
// obsolete/unused

bool NoteSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {
QLOG_DEBUG() << "lessThan";
//QLOG_DEBUG() << "lessThan";
QVariant leftData = this->sourceModel()->data(left);
QVariant rightData = this->sourceModel()->data(right);

Expand Down
213 changes: 213 additions & 0 deletions src/threads/encrypt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
#include "vigenere.h"
#include <iostream>
#include <string>
#include <vector>


class Base64
{
public:
static std::string encode(const std::vector<char>& data);
static std::vector<char> decode(const std::string& data);
};


const char fillchar = '=';

// 00000000001111111111222222
// 01234567890123456789012345
static std::string cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

// 22223333333333444444444455
// 67890123456789012345678901
"abcdefghijklmnopqrstuvwxyz"

// 555555556666
// 234567890123
"0123456789+/";

//
//----< convert vector of bytes to std::string >---------------------

std::string Base64::encode(const std::vector<char>& data)
{
Copy link
Owner

@robert7 robert7 Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why code (body of functions) is in a header file?

std::string::size_type i;
char c;
unsigned int len = data.size();
std::string ret;

for (i = 0; i < len; ++i)
{
c = (data[i] >> 2) & 0x3f;
ret.append(1, cvt[c]);
c = (data[i] << 4) & 0x3f;
if (++i < len)
c |= (data[i] >> 4) & 0x0f;

ret.append(1, cvt[c]);
if (i < len)
{
c = (data[i] << 2) & 0x3f;
if (++i < len)
c |= (data[i] >> 6) & 0x03;

ret.append(1, cvt[c]);
}
else
{
++i;
ret.append(1, fillchar);
}

if (i < len)
{
c = data[i] & 0x3f;
ret.append(1, cvt[c]);
}
else
{
ret.append(1, fillchar);
}
}

return(ret);
}
//
//----< convert std::string to vector of bytes >---------------------

std::vector<char> Base64::decode(const std::string& data)
{
std::string::size_type i;
char c;
char c1;
std::string::size_type len = data.length();
std::vector<char> ret;

for (i = 0; i < len; ++i)
{
c = (char) cvt.find(data[i]);
++i;
c1 = (char) cvt.find(data[i]);
c = (c << 2) | ((c1 >> 4) & 0x3);
ret.push_back(c);
if (++i < len)
{
c = data[i];
if (fillchar == c)
break;
c = (char) cvt.find(c);
c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
ret.push_back(c1);
}
if (++i < len)
{
c1 = data[i];
if (fillchar == c1)
break;
c1 = (char) cvt.find(c1);
c = ((c << 6) & 0xc0) | c1;
ret.push_back(c);
}
}
return(ret);
}


// encrypt

std::string encrypt(std::string& msg, std::string& key)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use some built-in strong encryption algorithm? "Home made cryptography" is discouraged. Please use some vetted stable library to do encryption and decryption.

{
std::vector<char> msg2(msg.begin(), msg.end());
std::string b64_str = Base64::encode(msg2);
std::string vigenere_msg = encrypt_vigenere(b64_str, key);
return vigenere_msg;
}

// https://stackoverflow.com/questions/17316506/strip-invalid-utf8-from-string-in-c-c
std::string sanitize_utf8(std::string& str)
{
int i,f_size=str.size();
unsigned char c,c2,c3,c4;
string to;
to.reserve(f_size);

for(i=0 ; i<f_size ; i++){
c=(unsigned char)(str)[i];
if(c<32){//control char
if(c==9 || c==10 || c==13){//allow only \t \n \r
to.append(1,c);
}
continue;
}else if(c<127){//normal ASCII
to.append(1,c);
continue;
}else if(c<160){//control char (nothing should be defined here either ASCI, ISO_8859-1 or UTF8, so skipping)
if(c2==128){//fix microsoft mess, add euro
to.append(1,226);
to.append(1,130);
to.append(1,172);
}
if(c2==133){//fix IBM mess, add NEL = \n\r
to.append(1,10);
to.append(1,13);
}
continue;
}else if(c<192){//invalid for UTF8, converting ASCII
to.append(1,(unsigned char)194);
to.append(1,c);
continue;
}else if(c<194){//invalid for UTF8, converting ASCII
to.append(1,(unsigned char)195);
to.append(1,c-64);
continue;
}else if(c<224 && i+1<f_size){//possibly 2byte UTF8
c2=(unsigned char)(str)[i+1];
if(c2>127 && c2<192){//valid 2byte UTF8
if(c==194 && c2<160){//control char, skipping
;
}else{
to.append(1,c);
to.append(1,c2);
}
i++;
continue;
}
}else if(c<240 && i+2<f_size){//possibly 3byte UTF8
c2=(unsigned char)(str)[i+1];
c3=(unsigned char)(str)[i+2];
if(c2>127 && c2<192 && c3>127 && c3<192){//valid 3byte UTF8
to.append(1,c);
to.append(1,c2);
to.append(1,c3);
i+=2;
continue;
}
}else if(c<245 && i+3<f_size){//possibly 4byte UTF8
c2=(unsigned char)(str)[i+1];
c3=(unsigned char)(str)[i+2];
c4=(unsigned char)(str)[i+3];
if(c2>127 && c2<192 && c3>127 && c3<192 && c4>127 && c4<192){//valid 4byte UTF8
to.append(1,c);
to.append(1,c2);
to.append(1,c3);
to.append(1,c4);
i+=3;
continue;
}
}
//invalid UTF8, converting ASCII (c>245 || string too short for multi-byte))
to.append(1,(unsigned char)195);
to.append(1,c-64);
}
return to;
}


std::string decrypt(std::string& encrypted_msg, std::string& key)
{
std::string newKey = extend_key(encrypted_msg, key);
std::string b64_encoded_str = decrypt_vigenere(encrypted_msg, newKey);
std::vector<char> b64_decode_vec = Base64::decode(b64_encoded_str);
std::string b64_decode_str(b64_decode_vec.begin(), b64_decode_vec.end());
return sanitize_utf8(b64_decode_str);
}
51 changes: 51 additions & 0 deletions src/threads/syncrunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
***********************************************************************************/

#include <QTimer>
#include <stdio.h>

#include "syncrunner.h"
#include "src/global.h"
Expand All @@ -34,6 +35,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "src/communication/communicationmanager.h"
#include "src/communication/communicationerror.h"
#include "src/sql/nsqlquery.h"
#include "encrypt.h"

#define CRYPT_KEY "6aabbb3efbd1ff6d606061684e9749cd476763a8"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The encryption key should never be checked in the version control. This defeats the whole purpose of encryption. Even if the code were not public, it still would only be an obfuscation.


extern Global global;

Expand Down Expand Up @@ -583,6 +587,30 @@ void SyncRunner::syncRemoteNotes(QList<Note> notes, qint32 account) {

for (int i = 0; i < notes.size() && keepRunning; i++) {
Note t = notes[i];

//QLOG_DEBUG() << t.content;

// decrypt =============================================
int crypt_s = t.content->indexOf("||crypt||");
if (crypt_s!=-1) {
int crypt_e = t.content->indexOf("</en-note>");
QString cryptq = t.content->mid(crypt_s + 9, crypt_e - crypt_s - 9);
cryptq.remove(QChar('\n'));

//QLOG_DEBUG() << cryptq;

string crypt = cryptq.toStdString();
string crypt_key = CRYPT_KEY;
string msg = decrypt(crypt, crypt_key);

notes[i].content = QString::fromUtf8(msg.c_str());

t = notes[i];

//QLOG_DEBUG() << t.content;
}
// ======================================================

qint32 lid = noteTable.getLid(t.guid);
if (lid > 0) {
// Find out if it is a conflicting change
Expand Down Expand Up @@ -1184,6 +1212,29 @@ qint32 SyncRunner::uploadPersonalNotes() {
Note note;
noteTable.get(note, validLids[i], true, true);

// encrypt content ============================
//QLOG_DEBUG() << note.content;

int crypt_tag = note.content->indexOf("{{encrypt}}");

if (crypt_tag != -1){
string msg = note.content->toStdString();
string crypt_key = CRYPT_KEY;
string crypt = encrypt(msg, crypt_key);

note.content = QString::fromUtf8(crypt.c_str());

note.content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\
"<!DOCTYPE en-note SYSTEM 'http://xml.evernote.com/pub/enml2.dtd'>"\
"<en-note style=\n\"word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;\">\n"\
"||crypt||" +
note.content +
"</en-note>";

//QLOG_DEBUG() << note.content;
}
////============================================

qint32 oldUsn = 0;
if (note.updateSequenceNum.isSet())
oldUsn = note.updateSequenceNum;
Expand Down
75 changes: 75 additions & 0 deletions src/threads/vigenere.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <stdio.h>
#include <string.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <ctype.h>

using namespace std;

std::string AVAILABLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

int index(char c) {
for(int ii = 0; ii < AVAILABLE_CHARS.size(); ii++) {
if(AVAILABLE_CHARS[ii] == c) {
// std::cout << ii << " " << c << std::endl;
return ii;
}
}
return -1;
}


std::string extend_key(std::string& msg, std::string& key) {
//generating new key
int msgLen = msg.size();
std::string newKey(msgLen, 'x');
int keyLen = key.size(), i, j;
for(i = 0, j = 0; i < msgLen; ++i, ++j){
if(j == keyLen)
j = 0;

newKey[i] = key[j];
}
newKey[i] = '\0';
return newKey;
}


std::string encrypt_vigenere(std::string& msg, std::string& key) {
int msgLen = msg.size(), keyLen = key.size(), i, j;
std::string encryptedMsg(msgLen, 'x');
// char newKey[msgLen], encryptedMsg[msgLen], decryptedMsg[msgLen];

std::string newKey = extend_key(msg, key);

//encryption
for(i = 0; i < msgLen; ++i) {
// std::cout << msg[i] << " " << isalnum(msg[i]) << std::endl;
if(isalnum(msg[i]) or msg[i] == ' ') {
encryptedMsg[i] = AVAILABLE_CHARS[((index(msg[i]) + index(newKey[i])) % AVAILABLE_CHARS.size())];
} else {
encryptedMsg[i] = msg[i];
}
}

encryptedMsg[i] = '\0';
return encryptedMsg;
}

std::string decrypt_vigenere(std::string& encryptedMsg, std::string& newKey) {
// decryption
int msgLen = encryptedMsg.size();
std::string decryptedMsg(msgLen, 'x');
int i;
for(i = 0; i < msgLen; ++i) {
if(isalnum(encryptedMsg[i]) or encryptedMsg[i] == ' ') {
decryptedMsg[i] = AVAILABLE_CHARS[(((index(encryptedMsg[i]) - index(newKey[i])) + AVAILABLE_CHARS.size()) % AVAILABLE_CHARS.size())];
} else {
decryptedMsg[i] = encryptedMsg[i];
}
}
decryptedMsg[i] = '\0';
return decryptedMsg;
}