aboutsummaryrefslogtreecommitdiff
path: root/src/cryptography/noscrypt_cipher.cpp
blob: ae9aaf0700cebfa109c307fd5142388cb21cc039 (plain)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <plog/Init.h>
#include <plog/Log.h>

#include <openssl/evp.h>
#include <openssl/rand.h>

#include "cryptography/noscrypt_cipher.hpp"

using namespace nostr::cryptography;
using namespace std;

static void _printNoscryptError(NCResult result, const std::string funcName, int lineNum)
{
    uint8_t argPosition;

    switch (NCParseErrorCode(result, &argPosition))
    {
    case E_NULL_PTR:
        PLOG_ERROR << "noscrypt - error: A null pointer was passed in " << funcName << "(" << argPosition << ") at line " << lineNum;
        break;

    case E_INVALID_ARG:
        PLOG_ERROR << "noscrypt - error: An invalid argument was passed in " << funcName << "(" << argPosition << ") at line " << lineNum;
        break;

    case E_INVALID_CONTEXT:
        PLOG_ERROR << "noscrypt - error: An invalid context was passed in " << funcName << "(" << argPosition << ") on line " << lineNum;
        break;

    case E_ARGUMENT_OUT_OF_RANGE:
        PLOG_ERROR << "noscrypt - error: An argument was out of range in " << funcName << "(" << argPosition << ") at line " << lineNum;
        break;

    case E_OPERATION_FAILED:
        PLOG_ERROR << "noscrypt - error: An operation failed in " << funcName << "(" << argPosition << ") at line " << lineNum;
        break;

    default:
        PLOG_ERROR << "noscrypt - error: An unknown error " << result << " occurred in " << funcName << "(" << argPosition << ") at line " << lineNum;
        break;
    }

}

#define LOG_NC_ERROR(result) _printNoscryptError(result, __func__, __LINE__)

NoscryptCipher::NoscryptCipher(uint32_t version, uint32_t mode) :
 _cipher(version, mode)
{
    /*
    * We can know what iv size we need for the cipher now and allocate
    * a buffer just to save some allocations and code during the
    * encryption phase. This buffer is only needed during an encryption
    * operation.
    */

    if ((mode & NC_UTIL_CIPHER_MODE) == NC_UTIL_CIPHER_MODE_ENCRYPT)
    {
        //Resize the vector to the size of the current cipher
        this->_ivBuffer.resize(this->_cipher.ivSize());

        //Safe to assign the iv to the context now and it will maintain a pointer to the buffer
        this->_cipher.setIV(this->_ivBuffer);
    }
}

std::string NoscryptCipher::update(
    const std::shared_ptr<const NCContext> libContext,
    const std::shared_ptr<const NCSecretKey> localKey,
    const std::shared_ptr<const NCPublicKey> remoteKey,
    const std::string& input
)
{
    NCResult result;

    //Argument exception if the input is empty
    if (input.empty())
    {
        return string();
    }

    //Safely convert the string to a vector of bytes (allocates and copies, so maybe speed up later)
    const vector<uint8_t> inputBuffer(input.begin(), input.end());

result = this->_cipher.setInput(inputBuffer);
    if (result != NC_SUCCESS)
    {
        LOG_NC_ERROR(result);
        return string();
    }

    /*
    * If were in encryption mode a random nonce (iv) must be generated. The size was determined
    * when the cipher was created and already assigned to the context, so we just need to assign
    * the random data.
    *
    * Keep in mind, this will automatically work for nip44 and nip04, either the
    * AES iv or the ChaCha nonce.
    */
    if ((this->_cipher.flags() & NC_UTIL_CIPHER_MODE) == NC_UTIL_CIPHER_MODE_ENCRYPT)
    {
        int code = RAND_bytes(
            this->_ivBuffer.data(),
            this->_ivBuffer.size()  //Size set in constructor
        );

        if (code <= 0)
        {
            PLOG_ERROR << "Failed to generate a nonce or IV for encryption.";
            return string();
        }
    }

    //Performs the operation (either encryption or decryption) 
    result = this->_cipher.update(libContext, localKey, remoteKey);
    if (result != NC_SUCCESS)
    {
        LOG_NC_ERROR(result);
        return string();
    }

    /*
    * Time to read the ciper output by getting the size of the output, then creating
    * a string to store it to
    */

    NCResult outputSize = this->_cipher.outputSize();
    if (outputSize <= 0)
    {
        LOG_NC_ERROR(outputSize);
        return string();
    }

    //Alloc vector for reading input data (maybe only alloc once)
    vector<uint8_t> output(outputSize);

    result = this->_cipher.readOutput(output);
    if (result != outputSize)
    {
        LOG_NC_ERROR(result);
        return string();
    }

    return string(output.begin(), output.end());
}

string NoscryptCipher::naiveEncodeBase64(const std::string& str)
{
    // Compute base64 size and allocate a string buffer of that size.
    const size_t encodedSize = NoscryptCipher::base64EncodedSize(str.size());
    unsigned char* encodedData = new unsigned char[encodedSize];

    // Encode the input string to base64.
    EVP_EncodeBlock(encodedData, (const unsigned char*)str.data(), str.size());

    // Construct the encoded string from the buffer.
    string encodedStr((char*)encodedData);

    // Zero out the buffer and delete the pointer.
    memset(encodedData, 0, encodedSize);
    delete [] encodedData;

    return encodedStr;
}

string NoscryptCipher::naiveDecodeBase64(const string& str)
{
    // Compute the size of the decoded string and allocate a buffer of that size.
    const size_t decodedSize = NoscryptCipher::base64DecodedSize(str.size());
    unsigned char* decodedData = new unsigned char[decodedSize];

    // Decode the input string from base64.
    EVP_DecodeBlock(decodedData, (const unsigned char*)str.data(), str.size());

    // Construct the decoded string from the buffer.
    string decodedStr((char*)decodedData);

    // Zero out the buffer and delete the pointer.
    memset(decodedData, 0, decodedSize);
    delete [] decodedData;

    return decodedStr;
};