Implemented encryption and decryption for GSM logical channel.

1. Added functions for encryption and decryption activation and setting KC and CipherID to GSMLogicalChannel.
2. Added functions for encrypt, decrypt, setting KC and CipherID to L1Encoder, L1Decoder and L1FEC.
3. Implemented decrypt and encrypt for XCCHL1 Decoder and Encoder, TCHFACCH Decoder and Encoder.
4. Added xor_apply function to BitVector and SoftVector.
5. Added libosmocore and libosmogsm dependences for using osmo_a5 and osmo_hexparse functions.
This commit is contained in:
Ivan Kluchnikov
2012-12-01 17:19:26 +04:00
committed by Alexander Chemeris
parent b6f4309fb0
commit 3ae3c84d5d
8 changed files with 204 additions and 3 deletions

View File

@@ -163,6 +163,15 @@ void BitVector::invert()
}
unsigned BitVector::xor_apply(uint8_t * gamma, size_t len)
{
if (len != size()) return 1;
for (size_t i = 0; i < size(); i++) {
if (gamma[i] > 1) return gamma[i];
mStart[i] ^= gamma[i];
}
return 0;
}
void BitVector::reverse8()
@@ -447,6 +456,17 @@ BitVector SoftVector::sliced() const
}
unsigned SoftVector::xor_apply(uint8_t * gamma, size_t len)
{
if (len != size()) return 1;
for (size_t i = 0; i < size(); i++) {
if (gamma[i] > 1) return gamma[i];
// soft-bit inversion
if (gamma[i]) mStart[i] = 1.0F - mStart[i];
}
return 0;
}
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
{

View File

@@ -296,6 +296,10 @@ class BitVector : public Vector<char> {
/** Invert 0<->1. */
void invert();
// apply gamma bit sequence (1 bit per byte) using xor function:
// return false on non-boolean gamma or length mismatch
unsigned xor_apply(uint8_t * gamma, size_t len);
/**@name Byte-wise operations. */
//@{
/** Reverse an 8-bit vector. */
@@ -409,6 +413,11 @@ class SoftVector: public Vector<float> {
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
//@}
// apply gamma bit sequence (1 bit per byte) using xor function:
// 1.0-x is the inverse of x soft-bit
// return false on non-boolean gamma or length mismatch
unsigned xor_apply(uint8_t * gamma, size_t len);
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
void decode(ViterbiR2O4 &decoder, BitVector& target) const;

View File

@@ -24,7 +24,10 @@
*/
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/gsm/a5.h>
}
#include "GSML1FEC.h"
#include "GSMCommon.h"
#include "GSMSAPMux.h"
@@ -230,6 +233,7 @@ void L1Encoder::open()
if (!mRunning) start();
mTotalBursts=0;
mActive = true;
mCipherID = 0;
resync();
}
@@ -313,6 +317,16 @@ unsigned L1Encoder::ARFCN() const
}
void L1Encoder::encrypt(BitVector &burst, uint32_t FN) const
{
if (mCipherID > 0) {
ubit_t ks[114];
osmo_a5(mCipherID, mKc, FN, ks, NULL);
unsigned e = burst.xor_apply(ks, 114);
if (e) LOG(ERR) << "Length mismatch while applying gamma: " << e;
}
}
unsigned L1Decoder::ARFCN() const
{
@@ -336,6 +350,7 @@ void L1Decoder::open()
mT3109.reset();
mT3101.set();
mActive = true;
mCipherID = 0;
}
@@ -398,7 +413,15 @@ void L1Decoder::countBadFrame()
OBJLOG(DEBUG) <<"L1Decoder FER=" << mFER;
}
void L1Decoder::decrypt(SoftVector &burst, uint32_t FN) const
{
if (mCipherID > 0) {
ubit_t ks[114];
osmo_a5(mCipherID, mKc, FN, NULL, ks);
unsigned e = burst.xor_apply(ks, 114);
if (e) LOG(ERR) << "Length mismatch while applying gamma: " << e;
}
}
void L1FEC::downstream(ARFCNManager* radio)
@@ -589,6 +612,9 @@ bool XCCHL1Decoder::processBurst(const RxBurst& inBurst)
if (B==0)
mReadTime = inBurst.time();
// Decrypt the burst
decrypt(mI[B], inBurst.time().FN());
// If the burst index is 3, then this is the last burst in the L2 frame.
// Return true to indicate that we are ready to deinterleave.
return B==3;
@@ -881,6 +907,8 @@ void XCCHL1Encoder::transmit()
for (int B=0; B<4; B++) {
mBurst.time(mNextWriteTime);
// Encrypt the burst
encrypt(mI[B], mNextWriteTime.FN());
// Copy in the "encrypted" bits, GSM 05.03 4.1.5, 05.02 5.2.3.
OBJLOG(DEBUG) << "XCCHL1Encoder mI["<<B<<"]=" << mI[B];
mI[B].segment(0,57).copyToSegment(mBurst,3);
@@ -1095,6 +1123,9 @@ bool TCHFACCHL1Decoder::processBurst( const RxBurst& inBurst)
inBurst.data1().copyToSegment(mI[B],0);
inBurst.data2().copyToSegment(mI[B],57);
// Decrypt the burst
decrypt(mI[B], inBurst.time().FN());
// Every 4th frame is the start of a new block.
// So if this isn't a "4th" frame, return now.
if (B%4!=3) return false;
@@ -1397,6 +1428,8 @@ void TCHFACCHL1Encoder::dispatch()
for (int B=0; B<4; B++) {
// set TDMA position
mBurst.time(mNextWriteTime);
// Encrypt the burst
encrypt(mI[B+mOffset], mNextWriteTime.FN());
// copy in the bits
mI[B+mOffset].segment(0,57).copyToSegment(mBurst,3);
mI[B+mOffset].segment(57,57).copyToSegment(mBurst,88);

View File

@@ -112,6 +112,12 @@ class L1Encoder {
bool mActive; ///< true between open() and close()
//@}
/**@ Cipher state */
//@{
uint8_t mKc[8]; ///< current Kc
unsigned mCipherID; ///< current cipher id
//@}
ViterbiR2O4 mVCoder; ///< nearly all GSM channels use the same convolutional code
char mDescriptiveString[100];
@@ -174,6 +180,13 @@ class L1Encoder {
const char* descriptiveString() const { return mDescriptiveString; }
/**@name Cipher support */
//@{
void setKc(uint8_t * Kc_key) { memcpy(mKc, Kc_key, 8); }
void setCipherID(unsigned cipherID) { mCipherID = cipherID; }
unsigned getCipherID() const { return mCipherID; }
//@}
protected:
/** Roll write times forward to the next positions. */
@@ -197,6 +210,9 @@ class L1Encoder {
*/
virtual void sendIdleFill();
/** Encrypt given burst in-place */
void encrypt(BitVector &burst, uint32_t FN) const;
};
@@ -240,6 +256,12 @@ class L1Decoder {
L1FEC* mParent; ///< a containing L1 processor, if any
//@}
/**@ Cipher state */
//@{
uint8_t mKc[8]; ///< current Kc
unsigned mCipherID; ///< current cipher id
//@}
ViterbiR2O4 mVCoder; ///< nearly all GSM channels use the same convolutional code
@@ -313,6 +335,13 @@ class L1Decoder {
TypeAndOffset typeAndOffset() const; ///< this comes from mMapping
//@}
/**@name Cipher support */
//@{
void setKc(uint8_t * Kc_key) { memcpy(mKc, Kc_key, 8); }
void setCipherID(unsigned cipherID) { mCipherID = cipherID; }
unsigned getCipherID() const { return mCipherID; }
//@}
protected:
@@ -330,6 +359,10 @@ class L1Decoder {
void countGoodFrame();
void countBadFrame();
/* Decrypt burst in-place */
void decrypt(SoftVector &burst, uint32_t FN) const;
};
@@ -413,6 +446,22 @@ class L1FEC {
const char* descriptiveString() const
{ assert(mEncoder); return mEncoder->descriptiveString(); }
void setKc(uint8_t *Kc) {
assert(mEncoder); mEncoder->setKc(Kc);
assert(mDecoder); mDecoder->setKc(Kc);
}
unsigned getEncCipherID() const
{ assert(mEncoder); return mEncoder->getCipherID(); }
unsigned getDecCipherID() const
{ assert(mDecoder); return mDecoder->getCipherID(); }
void setEncCipherID(unsigned cipherID)
{ assert(mEncoder); mEncoder->setCipherID(cipherID); }
void setDecCipherID(unsigned cipherID)
{ assert(mDecoder); return mDecoder->setCipherID(cipherID); }
//@}

View File

@@ -28,6 +28,10 @@
extern "C" {
#include <osmocom/core/utils.h>
}
#include "GSML3RRElements.h"
#include "GSML3Message.h"
#include "GSML3RRMessages.h"
@@ -64,6 +68,69 @@ void LogicalChannel::open()
}
bool LogicalChannel::setKc(const char * key)
{
uint8_t Kc[8];
if (!mL1) return false;
if(osmo_hexparse(key, Kc, 8) != 8) return false;
mL1->setKc(Kc);
if (mSACCH) mSACCH->setKc(key);
return true;
}
unsigned LogicalChannel::isEncrypting() const
{
if (mL1) {
return mL1->getEncCipherID();
}
else {
return 0;
}
}
unsigned LogicalChannel::isDecrypting() const
{
if (mL1) {
return mL1->getDecCipherID();
}
else {
return 0;
}
}
void LogicalChannel::activateEncryption(unsigned cipherID)
{
if (mL1) {
if (mL1->getEncCipherID() == 0) {
LOG(DEBUG) << "Activate Encryption on "<< mL1->descriptiveString();
mL1->setEncCipherID(cipherID);
}
else {
LOG(DEBUG) << "Encryption is already activated on " << mL1->descriptiveString();
}
}
if (mSACCH) ((LogicalChannel*)mSACCH)->activateEncryption(cipherID);
}
void LogicalChannel::activateDecryption(unsigned cipherID)
{
if (mL1) {
if (mL1->getDecCipherID() == 0) {
LOG(DEBUG) << "Activate Dencryption on "<< mL1->descriptiveString();
mL1->setDecCipherID(cipherID);
}
else {
LOG(DEBUG) << "Decryption is already activated on " << mL1->descriptiveString();
}
}
if (mSACCH) ((LogicalChannel*)mSACCH)->activateDecryption(cipherID);
}
void LogicalChannel::connect()
{
mMux.downstream(mL1);

View File

@@ -205,6 +205,21 @@ public:
/** Return true if the channel is active. */
bool active() const { assert(mL1); return mL1->active(); }
/** Set Kc for L1 */
bool setKc(const char * key);
/** Return CipherID or 0 if encryption is not activated. */
unsigned isEncrypting() const;
/** Return CipherID or 0 if decryption is not activated. */
unsigned isDecrypting() const;
/** Activate encryption, use A5/1 by default. */
void activateEncryption(unsigned cipherID = 1);
/** Activate decryption, use A5/1 by default. */
void activateDecryption(unsigned cipherID = 1);
/** The TDMA parameters for the transmit side. */
const TDMAMapping& txMapping() const { assert(mL1); return mL1->txMapping(); }

View File

@@ -42,7 +42,9 @@ OpenBTS_LDADD = \
$(SQLITE_LA) \
$(SMS_LA) \
$(OSIP_LIBS) \
$(ORTP_LIBS)
$(ORTP_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS)
OpenBTSCLI_SOURCES = OpenBTSCLI.cpp
OpenBTSDo_SOURCES = OpenBTSDo.cpp

View File

@@ -124,6 +124,12 @@ PKG_CHECK_MODULES(OSIP, libosip2)
# Defines ORTP_CFLAGS, ORTP_INCLUDEDIR, and ORTP_LIBS
PKG_CHECK_MODULES(ORTP, ortp)
# Check for A5 and COMP128 library
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
# Check for Osmocom core library
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
# Defines LIBUSB_TRANSFER_CANCELLED, LIBUSB_TRANSFER_COMPLETED, LIBUSB_SUCCESS, LIBUSB_ERROR_*
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)