package de.tum.in.tumcampusapp.notifications;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Base64;
import com.google.common.base.Charsets;
import com.google.gson.Gson;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import de.tum.in.tumcampusapp.R;
import de.tum.in.tumcampusapp.activities.AlarmActivity;
import de.tum.in.tumcampusapp.api.TUMCabeClient;
import de.tum.in.tumcampusapp.auxiliary.AuthenticationManager;
import de.tum.in.tumcampusapp.auxiliary.RSASigner;
import de.tum.in.tumcampusapp.auxiliary.Utils;
import de.tum.in.tumcampusapp.models.gcm.GCMAlert;
import de.tum.in.tumcampusapp.models.gcm.GCMNotification;
public class Alarm extends GenericNotification {
/**
* This is the private key used to sign all messages sent by the alarm system - used to verify that the sent message is correct
*/
private static final String PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSukueIrdowjJB/IHR6+tsCbYLF9kmC/2Sa8/kI9Ttq0aUyC0hDt2SBzuDDmp/RwnUap5/0xT/h3z+WgKOjrzWig4lmb7G2+RuuVn8466AErfp3YQVFiovNLGMqwfJzPZ9aV3sZBXCTeEbDkd/CLRp3kBYkAtL8NfIlbNaII9CWKdhS907JyEWRZO2DLiYLm37vK/hwg58eXHwL9jNYY3gFqGUlfWXwGC2a0yTOk9rgJejhUbU9GLWSL3OwiHVXlpPsvTC1Ry0H4kQQeisjCgpkPjOQAnAFRN9zZLtBZlIsssYvL3ohY/C1HfGzDwGTaELjhtzY9qHdFW/4GDZh8swIDAQAB";
private final GCMAlert alert;
private final GCMNotification info;
public Alarm(String payload, Context context, int notification) {
super(context, 3, notification, true); //Let the base class know which id this notification has
//Check if a payload was passed
if (payload == null) {
throw new IllegalArgumentException();
}
//Get data from server
info = getNotificationFromServer();
// parse data
this.alert = new Gson().fromJson(payload, GCMAlert.class);
}
private GCMNotification getNotificationFromServer() {
try {
return TUMCabeClient.getInstance(this.context).getNotification(this.notification);
} catch (IOException e) {
Utils.log(e);
return null;
}
}
/**
* Validates the signature sent to us
*
* @param title the message title
* @param description the message description
* @param signature the message signature
* @return if the signature is valid
*/
private static boolean isValidSignature(String title, String description, String signature) {
PublicKey key = getCabePublicKey();
if (key == null) {
return false;
}
Signature sig = RSASigner.getSignatureInstance();
try {
sig.initVerify(key);
} catch (InvalidKeyException e) {
Utils.log(e);
return false;
}
String text = title + description;
byte[] textBytes = text.getBytes(Charsets.UTF_8);
try {
sig.update(textBytes);
} catch (SignatureException e) {
Utils.log(e);
return false;
}
try {
return sig.verify(Base64.decode(signature, Base64.DEFAULT));
} catch (SignatureException | IllegalArgumentException e) {
Utils.log(e);
return false;
}
}
/**
* converts the above base64 representation of the public key to a java object
*
* @return the public key of the TUMCabe server
*/
private static PublicKey getCabePublicKey() {
// Base64 string -> Bytes
KeyFactory keyFactory = AuthenticationManager.getKeyFactoryInstance();
byte[] keyBytes = Base64.decode(PUB_KEY, Base64.NO_WRAP);
// Bytes -> PublicKey
try {
return keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));
} catch (InvalidKeySpecException e) {
Utils.log(e);
return null;
}
}
@Override
public Notification getNotification() {
if (alert.silent || info == null) {
//Do nothing
return null;
}
if (!isValidSignature(info.getTitle(), info.getDescription(), info.getSignature())) {
Utils.log("Received an invalid RSA signature");
return null;
}
// GCMNotification sound
Uri sound = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.message);
Intent alarm = new Intent(this.context, AlarmActivity.class);
alarm.putExtra("info", this.info);
alarm.putExtra("alert", this.alert);
PendingIntent pending = PendingIntent.getActivity(this.context, 0, alarm, PendingIntent.FLAG_UPDATE_CURRENT);
String strippedDescription = Utils.stripHtml(info.getDescription()); // Strip any html tags from the description
return new NotificationCompat.Builder(context)
.setSmallIcon(this.icon)
.setContentTitle(info.getTitle())
.setStyle(new NotificationCompat.BigTextStyle().bigText(strippedDescription))
.setContentText(strippedDescription)
.setContentIntent(pending)
.setDefaults(Notification.DEFAULT_VIBRATE)
.setLights(0xff0000ff, 500, 500)
.setSound(sound)
.setAutoCancel(true)
.build();
}
@Override
public int getNotificationIdentification() {
return 3;
}
}