mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 09:06:51 +08:00
Add A5 Cipher (#3292)
This commit is contained in:
30
src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java
Normal file
30
src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
10
src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java
Normal file
10
src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java
Normal 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;
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
78
src/main/java/com/thealgorithms/ciphers/a5/LFSR.java
Normal file
78
src/main/java/com/thealgorithms/ciphers/a5/LFSR.java
Normal 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();
|
||||
}
|
||||
}
|
21
src/main/java/com/thealgorithms/ciphers/a5/Utils.java
Normal file
21
src/main/java/com/thealgorithms/ciphers/a5/Utils.java
Normal 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user