That fuck shit the fascists are using
at master 138 lines 5.5 kB view raw
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}