That fuck shit the fascists are using
1/**
2 * Copyright (C) 2011 Whisper Systems
3 * Copyright (C) 2013 Open Whisper Systems
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18package org.tm.archive.crypto;
19
20import org.signal.core.util.Conversions;
21import org.signal.libsignal.protocol.InvalidKeyException;
22import org.signal.libsignal.protocol.InvalidMessageException;
23import org.signal.libsignal.protocol.ecc.Curve;
24import org.signal.libsignal.protocol.ecc.ECKeyPair;
25import org.signal.libsignal.protocol.ecc.ECPrivateKey;
26import org.signal.libsignal.protocol.ecc.ECPublicKey;
27import org.signal.core.util.Base64;
28import org.tm.archive.util.Util;
29
30import java.io.IOException;
31import java.security.NoSuchAlgorithmException;
32
33import javax.crypto.Mac;
34import javax.crypto.spec.SecretKeySpec;
35
36/**
37 * This class is used to asymmetrically encrypt local data. This is used in the case
38 * where TextSecure receives an SMS, but the user's local encryption passphrase is
39 * not cached (either because of a timeout, or because it hasn't yet been entered).
40 *
41 * In this case, we have access to the public key of a local keypair. We encrypt
42 * the message with this, and put it into the DB. When the user enters their passphrase,
43 * we can get access to the private key of the local keypair, decrypt the message, and
44 * replace it into the DB with symmetric encryption.
45 *
46 * The encryption protocol is as follows:
47 *
48 * 1) Generate an ephemeral keypair.
49 * 2) Do ECDH with the public key of the local durable keypair.
50 * 3) Do KMF with the ECDH result to obtain a master secret.
51 * 4) Encrypt the message with that master secret.
52 *
53 * @author Moxie Marlinspike
54 *
55 */
56public class AsymmetricMasterCipher {
57
58 private final AsymmetricMasterSecret asymmetricMasterSecret;
59
60 public AsymmetricMasterCipher(AsymmetricMasterSecret asymmetricMasterSecret) {
61 this.asymmetricMasterSecret = asymmetricMasterSecret;
62 }
63
64 public byte[] encryptBytes(byte[] body) {
65 try {
66 ECPublicKey theirPublic = asymmetricMasterSecret.getDjbPublicKey();
67 ECKeyPair ourKeyPair = Curve.generateKeyPair();
68 byte[] secret = Curve.calculateAgreement(theirPublic, ourKeyPair.getPrivateKey());
69 MasterCipher masterCipher = getMasterCipherForSecret(secret);
70 byte[] encryptedBodyBytes = masterCipher.encryptBytes(body);
71
72 PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey());
73 byte[] publicKeyBytes = ourPublicKey.serialize();
74
75 return Util.combine(publicKeyBytes, encryptedBodyBytes);
76 } catch (InvalidKeyException e) {
77 throw new AssertionError(e);
78 }
79 }
80
81 public byte[] decryptBytes(byte[] combined) throws IOException, InvalidMessageException {
82 try {
83 byte[][] parts = Util.split(combined, PublicKey.KEY_SIZE, combined.length - PublicKey.KEY_SIZE);
84 PublicKey theirPublicKey = new PublicKey(parts[0], 0);
85
86 ECPrivateKey ourPrivateKey = asymmetricMasterSecret.getPrivateKey();
87 byte[] secret = Curve.calculateAgreement(theirPublicKey.getKey(), ourPrivateKey);
88 MasterCipher masterCipher = getMasterCipherForSecret(secret);
89
90 return masterCipher.decryptBytes(parts[1]);
91 } catch (InvalidKeyException e) {
92 throw new InvalidMessageException(e);
93 }
94 }
95
96 public String decryptBody(String body) throws IOException, InvalidMessageException {
97 byte[] combined = Base64.decode(body);
98 return new String(decryptBytes(combined));
99 }
100
101 public String encryptBody(String body) {
102 return Base64.encodeWithPadding(encryptBytes(body.getBytes()));
103 }
104
105 private MasterCipher getMasterCipherForSecret(byte[] secretBytes) {
106 SecretKeySpec cipherKey = deriveCipherKey(secretBytes);
107 SecretKeySpec macKey = deriveMacKey(secretBytes);
108 MasterSecret masterSecret = new MasterSecret(cipherKey, macKey);
109
110 return new MasterCipher(masterSecret);
111 }
112
113 private SecretKeySpec deriveMacKey(byte[] secretBytes) {
114 byte[] digestedBytes = getDigestedBytes(secretBytes, 1);
115 byte[] macKeyBytes = new byte[20];
116
117 System.arraycopy(digestedBytes, 0, macKeyBytes, 0, macKeyBytes.length);
118 return new SecretKeySpec(macKeyBytes, "HmacSHA1");
119 }
120
121 private SecretKeySpec deriveCipherKey(byte[] secretBytes) {
122 byte[] digestedBytes = getDigestedBytes(secretBytes, 0);
123 byte[] cipherKeyBytes = new byte[16];
124
125 System.arraycopy(digestedBytes, 0, cipherKeyBytes, 0, cipherKeyBytes.length);
126 return new SecretKeySpec(cipherKeyBytes, "AES");
127 }
128
129 private byte[] getDigestedBytes(byte[] secretBytes, int iteration) {
130 try {
131 Mac mac = Mac.getInstance("HmacSHA256");
132 mac.init(new SecretKeySpec(secretBytes, "HmacSHA256"));
133 return mac.doFinal(Conversions.intToByteArray(iteration));
134 } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) {
135 throw new AssertionError(e);
136 }
137 }
138}