forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
315 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
src/main/java/com/thealgorithms/ciphers/a5/A5KeyStreamGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package com.thealgorithms.ciphers.a5; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.BitSet; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
|
||
// Basic tests for sanity check | ||
class LFSRTest { | ||
// Represents 0100 1110 0010 1111 0100 1101 0111 1100 0001 1110 1011 1000 1000 1011 0011 1010 | ||
// But we start reverse way because bitset starts from most right (1010) | ||
byte[] sessionKeyBytes = { 58, (byte) 139, (byte) 184, 30, 124, 77, 47, 78 }; | ||
|
||
// Represents 11 1010 1011 0011 1100 1011 | ||
byte[] frameCounterBytes = { (byte) 203, (byte) 179, 58 }; | ||
|
||
@Test | ||
void initialize() { | ||
BitSet sessionKey = BitSet.valueOf( sessionKeyBytes ); | ||
BitSet frameCounter = BitSet.valueOf( frameCounterBytes ); | ||
|
||
BitSet expected = new BitSet( 19 ); | ||
expected.set( 0 ); | ||
expected.set( 1 ); | ||
expected.set( 3 ); | ||
expected.set( 4 ); | ||
expected.set( 5 ); | ||
expected.set( 7 ); | ||
expected.set( 9 ); | ||
expected.set( 10 ); | ||
expected.set( 11 ); | ||
expected.set( 12 ); | ||
expected.set( 13 ); | ||
expected.set( 15 ); | ||
expected.set( 16 ); | ||
expected.set( 17 ); | ||
|
||
LFSR lfsr0 = new LFSR( 19, 8, new int[]{ 13, 16, 17, 18 } ); | ||
lfsr0.initialize( sessionKey, frameCounter ); | ||
assertEquals( expected.toString(), lfsr0.toString() ); | ||
} | ||
|
||
@Test | ||
void clock() { | ||
BitSet sessionKey = BitSet.valueOf( sessionKeyBytes ); | ||
BitSet frameCounter = BitSet.valueOf( frameCounterBytes ); | ||
|
||
LFSR lfsr0 = new LFSR( 19, 8, new int[]{ 13, 16, 17, 18 } ); | ||
lfsr0.initialize( sessionKey, frameCounter ); | ||
|
||
BitSet expected = new BitSet( 19 ); | ||
expected.set( 0 ); | ||
expected.set( 1 ); | ||
expected.set( 2 ); | ||
expected.set( 4 ); | ||
expected.set( 5 ); | ||
expected.set( 6 ); | ||
expected.set( 8 ); | ||
expected.set( 10 ); | ||
expected.set( 11 ); | ||
expected.set( 12 ); | ||
expected.set( 13 ); | ||
expected.set( 14 ); | ||
expected.set( 16 ); | ||
expected.set( 17 ); | ||
expected.set( 18 ); | ||
|
||
lfsr0.clock(); | ||
assertEquals( expected.toString(), lfsr0.toString() ); | ||
} | ||
|
||
@Test | ||
void getClockBit() { | ||
BitSet sessionKey = BitSet.valueOf( sessionKeyBytes ); | ||
BitSet frameCounter = BitSet.valueOf( frameCounterBytes ); | ||
|
||
LFSR lfsr0 = new LFSR( 19, 8, new int[]{ 13, 16, 17, 18 } ); | ||
|
||
assertFalse( lfsr0.getClockBit() ); | ||
|
||
lfsr0.initialize( sessionKey, frameCounter ); | ||
|
||
assertFalse( lfsr0.getClockBit() ); | ||
} | ||
} |