[SGSN] Use libosmocore GPRS encryption plugins from LLC layer

This adds the bits that call into libosmocore (and its plugins)
to implement GPRS (LLC) encryption.
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
index 7e16d1b..4c870d3 100644
--- a/openbsc/src/gprs/gprs_llc.c
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -313,6 +313,7 @@
 	uint8_t addr, ctrl[2];
 	uint32_t fcs_calc;
 	uint16_t nu = 0;
+	uint32_t oc;
 
 	/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
 
@@ -334,9 +335,14 @@
 	lle->llme->bvci = msgb_bvci(msg);
 	lle->llme->nsei = msgb_nsei(msg);
 
-	/* Increment V(U) */
+	/* Obtain current values for N(u) and OC */
 	nu = lle->vu_send;
+	oc = lle->oc_ui_send;
+	/* Increment V(U) */
 	lle->vu_send = (lle->vu_send + 1) % 512;
+	/* Increment Overflow Counter, if needed */
+	if ((lle->vu_send + 1) / 512)
+		lle->oc_ui_send += 512;
 
 	/* Address Field */
 	addr = sapi & 0xf;
@@ -362,6 +368,34 @@
 	fcs[1] = (fcs_calc >> 8) & 0xff;
 	fcs[2] = (fcs_calc >> 16) & 0xff;
 
+	/* encrypt information field + FCS, if needed! */
+	if (lle->llme->algo != GPRS_ALGO_GEA0) {
+		uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
+		uint16_t crypt_len = (fcs + 3) - (llch + 3);
+		uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
+		uint32_t iv;
+		int rc, i;
+		uint64_t kc = *(uint64_t *)&lle->llme->kc;
+
+		/* Compute the 'Input' Paraemeter */
+		iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc);
+
+		/* Compute the keystream that we need to XOR with the data */
+		rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
+				     kc, iv, GPRS_CIPH_SGSN2MS);
+		if (rc < 0) {
+			LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc);
+			return rc;
+		}
+
+		/* XOR the cipher output with the information field + FCS */
+		for (i = 0; i < crypt_len; i++)
+			*(llch + 3 + i) ^= cipher_out[i];
+
+		/* Mark frame as encrypted */
+		ctrl[1] |= 0x02;
+	}
+
 	/* Identifiers passed down: (BVCI, NSEI) */
 
 	/* Send BSSGP-DL-UNITDATA.req */
@@ -431,6 +465,9 @@
 		}
 		/* Increment the sequence number that we expect in the next frame */
 		lle->vu_recv = (gph->seq_tx + 1) % 512;
+		/* Increment Overflow Counter */
+		if ((gph->seq_tx + 1) / 512)
+			lle->oc_ui_recv += 512;
 		break;
 	}
 
@@ -591,8 +628,10 @@
 		}
 	}
 
-	/* calculate what FCS we expect */
-	ghp->fcs_calc = gprs_llc_fcs(llc_hdr, crc_length);
+	if (!ghp->is_encrypted) {
+		/* calculate what FCS we expect */
+		ghp->fcs_calc = gprs_llc_fcs(llc_hdr, crc_length);
+	}
 
 	/* FIXME: parse sack frame */
 	if (ghp->cmd == GPRS_LLC_SACK) {
@@ -622,11 +661,6 @@
 		return rc;
 	}
 
-	if (llhp.fcs != llhp.fcs_calc) {
-		LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n");
-		return -EIO;
-	}
-
 	switch (gprs_tlli_type(msgb_tlli(msg))) {
 	case TLLI_LOCAL:
 	case TLLI_FOREIGN:
@@ -658,6 +692,48 @@
 		}
 	}
 
+	/* decrypt information field + FCS, if needed! */
+	if (llhp.is_encrypted) {
+		uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
+		uint16_t crypt_len = llhp.data_len + 3;
+		uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
+		uint32_t iv;
+		uint64_t kc = *(uint64_t *)&lle->llme->kc;
+		int rc, i;
+
+		if (lle->llme->algo == GPRS_ALGO_GEA0) {
+			LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that "
+				"has no KC/Algo! Dropping.\n");
+			return 0;
+		}
+
+		iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx,
+						lle->oc_ui_recv);
+		rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
+				     kc, iv, GPRS_CIPH_MS2SGSN);
+		if (rc < 0) {
+			LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n",
+			     rc);
+			return rc;
+		}
+
+		/* XOR the cipher output with the information field + FCS */
+		for (i = 0; i < crypt_len; i++)
+			*(llhp.data + i) ^= cipher_out[i];
+	} else {
+		if (lle->llme->algo != GPRS_ALGO_GEA0) {
+			LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC "
+				"that is supposed to be encrypted. Dropping.\n");
+			return 0;
+		}
+	}
+
+	/* We have to do the FCS check _after_ decryption */
+	if (llhp.fcs != llhp.fcs_calc) {
+		LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n");
+		return -EIO;
+	}
+
 	/* Update LLE's (BVCI, NSEI) tuple */
 	lle->llme->bvci = msgb_bvci(msg);
 	lle->llme->nsei = msgb_nsei(msg);
@@ -704,6 +780,10 @@
 {
 	unsigned int i;
 
+	/* Update the crypto parameters */
+	memcpy(llme->kc, kc, sizeof(llme->kc));
+	llme->algo = alg;
+
 	if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) {
 		/* TLLI Assignment 8.3.1 */
 		/* New TLLI shall be assigned and used when (re)transmitting LLC frames */