Write object into file

Hello guys,

i am working on a simple password manager and i want to write multiple struct Credential objects into a bin file.

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
#include <stdio.h>
#include <stdlib.h>
#include "../ransom/encryption/decrypt.hpp"
#include "../mylibs/arrtypes.h"
#include <iostream>

#ifndef __MGRTOOLS
#define __MGRTOOLS

#define _PWDMGR_MAXSIZE 1000


using namespace std;

struct Credential {
    const char *service{nullptr};
    const char *username{nullptr};
    uint8_t *password{nullptr};

    Credential() = default;
    Credential(const char *s, const char *u, uint8_t* p)
    {
        uint8_t *tmp = new uint8_t[strlen((const char *) p) + 1]{};
        strcpy((char *) tmp, (const char *) p);
        password = tmp;

        char *tmp2 = new char[strlen((const char *) u) + 1]{};
        strcpy((char *) tmp2, (const char *) u);
        username = tmp2;

        char *tmp3 = new char[strlen((const char *) s) + 1]{};
        strcpy((char *) tmp3, (const char *) s);
        service = tmp3;
    }


};

auto _AES = [](Credential *cred, uint8_t* key, void (*algortm)(uint8_t*, uint8_t*))-> void
{
    size_t aessize{strlen((const char*) cred->password)};

    while(aessize %  _AES_BLOCKSIZE != 0)
        aessize++;

    uint8_t *_buffer = new uint8_t[aessize]{};
    strcpy((char *) _buffer, (const char *) cred->password);

    algortm(_buffer, key);


    delete[] cred->password;

    cred->password = _buffer;



};

size_t write_file(const char *file, ContainerList<Credential> data, uint8_t* key)
{
    size_t written;
    FILE *fp = fopen(file, "wb");
    if (fp == nullptr)
        return numeric_limits<size_t>::max(); // error

    data.insert(0, ContainerList<Credential>{{"TESTSERVICE", "TESTUSERNAME", (uint8_t *) "TESTPASS"}});

    for (auto &item : data)
        _AES(&item, key, AES_encrypt);


    written = fwrite((const void *) data.begin(), sizeof(*data.begin()), data.size(), fp);

    fclose(fp);

    return written - 1;
}


ContainerList<Credential> read_file(const char *file, uint8_t* key)
{
    size_t read;
    FILE *fp = fopen(file, "rb");

    if (fp == nullptr)
        return {};

    Credential *_in = new Credential[_PWDMGR_MAXSIZE];
    read = fread(_in, sizeof(*_in), _PWDMGR_MAXSIZE, fp);

    ContainerList<Credential> result{_in, _in + read};

    for (auto &item : result)
        _AES(&item, key, AES_decrypt);

    fclose(fp);
    delete[] _in;

    return result;
}

#endif 


"../ransom/encryption/decrypt.hpp" contains just two functions AES_encrypt and AES_decrypt and both take uin8_t* buffer and uint8_t* key as params.

"../mylibs/arrtypes.h": (my own container library)
https://codeshare.io/JbnQoE // sorry just exceeded max char for cplusplus.com

The Problem now is, that this works just fine:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "mgrtools.h"

int main() {

    uint8_t key[16]{"abcdefghijklmnn"};
    std::cout << write_file("hell.txt", ContainerList<Credential>{Credential{"Google.de", "luke_123@gmail.com", (uint8_t *) "lukespass"}, Credential{"github.com", "luke@gmail.com", (uint8_t *) "gitpass"}}, key);

    auto res = read_file("hell.txt", key);

    for (auto i : res)
        std::cout << i.service << ", " << i.username << ", " << i.password << "\n";
}

output:
1
2
3
4
5
2TESTSERVICE, TESTUSERNAME, TESTPASS
Google.de, luke123@gmail.com, lukespass
github.com, luke@gmail.com, gitpass

Process finished with exit code 0

Note: testpass entry is for key validation.

, but only if i write to the file and read afterwards.
If i try the following with the data already written to my file in the previeous process, i get an overflow err:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "mgrtools.h"

int main() {

    uint8_t key[16]{"abcdefghijklmnn"};
   

    auto res = read_file("hell.txt", key); // only read in this process failes

    for (auto i : res)
        std::cout << i.service << ", " << i.username << ", " << i.password << "\n";
}

output is none with this disgusting delay, we all know about before the process finishes.

My general question will be, what does cause this overflow if i dont write to the file in the same process before. Does Credential do not meet the requirements to be able to be written into the file successfully and if so, what do i need to change then?
I can imagine that using const char * raw pointers in my structure could cause this error, but I have seen (stackoverflow quick search) that it should be possible even with such raw pointers if you assign the right memory to the pointers. As you can see, ive already tried out to convert the Credentials constructor args into c++ pointers with the new keyword, but still no success. This is beyond my knowledge now.

i thank you very much in advance for your help

Luke
Last edited on
Credential uses dynamic memory. To store this struct in a file you need to serialise the data. You can't just copy the pointers which you're doing with fwrite(). You need to write the actual data pointed to by the dynamic memory pointers (and then read it and form a correct Credential). To just use fwrite(), then you need to use 'c style' arrays in Credential.

data serialisation can be a big topic. There's loads of info around about it and several serialization libraries on github.

However, in this case the easiest option would be to make service, username and password fixed-size arrays.
Thank you,
i now understand that if i write const char[100] instead of const char * this will work, right?

kind regards

Luke
Partly. If you make it const char[100] then you can't change the array contents as you probably want to, so just char[100]. Obviously you'll need to change Credential constructor to not use new and pointers.
OMG. I did just the following as you assumed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define _PWDMGR_MAXSIZE 1000

struct Credential {
    char service[_PWDMGR_MAXSIZE]{};
    char username[_PWDMGR_MAXSIZE]{};
    uint8_t password[_PWDMGR_MAXSIZE * _AES_BLOCKSIZE]{};

    Credential() = default;
    Credential(char *s, char *u, uint8_t* p)
    {
        memset(service, 0, _PWDMGR_MAXSIZE);
        memset(username, 0, _PWDMGR_MAXSIZE);
        memset(password, 0, _PWDMGR_MAXSIZE * _AES_BLOCKSIZE);

        strcpy(service, s);
        strcpy(username, u);
        strcpy((char *) password, (char *) p);
    }

};

Thank you so much!
You don't need L11-13 as strcpy will null-terminate - and L4-6 will initialise anyhow to all 0.

Will password ever contain a null char (\0)? If yes, then you can't use strcpy() as this terminates on a null.
Last edited on
Thanks, thats good to know ive already thought that too. I dont suppose the password to contain null characters by the way. So thats no problem. Thank you for your help.
Luke
Topic archived. No new replies allowed.