// RC6.h #ifndef RC6h #define RC6h #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 /* RC6 is trademark of RSA Data Security, Inc. They're American, so they're patenting it too. The difficulty of breaking RC6 is estimated as being min(2^(8*KeyBytes),2^704) The algorithm can have many variants. The variant is described as follows: RC6-/W/R/B W = Word Size in bits (Default is 32) I made a version with a templated base class to handle different values of W, but found no advantages, particularly as there are no official test vectors for the non-32 bit algorithm. R = Number of Rounds (R=(Depth+2)<<2; Minimum is Depth=0 (R=4), Default is Depth=3 (R=20), my code has maximum Depth=65535 (R=262148) which would use a 2MB State Table. B = Key Length in Bytes ((0 <= B <= 255) Default is 16) AES Submission required W=32 and R=20 The constructor handles the parameter ranges by taking integer and creating a valid value from it: So the number of rounds, R, can be entered as a Depth; The actual value used is R=(Depth+2)<<2; which puts it in the correct range. The Key will be truncated if it exceeds 255 Bytes to suit the RC6 Standard. Usage: const char* Key="Patently obvious"; CBytes Text("This is the message that will get encrypted."); CRC6(Key, Text); This encrypts 'Text' using default RC6 settings. The length of 'Text' increases by upto 16 Bytes (unless 'Text' is an empty string) - the last Byte is 0xE6 to indicate encrypted data. ...do something with 'Text' here CRC6(Key, Text); This decrypts 'Text'. Key,Data,Cipher 0x00000000000000000000000000000000,0x00000000000000000000000000000000,0x8FC3A53656B1F778C129DF4E9848A41E 0x0123456789ABCDEF0112233445566778,0x02132435465768798A9BACBDCEDFE0F1,0x524E192F4715C6231F51F6367EA43F18 0x000000000000000000000000000000000000000000000000,0x00000000000000000000000000000000,0x6CD61BCB190B30384E8A3F168690AE82 0x0123456789ABCDEF0112233445566778899AABBCCDDEEFF0,0x02132435465768798A9BACBDCEDFE0F1,0x688329D019E505041E52E92AF95291D4 0x0000000000000000000000000000000000000000000000000000000000000000,0x00000000000000000000000000000000,0x8F5FBD0510D15FA893FA3FDA6E857EC2 0x0123456789ABCDEF0112233445566778899AABBCCDDEEFF01032547698BADCFE,0x02132435465768798A9BACBDCEDFE0F1,0xC8241816F0D7E48920AD16A1674E5D48 */ #include "Bytes.h" class CRC6 { const DWORD R; // R = Number of Rounds (R=(Depth+2)<<2; Minimum is Depth=0 (R=4), Default is Depth=3 (R=20), my code has maximum Depth=65535 (R=262148) which would use a 2MB State Table. const BYTE W; // W = Word Size in bits (Default is 32) I made a version with a templated base class to handle different values of W, but found no advantages, particularly as there are no official test vectors for the non-32 bit algorithm. const BYTE WBits; // WBits=5; Number of Bits in W. In the original spec: WBits=log(W)/log(2); but could be initialised with: for(BYTE w=W; w>>=1; ++Mask,++WBits) Mask<<=1; const BYTE WBytes; // Number of Bytes to hold one W Word const DWORD Mask; // Mask=0x1F; The WBits least significant bits of Mask are set to 1. Used to get the remainder of Bits by ANDing. DWORD* States; // State Table used throughout encryption and decryption public: CRC6(const char* Key, CBytes& iBytes, WORD Depth=3) : R((Depth+2)<<2), W(32), WBits(5), WBytes((W+7)>>3), Mask(0x1F), States(0) { if(iBytes.IsEmpty()) return; States=(DWORD*)LocalAlloc(LMEM_FIXED, (UINT)(WBytes*(2*R+4))); // LocalAlloc allocates process-private memory. // if(Key.GetLength()>255) Key.Length=255; // Just to keep it in the RC6 Standard :-) if(iBytes[iBytes.Length-1]!=(BYTE)0xE6) { // Encoded strings have a 0xE6 trailing Byte. DWORD Length=(iBytes.Length+(4*sizeof(DWORD))-1)&~0xF; // Round up the Buffer size to suit four DWORD blocks CBytes oBytes(Length+sizeof(DWORD)+2, false); // Need a bigger buffer: add space for the original length 'NullTerminated' Flag and Marker memcpy(oBytes.Bytes, iBytes.Bytes, iBytes.Length); memset(oBytes.Bytes+iBytes.Length, 0, oBytes.Length-iBytes.Length); BYTE* ptr=(BYTE*)Encode(Key, (DWORD*)oBytes.Bytes, Length); *((DWORD*)ptr)=iBytes.Length; // Append the original Length ptr+=sizeof DWORD; *ptr++=iBytes.NullTerminated; // Append the 0xE6 token to indicate compressed Data *ptr++=(BYTE)0xE6; // Append the 0xE6 token to indicate Encrypted RC6 Data oBytes.Length=ptr-oBytes.Bytes; oBytes.MoveTo(iBytes); }else{ // Decode: int Length=iBytes.GetLength()-sizeof(DWORD)-2; // Encrypted Data Length iBytes.Length=*((DWORD*)(iBytes.Bytes+Length)); iBytes.NullTerminated=(*(iBytes.Bytes+Length+sizeof(DWORD))!=0); Decode(Key, (DWORD*)iBytes.Bytes, Length); } } virtual ~CRC6() { // Don't leave Key traces in memory if(!States) return; memset(States, rand(), (size_t)(WBytes*(2*R+4))); LocalFree(States); } private: DWORD RotateL(DWORD x, DWORD Bits) {Bits&=Mask; return (x>>(W-Bits)) | (x<>Bits);} void Seed(const CBytes& Key) { BYTE c=(BYTE)Key.GetLength(); DWORD* L=new DWORD[(c>>2)+1]; L[c>>2]=0; // Pad out the Key with zeros to be a multiple of 4 BYTES in length (write a DWORD of zeros at the end) char* ptr=(char*)L; const char* src=Key; DWORD i; for(i=c; i--; *ptr++=*src++); DWORD* dst=States; DWORD LastState=*dst++=0xB7E15163; // 0xB7E15163 = Binary expansion of e-2 (e=natural log base) DWORD iTop=2*R+4; for(i=iTop; --i; *dst++=(LastState+=0x9E3779B9)); // 0x9E3779B9 = Binary expansion of Phi-1 (Phi=Golden Ratio) DWORD A=0, B=0, s=3*max(c, iTop), j=i=0, jTop=c/(W>>3); while(s--) { A=States[i]=RotateL(States[i]+A+B, 3); B=L[j]=RotateL(L[j]+A+B, A+B); if(++i>=iTop) i=0; if(++j>=jTop) j=0; } delete[] L; } DWORD* Encode(const CBytes& Key, DWORD* ptr, DWORD Length) { Seed(Key); for(DWORD n=0; n>1) { DWORD* State=States; DWORD A=*ptr++; DWORD B=*ptr++ + *State++; DWORD C=*ptr++; DWORD D=*ptr + *State++; for(DWORD i=R; i--;) { DWORD t=RotateL(B*(2*B+1), WBits); DWORD u=RotateL(D*(2*D+1), WBits); A=RotateL(A^t, u) + *State++; C=RotateL(C^u, t) + *State++; DWORD temp=A; A=B; B=C; C=D; D=temp; } *ptr--=D; *ptr--=C+State[1]; *ptr--=B; *ptr=A+*State; ptr+=4; } return ptr; } void Decode(const CBytes& Key, DWORD* ptr, DWORD Length) { Seed(Key); for(DWORD n=0; n>1) { DWORD* State=States+2*R+4; DWORD A=*ptr++; DWORD B=*ptr++; DWORD C=*ptr++; DWORD D=*ptr; C-=*--State; A-=*--State; for(DWORD i=R; i>=1; --i) { DWORD temp=D; D=C; C=B; B=A; A=temp; DWORD u=RotateL(D*(2*D+1), WBits); DWORD t=RotateL(B*(2*B+1), WBits); C=(RotateR(C-*--State, t))^u; A=(RotateR(A-*--State, u))^t; } *ptr--=D-*--State; *ptr--=C; *ptr--=B-*--State; *ptr=A; ptr+=4; } } }; #endif //ndef RC6h