Add A5 Cipher (#3292)

This commit is contained in:
J2mF
2022-09-27 19:33:13 +03:00
committed by GitHub
parent 8ca571d887
commit e4eb99ff18
7 changed files with 315 additions and 0 deletions

View File

@ -0,0 +1,30 @@
package com.thealgorithms.ciphers.a5;
import java.util.BitSet;
// https://en.wikipedia.org/wiki/A5/1
public class A5Cipher {
private final A5KeyStreamGenerator keyStreamGenerator;
private static final int KEY_STREAM_LENGTH = 228; // 28.5 bytes so we need to pad bytes or something
public A5Cipher( BitSet sessionKey, BitSet frameCounter ) {
keyStreamGenerator = new A5KeyStreamGenerator();
keyStreamGenerator.initialize( sessionKey, frameCounter );
}
public BitSet encrypt( BitSet plainTextBits ) {
// create a copy
var result = new BitSet( KEY_STREAM_LENGTH );
result.xor( plainTextBits );
var key = keyStreamGenerator.getNextKeyStream();
result.xor( key );
return result;
}
public void resetCounter() {
keyStreamGenerator.reInitialize();
}
}

View File

@ -0,0 +1,54 @@
package com.thealgorithms.ciphers.a5;
import java.util.BitSet;
// TODO: raise exceptions for improper use
public class A5KeyStreamGenerator extends CompositeLFSR {
private BitSet initialFrameCounter;
private BitSet frameCounter;
private BitSet sessionKey;
private static final int INITIAL_CLOCKING_CYCLES = 100;
private static final int KEY_STREAM_LENGTH = 228; // 28.5 bytes so we need to pad bytes or something
@Override
public void initialize( BitSet sessionKey, BitSet frameCounter ) {
this.sessionKey = sessionKey;
this.frameCounter = (BitSet) frameCounter.clone();
this.initialFrameCounter = (BitSet) frameCounter.clone();
registers.clear();
LFSR lfsr1 = new LFSR( 19, 8, new int[]{ 13, 16, 17, 18 } );
LFSR lfsr2 = new LFSR( 22, 10, new int[]{ 20, 21 } );
LFSR lfsr3 = new LFSR( 23, 10, new int[]{ 7, 20, 21, 22 } );
registers.add( lfsr1 );
registers.add( lfsr2 );
registers.add( lfsr3 );
registers.forEach( lfsr -> lfsr.initialize( sessionKey, frameCounter ) );
}
public void reInitialize() {
this.initialize( sessionKey, initialFrameCounter );
}
public BitSet getNextKeyStream() {
for ( int cycle = 1; cycle <= INITIAL_CLOCKING_CYCLES; ++cycle )
this.clock();
BitSet result = new BitSet( KEY_STREAM_LENGTH );
for ( int cycle = 1; cycle <= KEY_STREAM_LENGTH; ++cycle ) {
boolean outputBit = this.clock();
result.set( cycle - 1, outputBit );
}
reInitializeRegisters();
return result;
}
private void reInitializeRegisters() {
incrementFrameCounter();
registers.forEach( lfsr -> lfsr.initialize( sessionKey, frameCounter ) );
}
private void incrementFrameCounter() {
Utils.increment( frameCounter, FRAME_COUNTER_LENGTH );
}
}

View File

@ -0,0 +1,10 @@
package com.thealgorithms.ciphers.a5;
import java.util.BitSet;
public interface BaseLFSR {
void initialize(BitSet sessionKey, BitSet frameCounter);
boolean clock();
int SESSION_KEY_LENGTH = 64;
int FRAME_COUNTER_LENGTH = 22;
}

View File

@ -0,0 +1,35 @@
package com.thealgorithms.ciphers.a5;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public abstract class CompositeLFSR implements BaseLFSR {
protected final List<LFSR> registers = new ArrayList<>();
/**
* Implements irregular clocking using the clock bit for each register
* @return the registers discarded bit xored value
*/
@Override
public boolean clock() {
boolean majorityBit = getMajorityBit();
boolean result = false;
for ( var register : registers ) {
result ^= register.getLastBit();
if ( register.getClockBit() == majorityBit )
register.clock();
}
return result;
}
private boolean getMajorityBit() {
Map<Boolean, Integer> bitCount = new TreeMap<>();
bitCount.put( false, 0 );
bitCount.put( true, 0 );
registers.forEach( lfsr -> bitCount.put( lfsr.getClockBit(), bitCount.get( lfsr.getClockBit() ) + 1 ) );
return bitCount.get( false ) <= bitCount.get( true );
}
}

View File

@ -0,0 +1,78 @@
package com.thealgorithms.ciphers.a5;
import java.util.BitSet;
public class LFSR implements BaseLFSR {
private final BitSet register;
private final int length;
private final int clockBitIndex;
private final int[] tappingBitsIndices;
public LFSR( int length, int clockBitIndex, int[] tappingBitsIndices ) {
this.length = length;
this.clockBitIndex = clockBitIndex;
this.tappingBitsIndices = tappingBitsIndices;
register = new BitSet( length );
}
@Override
public void initialize( BitSet sessionKey, BitSet frameCounter ) {
register.clear();
clock( sessionKey, SESSION_KEY_LENGTH );
clock( frameCounter, FRAME_COUNTER_LENGTH );
}
private void clock( BitSet key, int keyLength ) {
// We start from reverse because LFSR 0 index is the left most bit
// while key 0 index is right most bit, so we reverse it
for ( int i = keyLength - 1; i >= 0; --i ) {
var newBit = key.get( i ) ^ xorTappingBits();
pushBit( newBit );
}
}
@Override
public boolean clock() {
return pushBit( xorTappingBits() );
}
public boolean getClockBit() {
return register.get( clockBitIndex );
}
public boolean get( int bitIndex ) {
return register.get( bitIndex );
}
public boolean getLastBit() {
return register.get( length - 1 );
}
private boolean xorTappingBits() {
boolean result = false;
for ( int i : tappingBitsIndices ) {
result ^= register.get( i );
}
return result;
}
private boolean pushBit( boolean bit ) {
boolean discardedBit = rightShift();
register.set( 0, bit );
return discardedBit;
}
private boolean rightShift() {
boolean discardedBit = get( length - 1 );
for ( int i = length - 1; i > 0; --i ) {
register.set( i, get( i - 1 ) );
}
register.set( 0, false );
return discardedBit;
}
@Override
public String toString() {
return register.toString();
}
}

View File

@ -0,0 +1,21 @@
package com.thealgorithms.ciphers.a5;
// Source http://www.java2s.com/example/java-utility-method/bitset/increment-bitset-bits-int-size-9fd84.html
//package com.java2s;
//License from project: Open Source License
import java.util.BitSet;
public class Utils {
public static boolean increment( BitSet bits, int size ) {
int i = size - 1;
while ( i >= 0 && bits.get( i ) ) {
bits.set( i--, false );/*from w w w . j a v a 2s .c o m*/
}
if ( i < 0 ) {
return false;
}
bits.set( i, true );
return true;
}
}