MT#56025 DSM Utils: new function for voucher code enc/decryption

New functions introduced for DSM API (mod_utils):
- encryptCodeAes128CBC()
- decryptCodeAes128CBC()

Will be used by the voucher DSM application to encrypt/decrypt
the code given by a subscriber when filling up the balance.

OpenSSL AES128 CBC is used as an algorithm.
(with default AES key size 16 bytes)

Required things to let it be working:
- base64 encoding/decoding (of binary raw data)
- padding based on default AES key size (16)

Change-Id: I4b47f49ebf4b61157c3a4631cd8302565c660bc3
mr13.3.1
Donat Zenichev 1 year ago
parent f2c620d8a0
commit 5d858dd1cc

@ -30,8 +30,9 @@
#include "DSMStateEngine.h"
#include <string>
using std::string;
#include <map>
using std::string;
using std::map;
class AmSession;

@ -35,6 +35,11 @@
#include "AmSession.h"
#include "AmPlaylist.h"
/* voucher */
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
SC_EXPORT(MOD_CLS_NAME);
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
@ -58,6 +63,9 @@ MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
DEF_CMD("utils.unescapeCRLF", SCUUnescapeCRLFAction);
DEF_CMD("utils.playRingTone", SCUPlayRingToneAction);
DEF_CMD("utils.encryptCodeAes128CBC", SCEncryptCodeAes128CBC);
DEF_CMD("utils.decryptCodeAes128CBC", SCDecryptCodeAes128CBC);
} MOD_ACTIONEXPORT_END;
MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
@ -67,6 +75,10 @@ MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
} MOD_CONDITIONEXPORT_END;
/**
* Helper functions
*/
vector<string> utils_get_count_files(DSMSession* sc_sess, unsigned int cnt,
const string& basedir, const string& suffix, bool right) {
@ -158,6 +170,94 @@ bool utils_play_count(DSMSession* sc_sess, unsigned int cnt,
return false;
}
static int encryptDataAES128CBC(unsigned char *plaintext, int plaintextLen, unsigned char *key,
unsigned char *iv, unsigned char *ciphertext)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciphertextLen;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new()))
return -1;
/* Initialise the encryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits
*/
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
return -1;
/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen))
return -1;
ciphertextLen = len;
/* Finalise the encryption. Further ciphertext bytes may be written at
* this stage.
*/
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
return -1;
ciphertextLen += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertextLen;
}
static int decryptDataAES128CBC(unsigned char *ciphertext, int ciphertextLen, unsigned char *key,
unsigned char *iv, unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciplaintextLen;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new()))
return -1;
/* Initialise the decryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits
*/
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
return -1;
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary.
*/
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen))
return -1;
ciplaintextLen = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
*/
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
return -1;
ciplaintextLen += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciplaintextLen;
}
static void removeFromString(string& value, const string & pattern) {
size_t pos = std::string::npos;
while ((pos = value.find(pattern)) != std::string::npos)
value.erase(pos, pattern.length());
}
/**
* base64
*/
@ -645,3 +745,125 @@ MATCH_CONDITION_START(IsInListCondition) {
return res;
}
} MATCH_CONDITION_END;
CONST_ACTION_2P(SCEncryptCodeAes128CBC, ',', true);
EXEC_ACTION_START(SCEncryptCodeAes128CBC) {
string source_name = (par1.length() && par1[0] == '$') ? par1.substr(1) : par1;
string source_value = resolveVars(par1, sess, sc_sess, event_params);
/* voucher_iv and voucher_key */
vector<string> params = explode(par2, ",");
if (params.size() < 2) {
throw DSMException("encrypt", "cause", "Lack of arguments!\n");
}
if (params[0].empty() || params[1].empty()) {
throw DSMException("encrypt", "cause", "Empty arguments: iv or key!\n");
}
if (source_value.empty()) {
throw DSMException("encrypt", "cause", "Empty arguments: source value!\n");
}
/* multi-arguments list is passed as one string, make sure to remove trash */
for (auto it = params.begin(); it != params.end(); ++it)
{
removeFromString(*it, " ");
removeFromString(*it, "\"");
}
/* vector and key, cast right away for encryption needs */
string rawIv = resolveVars(params[0], sess, sc_sess, event_params);
string rawKey = resolveVars(params[1], sess, sc_sess, event_params);
unsigned char * voucherIv = (unsigned char *)rawIv.c_str();
unsigned char * voucherKey = (unsigned char *)rawKey.c_str();
/* prepare plain code for encryption */
unsigned char * plainCode = (unsigned char *)source_value.c_str();
/* padding with default AES key length */
int paddedLen = source_value.length() + AES_KEY_SIZE - (source_value.length() % AES_KEY_SIZE);
/* a buffer for an encrypted data */
unsigned char encryptedCode[paddedLen];
/* base64() related things */
int flen = 0;
char * encryptedCodeBase64;
int encryptedLen = encryptDataAES128CBC(plainCode, strlen((char *)plainCode),
voucherKey, voucherIv, encryptedCode);
if (encryptedLen == -1) {
throw DSMException("encrypt", "cause", "Encrypt failure!\n");
/* format into base64 */
} else {
encryptedCodeBase64 = base64(encryptedCode, paddedLen, &flen );
DBG("Successfully encrypted the value: <%s>\n", encryptedCodeBase64);
sc_sess->var[source_name] = encryptedCodeBase64;
}
/* base64() uses malloc, don't forget to free() */
free(encryptedCodeBase64);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCDecryptCodeAes128CBC, ',', true);
EXEC_ACTION_START(SCDecryptCodeAes128CBC) {
/* encrypted code must be given in base64 */
string source_name = (par1.length() && par1[0] == '$') ? par1.substr(1) : par1;
string source_value = resolveVars(par1, sess, sc_sess, event_params);
/* voucher_iv and voucher_key */
vector<string> params = explode(par2, ",");
if (params.size() < 2) {
WARN("Lack of arguments!\n");
throw DSMException("encrypt", "cause", "Lack of arguments!\n");
}
if (params[0].empty() || params[1].empty()) {
WARN("Empty arguments: iv or key!\n");
throw DSMException("encrypt", "cause", "Empty arguments: iv or key!\n");
}
if (source_value.empty()) {
WARN("Empty arguments: source value!\n");
throw DSMException("encrypt", "cause", "Empty arguments: source value!\n");
}
/* multi-arguments list is passed as one string, make sure to remove trash */
for (auto it = params.begin(); it != params.end(); ++it)
{
removeFromString(*it, " ");
removeFromString(*it, "\"");
}
/* vector and key, cast right away for encryption needs */
string rawIv = resolveVars(params[0], sess, sc_sess, event_params);
string rawKey = resolveVars(params[1], sess, sc_sess, event_params);
unsigned char * voucherIv = (unsigned char *)rawIv.c_str();
unsigned char * voucherKey = (unsigned char *)rawKey.c_str();
/* base64() related things */
int flen = 0;
/* buffer for decryption, must be big enough */
unsigned char decryptedCode[DEC_BUF_LEN];
/* convert the encrypted code (in base64) into the raw binary data */
unsigned char * encryptedCodeRaw = unbase64(source_value.c_str(), source_value.length(), &flen);
int decrypteLen = decryptDataAES128CBC(encryptedCodeRaw, strlen((char *)encryptedCodeRaw),
voucherKey, voucherIv, decryptedCode);
if (decrypteLen == -1) {
throw DSMException("decrypt", "cause", "Decrypt failure!\n");
} else {
DBG("Successfully decrypted the value: <%s>\n", decryptedCode);
sc_sess->var[source_name] = (char *)decryptedCode;
}
/* unbase64() uses malloc, don't forget to free() */
free(encryptedCodeRaw);
} EXEC_ACTION_END;

@ -35,6 +35,9 @@
#define MOD_CLS_NAME SCUtilsModule
#define DEC_BUF_LEN 512
#define AES_KEY_SIZE 16
DECLARE_MODULE(MOD_CLS_NAME);
DEF_CONDITION_2P(IsInListCondition);
@ -60,6 +63,8 @@ DEF_ACTION_1P(SCUUnescapeCRLFAction);
DEF_ACTION_2P(SCUPlayRingToneAction);
DEF_ACTION_2P(SCEncryptCodeAes128CBC);
DEF_ACTION_2P(SCDecryptCodeAes128CBC);
class DSMRingTone
: public AmRingTone,

Loading…
Cancel
Save