2022-10-29 03:09:41 +09:00
/**
* @author mikecat
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs" ;
import Utils from "../Utils.mjs" ;
import { toHexFast } from "../lib/Hex.mjs" ;
import OperationError from "../errors/OperationError.mjs" ;
/**
2022-11-25 16:48:54 +00:00
* Rabbit operation
2022-10-29 03:09:41 +09:00
*/
2022-11-25 16:48:54 +00:00
class Rabbit extends Operation {
2022-10-29 03:09:41 +09:00
/**
2022-11-25 16:48:54 +00:00
* Rabbit constructor
2022-10-29 03:09:41 +09:00
*/
constructor () {
super ();
2022-11-25 16:48:54 +00:00
this . name = "Rabbit" ;
2022-10-29 03:09:41 +09:00
this . module = "Ciphers" ;
2022-11-25 16:48:54 +00:00
this . description = "Rabbit is a high-speed stream cipher introduced in 2003 and defined in RFC 4503.<br><br>The cipher uses a 128-bit key and an optional 64-bit initialization vector (IV).<br><br>big-endian: based on RFC4503 and RFC3447<br>little-endian: compatible with Crypto++" ;
2022-10-29 03:09:41 +09:00
this . infoURL = "https://wikipedia.org/wiki/Rabbit_(cipher)" ;
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
"name" : "Key" ,
"type" : "toggleString" ,
"value" : "" ,
"toggleValues" : [ "Hex" , "UTF8" , "Latin1" , "Base64" ]
},
{
"name" : "IV" ,
"type" : "toggleString" ,
"value" : "" ,
"toggleValues" : [ "Hex" , "UTF8" , "Latin1" , "Base64" ]
},
{
"name" : "Endianness" ,
"type" : "option" ,
"value" : [ "Big" , "Little" ]
},
{
"name" : "Input" ,
"type" : "option" ,
"value" : [ "Raw" , "Hex" ]
},
{
"name" : "Output" ,
"type" : "option" ,
"value" : [ "Raw" , "Hex" ]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run ( input , args ) {
const key = Utils . convertToByteArray ( args [ 0 ]. string , args [ 0 ]. option ),
iv = Utils . convertToByteArray ( args [ 1 ]. string , args [ 1 ]. option ),
endianness = args [ 2 ],
inputType = args [ 3 ],
outputType = args [ 4 ];
const littleEndian = endianness === "Little" ;
if ( key . length !== 16 ) {
throw new OperationError ( `Invalid key length: ${ key . length } bytes (expected: 16)` );
}
if ( iv . length !== 0 && iv . length !== 8 ) {
throw new OperationError ( `Invalid IV length: ${ iv . length } bytes (expected: 0 or 8)` );
}
// Inner State
2022-10-29 03:33:30 +09:00
const X = new Uint32Array ( 8 ), C = new Uint32Array ( 8 );
2022-10-29 03:09:41 +09:00
let b = 0 ;
// Counter System
const A = [
0x4d34d34d , 0xd34d34d3 , 0x34d34d34 , 0x4d34d34d ,
0xd34d34d3 , 0x34d34d34 , 0x4d34d34d , 0xd34d34d3
];
const counterUpdate = function () {
for ( let j = 0 ; j < 8 ; j ++ ) {
const temp = C [ j ] + A [ j ] + b ;
b = ( temp / (( 1 << 30 ) * 4 )) >>> 0 ;
2022-10-29 03:33:30 +09:00
C [ j ] = temp ;
2022-10-29 03:09:41 +09:00
}
};
// Next-State Function
const g = function ( u , v ) {
const uv = ( u + v ) >>> 0 ;
const upper = uv >>> 16 , lower = uv & 0xffff ;
const upperUpper = upper * upper ;
const upperLower2 = 2 * upper * lower ;
const lowerLower = lower * lower ;
const mswTemp = upperUpper + (( upperLower2 / ( 1 << 16 )) >>> 0 );
const lswTemp = lowerLower + ( upperLower2 & 0xffff ) * ( 1 << 16 );
const msw = mswTemp + (( lswTemp / (( 1 << 30 ) * 4 )) >>> 0 );
const lsw = lswTemp >>> 0 ;
2022-10-29 03:33:30 +09:00
return lsw ^ msw ;
2022-10-29 03:09:41 +09:00
};
const leftRotate = function ( value , width ) {
return ( value << width ) | ( value >>> ( 32 - width ));
};
const nextStateHelper1 = function ( v0 , v1 , v2 ) {
2022-10-29 03:33:30 +09:00
return v0 + leftRotate ( v1 , 16 ) + leftRotate ( v2 , 16 );
2022-10-29 03:09:41 +09:00
};
const nextStateHelper2 = function ( v0 , v1 , v2 ) {
2022-10-29 03:33:30 +09:00
return v0 + leftRotate ( v1 , 8 ) + v2 ;
2022-10-29 03:09:41 +09:00
};
2022-10-29 03:33:30 +09:00
const G = new Uint32Array ( 8 );
2022-10-29 03:09:41 +09:00
const nextState = function () {
for ( let j = 0 ; j < 8 ; j ++ ) {
G [ j ] = g ( X [ j ], C [ j ]);
}
X [ 0 ] = nextStateHelper1 ( G [ 0 ], G [ 7 ], G [ 6 ]);
X [ 1 ] = nextStateHelper2 ( G [ 1 ], G [ 0 ], G [ 7 ]);
X [ 2 ] = nextStateHelper1 ( G [ 2 ], G [ 1 ], G [ 0 ]);
X [ 3 ] = nextStateHelper2 ( G [ 3 ], G [ 2 ], G [ 1 ]);
X [ 4 ] = nextStateHelper1 ( G [ 4 ], G [ 3 ], G [ 2 ]);
X [ 5 ] = nextStateHelper2 ( G [ 5 ], G [ 4 ], G [ 3 ]);
X [ 6 ] = nextStateHelper1 ( G [ 6 ], G [ 5 ], G [ 4 ]);
X [ 7 ] = nextStateHelper2 ( G [ 7 ], G [ 6 ], G [ 5 ]);
};
// Key Setup Scheme
2022-10-29 03:33:30 +09:00
const K = new Uint16Array ( 8 );
2022-10-29 03:09:41 +09:00
if ( littleEndian ) {
for ( let i = 0 ; i < 8 ; i ++ ) {
2022-10-29 03:33:30 +09:00
K [ i ] = ( key [ 1 + 2 * i ] << 8 ) | key [ 2 * i ];
2022-10-29 03:09:41 +09:00
}
} else {
for ( let i = 0 ; i < 8 ; i ++ ) {
2022-10-29 03:33:30 +09:00
K [ i ] = ( key [ 14 - 2 * i ] << 8 ) | key [ 15 - 2 * i ];
2022-10-29 03:09:41 +09:00
}
}
for ( let j = 0 ; j < 8 ; j ++ ) {
if ( j % 2 === 0 ) {
2022-10-29 03:33:30 +09:00
X [ j ] = ( K [( j + 1 ) % 8 ] << 16 ) | K [ j ];
C [ j ] = ( K [( j + 4 ) % 8 ] << 16 ) | K [( j + 5 ) % 8 ];
2022-10-29 03:09:41 +09:00
} else {
2022-10-29 03:33:30 +09:00
X [ j ] = ( K [( j + 5 ) % 8 ] << 16 ) | K [( j + 4 ) % 8 ];
C [ j ] = ( K [ j ] << 16 ) | K [( j + 1 ) % 8 ];
2022-10-29 03:09:41 +09:00
}
}
for ( let i = 0 ; i < 4 ; i ++ ) {
counterUpdate ();
nextState ();
}
for ( let j = 0 ; j < 8 ; j ++ ) {
2022-10-29 03:33:30 +09:00
C [ j ] = C [ j ] ^ X [( j + 4 ) % 8 ];
2022-10-29 03:09:41 +09:00
}
// IV Setup Scheme
if ( iv . length === 8 ) {
const getIVValue = function ( a , b , c , d ) {
if ( littleEndian ) {
2022-10-29 03:33:30 +09:00
return ( iv [ a ] << 24 ) | ( iv [ b ] << 16 ) |
( iv [ c ] << 8 ) | iv [ d ];
2022-10-29 03:09:41 +09:00
} else {
2022-10-29 03:33:30 +09:00
return ( iv [ 7 - a ] << 24 ) | ( iv [ 7 - b ] << 16 ) |
( iv [ 7 - c ] << 8 ) | iv [ 7 - d ];
2022-10-29 03:09:41 +09:00
}
};
2022-10-29 03:33:30 +09:00
C [ 0 ] = C [ 0 ] ^ getIVValue ( 3 , 2 , 1 , 0 );
C [ 1 ] = C [ 1 ] ^ getIVValue ( 7 , 6 , 3 , 2 );
C [ 2 ] = C [ 2 ] ^ getIVValue ( 7 , 6 , 5 , 4 );
C [ 3 ] = C [ 3 ] ^ getIVValue ( 5 , 4 , 1 , 0 );
C [ 4 ] = C [ 4 ] ^ getIVValue ( 3 , 2 , 1 , 0 );
C [ 5 ] = C [ 5 ] ^ getIVValue ( 7 , 6 , 3 , 2 );
C [ 6 ] = C [ 6 ] ^ getIVValue ( 7 , 6 , 5 , 4 );
C [ 7 ] = C [ 7 ] ^ getIVValue ( 5 , 4 , 1 , 0 );
2022-10-29 03:09:41 +09:00
for ( let i = 0 ; i < 4 ; i ++ ) {
counterUpdate ();
nextState ();
}
}
// Extraction Scheme
2022-10-29 03:33:30 +09:00
const S = new Uint8Array ( 16 );
2022-10-29 03:09:41 +09:00
const extract = function () {
let pos = 0 ;
const addPart = function ( value ) {
S [ pos ++ ] = value >>> 8 ;
S [ pos ++ ] = value & 0xff ;
};
counterUpdate ();
nextState ();
addPart (( X [ 6 ] >>> 16 ) ^ ( X [ 1 ] & 0xffff ));
addPart (( X [ 6 ] & 0xffff ) ^ ( X [ 3 ] >>> 16 ));
addPart (( X [ 4 ] >>> 16 ) ^ ( X [ 7 ] & 0xffff ));
addPart (( X [ 4 ] & 0xffff ) ^ ( X [ 1 ] >>> 16 ));
addPart (( X [ 2 ] >>> 16 ) ^ ( X [ 5 ] & 0xffff ));
addPart (( X [ 2 ] & 0xffff ) ^ ( X [ 7 ] >>> 16 ));
addPart (( X [ 0 ] >>> 16 ) ^ ( X [ 3 ] & 0xffff ));
addPart (( X [ 0 ] & 0xffff ) ^ ( X [ 5 ] >>> 16 ));
if ( littleEndian ) {
for ( let i = 0 , j = S . length - 1 ; i < j ;) {
const temp = S [ i ];
S [ i ] = S [ j ];
S [ j ] = temp ;
i ++ ;
j -- ;
}
}
};
const data = Utils . convertToByteString ( input , inputType );
const result = new Uint8Array ( data . length );
for ( let i = 0 ; i <= data . length - 16 ; i += 16 ) {
extract ();
for ( let j = 0 ; j < 16 ; j ++ ) {
result [ i + j ] = data . charCodeAt ( i + j ) ^ S [ j ];
}
}
if ( data . length % 16 !== 0 ) {
const offset = data . length - data . length % 16 ;
const length = data . length - offset ;
extract ();
if ( littleEndian ) {
for ( let j = 0 ; j < length ; j ++ ) {
result [ offset + j ] = data . charCodeAt ( offset + j ) ^ S [ j ];
}
} else {
for ( let j = 0 ; j < length ; j ++ ) {
result [ offset + j ] = data . charCodeAt ( offset + j ) ^ S [ 16 - length + j ];
}
}
}
if ( outputType === "Hex" ) {
return toHexFast ( result );
}
return Utils . byteArrayToChars ( result );
}
}
2022-11-25 16:48:54 +00:00
export default Rabbit ;