Skip to content

Commit

Permalink
pcbc mode
Browse files Browse the repository at this point in the history
  • Loading branch information
tugrul committed Mar 12, 2023
1 parent 0e59685 commit 21c90a7
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ assert(plaintext.equals(enigma.decrypt(ciphertext)), 'decrypted ciphertext shoul
All the following block cipher mode algorithms ported from libmcrypt

* CBC
* PCBC
* CFB (CFB8)
* CTR
* ECB
Expand Down
1 change: 1 addition & 0 deletions addon/lib/mode-suite.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

#include "mode/cbc.h"
#include "mode/pcbc.h"
#include "mode/cfb.h"
#include "mode/ctr.h"
#include "mode/ecb.h"
Expand Down
99 changes: 99 additions & 0 deletions addon/lib/mode/pcbc.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

#include "pcbc.h"
#include <cmath>

namespace cryptian {

namespace mode {

namespace pcbc {

bool Pcbc::isPaddingRequired() {
return true;
}

std::vector<char> Cipher::transform(const std::vector<char> chunk) {

const std::size_t blockSize = _algorithm->getBlockSize();
const std::size_t blocksCount = std::floor(chunk.size() / blockSize);

std::vector<char> blocks(blockSize * blocksCount);

char* block = new char[blockSize]();
char* propagation = new char[blockSize]();

for (size_t i = 0 ; i < blocksCount; i++) {

size_t offset = i * blockSize;

std::copy_n(chunk.begin() + offset, blockSize, block);
std::copy_n(chunk.begin() + offset, blockSize, propagation);

for (size_t j = 0; j < blockSize; j++) {
block[j] ^= _register[j];
}

std::vector<char> cipher = _algorithm->encrypt(std::vector<char>(block, block + blockSize));

std::copy(cipher.begin(), cipher.end(), blocks.begin() + offset);

std::copy(cipher.begin(), cipher.end(), _register.begin());

for (size_t j = 0; j < blockSize; j++) {
_register[j] ^= propagation[j];
}

}

delete[] block;
delete[] propagation;

return blocks;
}


std::vector<char> Decipher::transform(const std::vector<char> chunk) {

const std::size_t blockSize = _algorithm->getBlockSize();
const std::size_t blocksCount = std::floor(chunk.size() / blockSize);

std::vector<char> blocks(blockSize * blocksCount);

char* block = new char[blockSize]();
char* propagation = new char[blockSize]();

for (size_t i = 0 ; i < blocksCount; i++) {

size_t offset = i * blockSize;

std::copy_n(chunk.begin() + offset, blockSize, block);
std::copy_n(chunk.begin() + offset, blockSize, propagation);

std::vector<char> cipher = _algorithm->decrypt(std::vector<char>(block, block + blockSize));

for (size_t j = 0; j < blockSize; j++) {
cipher[j] ^= _register[j];
}

std::copy(cipher.begin(), cipher.end(), blocks.begin() + offset);

std::copy(cipher.begin(), cipher.end(), _register.begin());

for (size_t j = 0; j < blockSize; j++) {
_register[j] ^= propagation[j];
}

}

delete[] block;
delete[] propagation;

return blocks;

}

};

};

};
31 changes: 31 additions & 0 deletions addon/lib/mode/pcbc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

#include <mode-base.h>

namespace cryptian {

namespace mode {

namespace pcbc {

class Pcbc : public cryptian::mode::ModeBase {
public:
bool isPaddingRequired();
};


class Cipher : public Pcbc {
public:
std::vector<char> transform(const std::vector<char>);

};

class Decipher: public Pcbc {
public:
std::vector<char> transform(const std::vector<char>);
};

};

};

};
1 change: 1 addition & 0 deletions addon/src/node/cryptian.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ NAN_MODULE_INIT(Init) {
EXPORT_ALGORITHM_STREAM(Wake)

EXPORT_MODE(cbc)
EXPORT_MODE(pcbc)
EXPORT_MODE(cfb)
EXPORT_MODE(ctr)
EXPORT_MODE(ecb)
Expand Down
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"addon/lib/algorithm/xtea.cc",
"addon/lib/algorithm/dummy.cc",
"addon/lib/mode/cbc.cc",
"addon/lib/mode/pcbc.cc",
"addon/lib/mode/cfb.cc",
"addon/lib/mode/ctr.cc",
"addon/lib/mode/ecb.cc",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type ModeCipherDecipher = {

export enum ModeList {
Cbc = 'cbc',
Pcbc = 'pcbc',
Cfb = 'cfb',
Ctr = 'ctr',
Ecb = 'ecb',
Expand Down
115 changes: 115 additions & 0 deletions test/mode/pcbc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@


import {expect} from '@jest/globals';

import { randomBytes } from 'crypto';
import assert from 'assert';
import cryptian from '../..';

const {algorithm: {Dummy}, mode: {pcbc}} = cryptian;


(typeof pcbc === 'object' ? describe : describe.skip) ('pcbc', () => {

const plaintext = Buffer.from('0a4e0adf528e898594699188930c6247493f3c17fce03723e123517d62533c4a', 'hex');
const ciphertext = Buffer.from('09c31ca738fad69e97e487f0f9783d5c4ab22a6f96946838e2ae470508276351', 'hex');

const iv = Buffer.from('038d16786a745f1b', 'hex');
const dummy = new Dummy();


describe('cipher', () => {

it('undivided', () => {
const cipher = new pcbc.Cipher(dummy, iv);

assert(ciphertext.equals(cipher.transform(plaintext)), 'transformed plaintext should be equal to ciphertext');
});

it('divided', () => {
const cipher = new pcbc.Cipher(dummy, iv);
const part = Buffer.concat([
cipher.transform(plaintext.slice(0, 8)),
cipher.transform(plaintext.slice(8))
]);

assert(ciphertext.equals(part), 'transformed plaintext should be equal to ciphertext');
});

describe('throw padding exception', () => {

it('getBlockSize', () => {
const cipher = new pcbc.Cipher(dummy, iv);
assert.equal(cipher.getBlockSize(), dummy.getBlockSize(), 'cipher.getBlockSize() should equal algorithm.getBlockSize()');
});

it('transform using 5 bytes', () => {
const cipher = new pcbc.Cipher(dummy, iv);

expect(() => cipher.transform(randomBytes(5)))
.toThrowError('Data size should be aligned to algorithm block size.');

});

it('transform using 12 bytes', () => {

const cipher = new pcbc.Cipher(dummy, iv);

expect(() => cipher.transform(randomBytes(12)))
.toThrowError('Data size should be aligned to algorithm block size.');

});

});

});


describe('decipher', () => {


it('undivided', () => {
const decipher = new pcbc.Decipher(dummy, iv);

assert(plaintext.equals(decipher.transform(ciphertext)), 'transformed ciphertext should be equal to plaintext');
});


it('divided', () => {
const decipher = new pcbc.Decipher(dummy, iv);
const part = Buffer.concat([
decipher.transform(ciphertext.slice(0, 8)),
decipher.transform(ciphertext.slice(8))
]);

assert(plaintext.equals(part), 'transformed ciphertext should be equal to plaintext');
});


describe('throw padding exception', () => {

it('getBlockSize', () => {
const decipher = new pcbc.Decipher(dummy, iv);
assert.equal(decipher.getBlockSize(), dummy.getBlockSize(), 'decipher.getBlockSize() should equal algorithm.getBlockSize()');
});


it('transform using 5 bytes', () => {
const decipher = new pcbc.Decipher(dummy, iv);

expect(() => decipher.transform(randomBytes(5))).toThrowError('Data size should be aligned to algorithm block size.');

});


it('transform using 12 bytes', () => {
const decipher = new pcbc.Decipher(dummy, iv);

expect(() => decipher.transform(randomBytes(12))).toThrowError('Data size should be aligned to algorithm block size.');
});

});

});

});

0 comments on commit 21c90a7

Please sign in to comment.