/* PC4 Cipher */
/* 49-bit block cipher in ECB Mode for DMR Radio*/
/* by Alexander PUKALL 2015 */
/* Code free for all, even for commercial software */
/* No restriction to use. Public Domain */
/* Use MD2-II */
/* Use Arc4 */
/* Use Splitmix64 */
/* PC4 encryption uses key-dependent S-boxes */
/* Key can be hexadecimal or user password */
/* Key size can vary from 8 bits to 2112 bits */
/* PC4 uses 253 encryption rounds */
/* The number of round can be reduced */
/* if the DMR processor is too slow */
/* how to compile : gcc pc4.c -o pc4 */
#include <stdio.h>
#include <stdint.h>
#define nbround 254
#define n1 264
short bits[49],temp[49];
uint8_t ptconvert;
uint8_t convert[7];
uint8_t perm[16][256];
uint8_t new1[256];
uint8_t array[49];
uint8_t array2[49];
uint8_t decal[nbround];
uint8_t rngxor[nbround][3];
uint8_t rngxor2[nbround][3];
uint8_t rounds;
uint8_t tab[256];
uint8_t inv[256];
uint8_t permut[3][3];
uint64_t bb;
uint64_t x;
uint8_t tot[3];
uint8_t l[2][3],r[2][3];
uint8_t y,totb;
uint32_t result;
uint8_t xyz, count;
uint8_t keys[16];
unsigned char array_arc4[256];
int i_arc4,j_arc4;
int x1,x2,i;
unsigned char h2[n1];
unsigned char h1[n1*3];
uint64_t next() {
uint64_t z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
void arc4_init(unsigned char key[])
{
int tmp;
for (i_arc4=0;i_arc4<256;i_arc4++)
{
array_arc4[i_arc4]=i_arc4;
}
j_arc4=0;
for (i_arc4=0;i_arc4<256;i_arc4++)
{
j_arc4=(j_arc4+array_arc4[i_arc4]+key[i_arc4%256])%256;
tmp=array_arc4[i_arc4];
array_arc4[i_arc4]=array_arc4[j_arc4];
array_arc4[j_arc4]=tmp;
}
i_arc4=0;
j_arc4=0;
}
unsigned char arc4_output()
{
uint8_t rndbyte,decal;
int tmp, t;
i_arc4=(i_arc4+1)%256;
j_arc4=(j_arc4+array_arc4[i_arc4])%256;
tmp=array_arc4[i_arc4];
array_arc4[i_arc4]=array_arc4[j_arc4];
array_arc4[j_arc4]=tmp;
t=(array_arc4[i_arc4]+array_arc4[j_arc4])%256;
if (xyz==0) bb=next();
decal=56-(8*xyz);
rndbyte=(bb>>decal)& 0xff;
xyz++;
if (xyz==8) xyz=0;
if (count==0)
{
rndbyte=rndbyte^array_arc4[t];
count=1;
}
else
{
rndbyte=rndbyte+array_arc4[t];
count=0;
}
return(rndbyte);
}
void md2_init()
{
x1 = 0;
x2 = 0;
for (i = 0; i < n1; i++)
h2[i] = 0;
for (i = 0; i < n1; i++)
h1[i] = 0;
}
void md2_hashing(unsigned char t1[], size_t b6)
{
static unsigned char s4[256] =
{ 13, 199, 11, 67, 237, 193, 164, 77, 115, 184, 141, 222, 73,
38, 147, 36, 150, 87, 21, 104, 12, 61, 156, 101, 111, 145,
119, 22, 207, 35, 198, 37, 171, 167, 80, 30, 219, 28, 213,
121, 86, 29, 214, 242, 6, 4, 89, 162, 110, 175, 19, 157,
3, 88, 234, 94, 144, 118, 159, 239, 100, 17, 182, 173, 238,
68, 16, 79, 132, 54, 163, 52, 9, 58, 57, 55, 229, 192,
170, 226, 56, 231, 187, 158, 70, 224, 233, 245, 26, 47, 32,
44, 247, 8, 251, 20, 197, 185, 109, 153, 204, 218, 93, 178,
212, 137, 84, 174, 24, 120, 130, 149, 72, 180, 181, 208, 255,
189, 152, 18, 143, 176, 60, 249, 27, 227, 128, 139, 243, 253,
59, 123, 172, 108, 211, 96, 138, 10, 215, 42, 225, 40, 81,
65, 90, 25, 98, 126, 154, 64, 124, 116, 122, 5, 1, 168,
83, 190, 131, 191, 244, 240, 235, 177, 155, 228, 125, 66, 43,
201, 248, 220, 129, 188, 230, 62, 75, 71, 78, 34, 31, 216,
254, 136, 91, 114, 106, 46, 217, 196, 92, 151, 209, 133, 51,
236, 33, 252, 127, 179, 69, 7, 183, 105, 146, 97, 39, 15,
205, 112, 200, 166, 223, 45, 48, 246, 186, 41, 148, 140, 107,
76, 85, 95, 194, 142, 50, 49, 134, 23, 135, 169, 221, 210,
203, 63, 165, 82, 161, 202, 53, 14, 206, 232, 103, 102, 195,
117, 250, 99, 0, 74, 160, 241, 2, 113};
int b1,b2,b3,b4,b5;
b4=0;
while (b6) {
for (; b6 && x2 < n1; b6--, x2++) {
b5 = t1[b4++];
h1[x2 + n1] = b5;
h1[x2 + (n1*2)] = b5 ^ h1[x2];
x1 = h2[x2] ^= s4[b5 ^ x1];
}
if (x2 == n1)
{
b2 = 0;
x2 = 0;
for (b3 = 0; b3 < (n1+2); b3++) {
for (b1 = 0; b1 < (n1*3); b1++)
b2 = h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
void md2_end(unsigned char h4[n1])
{
unsigned char h3[n1];
int i, n4;
n4 = n1 - x2;
for (i = 0; i < n4; i++) h3[i] = n4;
md2_hashing(h3, n4);
md2_hashing(h2, sizeof(h2));
for (i = 0; i < n1; i++) h4[i] = h1[i];
}
int mixy(int nn2)
{
int maxo;
maxo = arc4_output();
return (maxo%nn2);
}
void mixer(uint8_t *mixu, int nn)
{
int ii, jj, tmmp;
for (ii = nn - 1; ii > 0; ii--)
{
jj = mixy(ii + 1);
tmmp = mixu[jj];
mixu[jj] = mixu[ii];
mixu[ii] = tmmp;
}
}
void create_keys(unsigned char key1[], size_t size1)
{
int i,w,k;
unsigned char h4[n1];
md2_init();
md2_hashing(key1, size1);
md2_end(h4);
for (i=0;i<16;i++) keys[i]=h4[i];
arc4_init(h4);
x=0;
for (i=0;i<8;i++) x=(x<<8)+(h4[256+i]&0xff);
xyz=0;
count=0;
for (i=0;i<20000;i++) arc4_output();
uint8_t numbers[256];
for (w=0;w<16;w++)
{
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (i = 0; i < 256; i++) {numbers[i]= i;}
mixer(numbers, 256);
for ( i = 0; i < 256; i++)
{
perm[w][i]=numbers[i];
}
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (int i = 0; i < 256; i++) {numbers[i]= i;}
mixer(numbers, 256);
for (int i = 0; i < 256; i++)
{
new1[i]=numbers[i];
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (int i = 0; i < 49; i++) {numbers[i]= i;}
mixer(numbers, 49);
for (int i = 0; i < 49; i++)
{
array[i]=numbers[i];
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (int i=0;i<nbround;i++)
{
decal[i]=(arc4_output()%23)+1;
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (w=0;w<3;w++)
{
for (int i=0;i<nbround;i++)
{
rngxor[i][w]=arc4_output();
}
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (int i = 0; i < 49; i++) {numbers[i]= i;}
mixer(numbers, 49);
for (int i = 0; i < 49; i++)
{
array2[i]=numbers[i];
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (int i = 0; i < 256; i++) {numbers[i]= i;}
mixer(numbers, 256);
for (int i = 0; i < 256; i++)
{
tab[i]=numbers[i];
inv[tab[i]] = (unsigned char)i;
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (w=0;w<3;w++)
{
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (i = 0; i < 3; i++) {numbers[i]= i;}
mixer(numbers, 3);
for ( i = 0; i < 3; i++)
{
permut[w][i]=numbers[i];
}
}
k=arc4_output()+256;
for (i=0;i<k;i++) arc4_output();
for (w=0;w<3;w++)
{
for (int i=0;i<nbround;i++)
{
rngxor2[i][w]=arc4_output();
}
}
}
uint32_t ror(uint32_t x,int shift,int bits)
{
uint32_t m0=(1<<(bits-shift))-1;
uint32_t m1=(1<<shift)-1;
return ((x>>shift)&m0) | ((x&m1)<<(bits-shift));
}
uint32_t rol(uint32_t x,int shift,int bits)
{
uint32_t m0=(1<<(bits-shift))-1;
uint32_t m1=(1<<shift)-1;
return ((x&m0)<<shift) | ((x>>(bits-shift))&m1);
}
void compute(uint8_t *tab1, uint8_t round)
{
tot[0]=(perm[round][tab1[permut[0][0]]]+perm[round][tab1[permut[0][1]]])^perm[round][tab1[permut[0][2]]];
tot[0]=tot[0]+new1[tot[0]];
tot[1]=(perm[round][tab1[permut[1][0]]]+perm[round][tab1[permut[1][1]]])^perm[round][tab1[permut[1][2]]];
tot[1]=tot[1]+new1[tot[1]];
tot[2]=(perm[round][tab1[permut[2][0]]]+perm[round][tab1[permut[2][1]]])^perm[round][tab1[permut[2][2]]];
tot[2]=tot[2]+new1[tot[2]];
}
void pc4encrypt()
{
int i;
totb=0;
for (int i=0;i<3;i++)
{
l[0][i]=convert[i];
r[0][i]=convert[i+3];
}
for (int i=1;i<=rounds;i++)
{
totb=totb^r[(i-1)%2][0];
totb=totb^r[(i-1)%2][1];
totb=totb^r[(i-1)%2][2];
r[(i-1)%2][0]=r[(i-1)%2][0] + ((unsigned char)~rngxor2[rounds-i][0]);
r[(i-1)%2][1]=r[(i-1)%2][1] ^ ((unsigned char)~rngxor2[rounds-i][1]);
r[(i-1)%2][2]=r[(i-1)%2][2] + ((unsigned char)~rngxor2[rounds-i][2]);
result=0;
result=result+(r[(i-1)%2][0]<<16);
result=result+(r[(i-1)%2][1]<<8);
result=(result+r[(i-1)%2][2]);
result=rol(result,decal[i-1],24);
r[(i-1)%2][0]=result>>16;
r[(i-1)%2][1]=(result>>8)& 0xff;
r[(i-1)%2][2]=result & 0xff;
r[(i-1)%2][0]=tab[(r[(i-1)%2][0])];
r[(i-1)%2][0]=r[(i-1)%2][0] ^ rngxor[i-1][0];
r[(i-1)%2][1]=inv[(r[(i-1)%2][1])];
r[(i-1)%2][1]=r[(i-1)%2][1] - rngxor[i-1][1];
r[(i-1)%2][2]=tab[(r[(i-1)%2][2])];
r[(i-1)%2][2]=r[(i-1)%2][2] ^ rngxor[i-1][2];
compute(r[(i-1)%2],(i-1)%16);
l[i%2][0]=r[(i-1)%2][0];
r[i%2][0]=l[(i-1)%2][0] -tot[0];
l[i%2][1]=r[(i-1)%2][1];
r[i%2][1]=l[(i-1)%2][1] ^tot[1];
l[i%2][2]=r[(i-1)%2][2];
r[i%2][2]=l[(i-1)%2][2] -tot[2];
}
for (i=0;i<3;i++)
{
convert[i+3]=l[(rounds-1)%2][i];
convert[i]=r[(rounds-1)%2][i];
}
totb=totb%2;
}
void pc4decrypt()
{
int i;
totb=0;
for (i=0;i<3;i++)
{
l[0][i]=convert[i];
r[0][i]=convert[i+3];
}
y=(rounds-1)%16;
if (y==0) y=16;
for (i=1;i<=rounds;i++)
{
y--;
compute(r[(i-1)%2],y);
if (y==0) y=16;
result=0;
l[(i-1)%2][0]=l[(i-1)%2][0] ^ rngxor[rounds-i][0];
l[(i-1)%2][0]=inv[(l[(i-1)%2][0])];
l[(i-1)%2][1]=l[(i-1)%2][1] + rngxor[rounds-i][1];
l[(i-1)%2][1]=tab[(l[(i-1)%2][1])];
l[(i-1)%2][2]=l[(i-1)%2][2] ^ rngxor[rounds-i][2];
l[(i-1)%2][2]=inv[(l[(i-1)%2][2])];
result=result+(l[(i-1)%2][0]<<16);
result=result+(l[(i-1)%2][1]<<8);
result=(result+l[(i-1)%2][2]);
result=ror(result,decal[rounds-i],24);
l[(i-1)%2][0]=result>>16;
l[(i-1)%2][1]=(result>>8)& 0xff;
l[(i-1)%2][2]=result & 0xff;
l[(i-1)%2][0]=l[(i-1)%2][0] - ((unsigned char)~rngxor2[i-1][0]);
l[(i-1)%2][1]=l[(i-1)%2][1] ^ ((unsigned char)~rngxor2[i-1][1]);
l[(i-1)%2][2]=l[(i-1)%2][2] - ((unsigned char)~rngxor2[i-1][2]);
totb=totb^l[(i-1)%2][0];
totb=totb^l[(i-1)%2][1];
totb=totb^l[(i-1)%2][2];
l[i%2][0]=r[(i-1)%2][0];
r[i%2][0]=l[(i-1)%2][0] + tot[0];
l[i%2][1]=r[(i-1)%2][1];
r[i%2][1]=l[(i-1)%2][1] ^ tot[1];
l[i%2][2]=r[(i-1)%2][2];
r[i%2][2]=l[(i-1)%2][2] + tot[2];
}
for (i=0;i<3;i++)
{
convert[i+3]=l[(rounds-1)%2][i];
convert[i]=r[(rounds-1)%2][i];
}
totb=totb%2;
}
void binhex(short *z, int length){
short *b;
b=(short *)z;
uint8_t i,j;
for(i = 0; i < length; i = j){
uint8_t a = 0;
for(j = i; j < i+8; ++j){
a |= b[((7-(j%8))+j)-(j%8)]<<(j-i);
}
convert[ptconvert]=a;
ptconvert++;
}
}
void hexbin(short *q, uint8_t w, uint8_t hex) {
short *bits;
bits=(short *)q;
for (uint8_t i = 0; i < 8; ++i) {
bits[(7+w)-i] = (hex >> i) & 1;
}
}
void main()
{
rounds=254;
// Plaintext frame //
uint8_t frame1[49]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0};
uint8_t i,w;
// KEY 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
unsigned char data[16]={1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* The key creation procedure is very slow, it only needs to be done once */
/* as long as the user does not change the key. You can encrypt and decrypt */
/* as many frames as you want without having to recreate the key. */
create_keys(data,16); // KEY creation : key, length of key
printf("\nENCRYPTION PROCESS with key 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01\n");
for (i=0;i<49;i++) bits[i]=frame1[i];
printf("Plaintext Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
for (i=0; i<49; i++) temp[i]=bits[array[i]];
for (i=0; i<49; i++) bits[i]=temp[i];
ptconvert=0;
binhex(bits, 48);
pc4encrypt();
for (int q=0;q<6;q++)
{
w=q*8;
hexbin(bits, w,convert[q]);
}
bits[48]=bits[48]^totb;
for (i=0; i<49; i++) temp[array2[i]]=bits[i];
for (i=0; i<49; i++) bits[i]=temp[i];
printf("\nEncrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
printf("\n\nDECRYPTION PROCESS with key 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01\n");
printf("Encrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
for (i=0; i<49; i++) temp[i]=bits[array2[i]];
for (i=0; i<49; i++) bits[i]=temp[i];
ptconvert=0;
binhex(bits, 48);
pc4decrypt();
for (int q=0;q<6;q++)
{
w=q*8;
hexbin(bits, w,convert[q]);
}
bits[48]=bits[48]^totb;
for (i=0; i<49; i++) temp[array[i]]=bits[i];
for (i=0; i<49; i++) bits[i]=temp[i];
printf("\nDecrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
/* USER PASSWORD */
// plaintext frame
uint8_t frame2[49]={
0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,0,1};
/* The key creation procedure is very slow, it only needs to be done once */
/* as long as the user does not change the key. You can encrypt and decrypt */
/* as many frames as you want without having to recreate the key. */
create_keys("My Top Secret Password!",23); // KEY creation : password, length of password
printf("\n\nENCRYPTION PROCESS with user password 'My Top Secret Password!'\n");
for (i=0;i<49;i++) bits[i]=frame2[i];
printf("Plaintext Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
for (i=0; i<49; i++) temp[i]=bits[array[i]];
for (i=0; i<49; i++) bits[i]=temp[i];
ptconvert=0;
binhex(bits, 48);
pc4encrypt();
for (int q=0;q<6;q++)
{
w=q*8;
hexbin(bits, w,convert[q]);
}
bits[48]=bits[48]^totb;
for (i=0; i<49; i++) temp[array2[i]]=bits[i];
for (i=0; i<49; i++) bits[i]=temp[i];
printf("\nEncrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
printf("\n\nDECRYPTION PROCESS with user password 'My Top Secret Password!'\n");
printf("Encrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
for (i=0; i<49; i++) temp[i]=bits[array2[i]];
for (i=0; i<49; i++) bits[i]=temp[i];
ptconvert=0;
binhex(bits, 48);
pc4decrypt();
for (int q=0;q<6;q++)
{
w=q*8;
hexbin(bits, w,convert[q]);
}
bits[48]=bits[48]^totb;
for (i=0; i<49; i++) temp[array[i]]=bits[i];
for (i=0; i<49; i++) bits[i]=temp[i];
printf("\nDecrypted Frame :\n");
for (i=0;i<49;i++) printf("%d",bits[i]);
printf("\n");
}
/*
ENCRYPTION PROCESS with key 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
Plaintext Frame :
0000000000000000000000000000000000000000000000000
Encrypted Frame :
1000110101001100000100001000111011010010001001101
DECRYPTION PROCESS with key 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
Encrypted Frame :
1000110101001100000100001000111011010010001001101
Decrypted Frame :
0000000000000000000000000000000000000000000000000
ENCRYPTION PROCESS with user password 'My Top Secret Password!'
Plaintext Frame :
0010000001000000001000000000000010000000001000001
Encrypted Frame :
0000011100110010010110111010110010110100000110100
DECRYPTION PROCESS with user password 'My Top Secret Password!'
Encrypted Frame :
0000011100110010010110111010110010110100000110100
Decrypted Frame :
0010000001000000001000000000000010000000001000001
*/