WIP: clean up signal protocol

This commit is contained in:
Ryan ZHAO
2021-02-19 16:06:10 +11:00
parent 8cc78e8f4c
commit b34809f4d5
20 changed files with 93 additions and 462 deletions

View File

@@ -1,50 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.util.ByteUtil;
import java.text.ParseException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DerivedMessageSecrets {
public static final int SIZE = 80;
private static final int CIPHER_KEY_LENGTH = 32;
private static final int MAC_KEY_LENGTH = 32;
private static final int IV_LENGTH = 16;
private final SecretKeySpec cipherKey;
private final SecretKeySpec macKey;
private final IvParameterSpec iv;
public DerivedMessageSecrets(byte[] okm) {
try {
byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH);
this.cipherKey = new SecretKeySpec(keys[0], "AES");
this.macKey = new SecretKeySpec(keys[1], "HmacSHA256");
this.iv = new IvParameterSpec(keys[2]);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
public SecretKeySpec getCipherKey() {
return cipherKey;
}
public SecretKeySpec getMacKey() {
return macKey;
}
public IvParameterSpec getIv() {
return iv;
}
}

View File

@@ -1,31 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.util.ByteUtil;
public class DerivedRootSecrets {
public static final int SIZE = 64;
private final byte[] rootKey;
private final byte[] chainKey;
public DerivedRootSecrets(byte[] okm) {
byte[][] keys = ByteUtil.split(okm, 32, 32);
this.rootKey = keys[0];
this.chainKey = keys[1];
}
public byte[] getRootKey() {
return rootKey;
}
public byte[] getChainKey() {
return chainKey;
}
}

View File

@@ -6,9 +6,6 @@
package org.session.libsignal.libsignal.kdf;
import org.session.libsignal.libsignal.kdf.HKDFv2;
import org.session.libsignal.libsignal.kdf.HKDFv3;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -22,7 +19,6 @@ public abstract class HKDF {
public static HKDF createFor(int messageVersion) {
switch (messageVersion) {
case 2: return new HKDFv2();
case 3: return new HKDFv3();
default: throw new AssertionError("Unknown version: " + messageVersion);
}

View File

@@ -1,13 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.kdf;
public class HKDFv2 extends HKDF {
@Override
protected int getIterationStartOffset() {
return 0;
}
}

View File

@@ -1,24 +0,0 @@
package org.session.libsignal.metadata.certificate;
import java.util.HashSet;
import java.util.Set;
public class CertificateValidator {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final Set<Integer> REVOKED = new HashSet<Integer>() {{
}};
public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException {
if (certificate.getSender() == null || certificate.getSenderDeviceId() <= 0) {
throw new InvalidCertificateException("Sender or sender device id is invalid");
}
}
// VisibleForTesting
void validate(ServerCertificate certificate) throws InvalidCertificateException {
}
}

View File

@@ -1,12 +0,0 @@
package org.session.libsignal.metadata.certificate;
public class InvalidCertificateException extends Exception {
public InvalidCertificateException(String s) {
super(s);
}
public InvalidCertificateException(Exception e) {
super(e);
}
}

View File

@@ -1,51 +0,0 @@
package org.session.libsignal.metadata.certificate;
import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.metadata.SignalProtos;
public class SenderCertificate {
private final int senderDeviceId;
private final String sender;
private final byte[] serialized;
private final byte[] certificate;
public SenderCertificate(byte[] serialized) throws InvalidCertificateException {
try {
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.parseFrom(serialized);
if (!certificate.hasSenderDevice() || !certificate.hasSender()) {
throw new InvalidCertificateException("Missing fields");
}
this.sender = certificate.getSender();
this.senderDeviceId = certificate.getSenderDevice();
this.serialized = serialized;
this.certificate = certificate.toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new InvalidCertificateException(e);
}
}
public int getSenderDeviceId() {
return senderDeviceId;
}
public String getSender() {
return sender;
}
public byte[] getSerialized() {
return serialized;
}
public byte[] getCertificate() {
return certificate;
}
}

View File

@@ -1,66 +0,0 @@
package org.session.libsignal.metadata.certificate;
import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.metadata.SignalProtos;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.ecc.Curve;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
public class ServerCertificate {
private final int keyId;
private final ECPublicKey key;
private final byte[] serialized;
private final byte[] certificate;
private final byte[] signature;
public ServerCertificate(byte[] serialized) throws InvalidCertificateException {
try {
SignalProtos.ServerCertificate wrapper = SignalProtos.ServerCertificate.parseFrom(serialized);
if (!wrapper.hasCertificate() || !wrapper.hasSignature()) {
throw new InvalidCertificateException("Missing fields");
}
SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.parseFrom(wrapper.getCertificate());
if (!certificate.hasId() || !certificate.hasKey()) {
throw new InvalidCertificateException("Missing fields");
}
this.keyId = certificate.getId();
this.key = Curve.decodePoint(certificate.getKey().toByteArray(), 0);
this.serialized = serialized;
this.certificate = wrapper.getCertificate().toByteArray();
this.signature = wrapper.getSignature().toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new InvalidCertificateException(e);
} catch (InvalidKeyException e) {
throw new InvalidCertificateException(e);
}
}
public int getKeyId() {
return keyId;
}
public ECPublicKey getKey() {
return key;
}
public byte[] getSerialized() {
return serialized;
}
public byte[] getCertificate() {
return certificate;
}
public byte[] getSignature() {
return signature;
}
}

View File

@@ -14,7 +14,6 @@ import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
import org.session.libsignal.service.api.messages.SendMessageResult;
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@@ -165,21 +164,21 @@ public class SignalServiceMessageSender {
* @throws IOException
*/
public void sendReceipt(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess,
Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceReceiptMessage message)
throws IOException {
byte[] content = createReceiptContent(message);
boolean useFallbackEncryption = true;
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption);
sendMessage(recipient, unidentifiedAccess, message.getWhen(), content, false, message.getTTL(), useFallbackEncryption);
}
public void sendTyping(List<SignalServiceAddress> recipients,
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
List<Optional<UnidentifiedAccess>> unidentifiedAccess,
SignalServiceTypingMessage message)
throws IOException
{
byte[] content = createTypingContent(message);
sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false);
sendMessage(0, recipients, unidentifiedAccess, message.getTimestamp(), content, true, message.getTTL(), false, false);
}
/**
@@ -191,14 +190,14 @@ public class SignalServiceMessageSender {
*/
public SendMessageResult sendMessage(long messageID,
SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess,
Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceDataMessage message)
throws IOException
{
byte[] content = createMessageContent(message, recipient);
long timestamp = message.getTimestamp();
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), true, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccess, timestamp, content, false, message.getTTL(), true, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
return result;
}
@@ -212,7 +211,7 @@ public class SignalServiceMessageSender {
*/
public List<SendMessageResult> sendMessage(long messageID,
List<SignalServiceAddress> recipients,
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
List<Optional<UnidentifiedAccess>> unidentifiedAccess,
SignalServiceDataMessage message)
throws IOException {
// Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine
@@ -221,7 +220,7 @@ public class SignalServiceMessageSender {
long timestamp = message.getTimestamp();
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
return sendMessage(messageID, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent());
return sendMessage(messageID, recipients, unidentifiedAccess, timestamp, content, false, message.getTTL(), isClosedGroup, message.hasVisibleContent());
}
public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
@@ -885,25 +884,6 @@ public class SignalServiceMessageSender {
return new OutgoingPushMessageList(publicKey, timestamp, messages, false);
}
private Optional<UnidentifiedAccess> getTargetUnidentifiedAccess(Optional<UnidentifiedAccessPair> unidentifiedAccess) {
if (unidentifiedAccess.isPresent()) {
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
}
return Optional.absent();
}
private List<Optional<UnidentifiedAccess>> getTargetUnidentifiedAccess(List<Optional<UnidentifiedAccessPair>> unidentifiedAccess) {
List<Optional<UnidentifiedAccess>> results = new LinkedList<>();
for (Optional<UnidentifiedAccessPair> item : unidentifiedAccess) {
if (item.isPresent()) results.add(item.get().getTargetUnidentifiedAccess());
else results.add(Optional.<UnidentifiedAccess>absent());
}
return results;
}
public static interface EventListener {
public void onSecurityEvent(SignalServiceAddress address);

View File

@@ -1,8 +1,6 @@
package org.session.libsignal.service.api.crypto;
import org.session.libsignal.metadata.certificate.InvalidCertificateException;
import org.session.libsignal.metadata.certificate.SenderCertificate;
import org.session.libsignal.libsignal.util.ByteUtil;
import java.security.InvalidAlgorithmParameterException;
@@ -19,23 +17,16 @@ import javax.crypto.spec.SecretKeySpec;
public class UnidentifiedAccess {
private final byte[] unidentifiedAccessKey;
private final SenderCertificate unidentifiedCertificate;
public UnidentifiedAccess(byte[] unidentifiedAccessKey, byte[] unidentifiedCertificate)
throws InvalidCertificateException
public UnidentifiedAccess(byte[] unidentifiedAccessKey)
{
this.unidentifiedAccessKey = unidentifiedAccessKey;
this.unidentifiedCertificate = new SenderCertificate(unidentifiedCertificate);
}
public byte[] getUnidentifiedAccessKey() {
return unidentifiedAccessKey;
}
public SenderCertificate getUnidentifiedCertificate() {
return unidentifiedCertificate;
}
public static byte[] deriveAccessKeyFrom(byte[] profileKey) {
try {
byte[] nonce = new byte[12];
@@ -47,17 +38,7 @@ public class UnidentifiedAccess {
byte[] ciphertext = cipher.doFinal(input);
return ByteUtil.trim(ciphertext, 16);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
} catch (NoSuchPaddingException e) {
throw new AssertionError(e);
} catch (InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
} catch (BadPaddingException e) {
throw new AssertionError(e);
} catch (IllegalBlockSizeException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) {
throw new AssertionError(e);
}
}

View File

@@ -1,23 +0,0 @@
package org.session.libsignal.service.api.crypto;
import org.session.libsignal.libsignal.util.guava.Optional;
public class UnidentifiedAccessPair {
private final Optional<UnidentifiedAccess> targetUnidentifiedAccess;
private final Optional<UnidentifiedAccess> selfUnidentifiedAccess;
public UnidentifiedAccessPair(UnidentifiedAccess targetUnidentifiedAccess, UnidentifiedAccess selfUnidentifiedAccess) {
this.targetUnidentifiedAccess = Optional.of(targetUnidentifiedAccess);
this.selfUnidentifiedAccess = Optional.of(selfUnidentifiedAccess);
}
public Optional<UnidentifiedAccess> getTargetUnidentifiedAccess() {
return targetUnidentifiedAccess;
}
public Optional<UnidentifiedAccess> getSelfUnidentifiedAccess() {
return selfUnidentifiedAccess;
}
}