001/** 002 * 003 * Copyright 2017 Paul Schaub 004 * 005 * This file is part of smack-omemo-signal. 006 * 007 * smack-omemo-signal is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation; either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021package org.jivesoftware.smackx.omemo.signal; 022 023import java.io.IOException; 024import java.util.HashMap; 025import java.util.List; 026 027import org.jivesoftware.smackx.omemo.OmemoFingerprint; 028import org.jivesoftware.smackx.omemo.OmemoManager; 029import org.jivesoftware.smackx.omemo.OmemoStore; 030import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement; 031import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; 032import org.jivesoftware.smackx.omemo.internal.OmemoDevice; 033import org.jivesoftware.smackx.omemo.internal.OmemoSession; 034import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil; 035 036import org.jxmpp.jid.impl.JidCreate; 037import org.jxmpp.stringprep.XmppStringprepException; 038import org.whispersystems.libsignal.IdentityKey; 039import org.whispersystems.libsignal.IdentityKeyPair; 040import org.whispersystems.libsignal.InvalidKeyException; 041import org.whispersystems.libsignal.SessionCipher; 042import org.whispersystems.libsignal.SignalProtocolAddress; 043import org.whispersystems.libsignal.ecc.Curve; 044import org.whispersystems.libsignal.ecc.ECPublicKey; 045import org.whispersystems.libsignal.state.PreKeyBundle; 046import org.whispersystems.libsignal.state.PreKeyRecord; 047import org.whispersystems.libsignal.state.SessionRecord; 048import org.whispersystems.libsignal.state.SignedPreKeyRecord; 049import org.whispersystems.libsignal.util.KeyHelper; 050 051/** 052 * Concrete implementation of the KeyUtil for an implementation using the Signal library. 053 * 054 * @author Paul Schaub 055 */ 056public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, 057 SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> { 058 059 @Override 060 public IdentityKeyPair generateOmemoIdentityKeyPair() { 061 return KeyHelper.generateIdentityKeyPair(); 062 } 063 064 @Override 065 public HashMap<Integer, PreKeyRecord> generateOmemoPreKeys(int currentPreKeyId, int count) { 066 List<PreKeyRecord> preKeyRecords = KeyHelper.generatePreKeys(currentPreKeyId, count); 067 HashMap<Integer, PreKeyRecord> hashMap = new HashMap<>(); 068 for (PreKeyRecord p : preKeyRecords) { 069 hashMap.put(p.getId(), p); 070 } 071 return hashMap; 072 } 073 074 @Override 075 public SignedPreKeyRecord generateOmemoSignedPreKey(IdentityKeyPair identityKeyPair, int currentPreKeyId) throws CorruptedOmemoKeyException { 076 try { 077 return KeyHelper.generateSignedPreKey(identityKeyPair, currentPreKeyId); 078 } catch (InvalidKeyException e) { 079 throw new CorruptedOmemoKeyException(e.getMessage()); 080 } 081 } 082 083 @Override 084 public SignedPreKeyRecord signedPreKeyFromBytes(byte[] data) throws IOException { 085 return new SignedPreKeyRecord(data); 086 } 087 088 @Override 089 public byte[] signedPreKeyToBytes(SignedPreKeyRecord signedPreKeyRecord) { 090 return signedPreKeyRecord.serialize(); 091 } 092 093 @Override 094 public OmemoSession<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> 095 createOmemoSession(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore, 096 OmemoDevice contact, IdentityKey identityKey) { 097 return new SignalOmemoSession(omemoManager, omemoStore, contact, identityKey); 098 } 099 100 @Override 101 public OmemoSession<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> 102 createOmemoSession(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore, OmemoDevice from) { 103 return new SignalOmemoSession(omemoManager, omemoStore, from); 104 } 105 106 @Override 107 public SessionRecord rawSessionFromBytes(byte[] data) throws IOException { 108 return new SessionRecord(data); 109 } 110 111 @Override 112 public byte[] rawSessionToBytes(SessionRecord session) { 113 return session.serialize(); 114 } 115 116 @Override 117 public IdentityKeyPair identityKeyPairFromBytes(byte[] data) throws CorruptedOmemoKeyException { 118 try { 119 return new IdentityKeyPair(data); 120 } catch (InvalidKeyException e) { 121 throw new CorruptedOmemoKeyException(e.getMessage()); 122 } 123 } 124 125 @Override 126 public IdentityKey identityKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException { 127 try { 128 return new IdentityKey(data, 0); 129 } catch (InvalidKeyException e) { 130 throw new CorruptedOmemoKeyException(e.getMessage()); 131 } 132 } 133 134 @Override 135 public ECPublicKey ellipticCurvePublicKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException { 136 try { 137 return Curve.decodePoint(data, 0); 138 } catch (InvalidKeyException e) { 139 throw new CorruptedOmemoKeyException(e.getMessage()); 140 } 141 } 142 143 @Override 144 public byte[] preKeyToBytes(PreKeyRecord preKeyRecord) { 145 return preKeyRecord.serialize(); 146 } 147 148 @Override 149 public PreKeyRecord preKeyFromBytes(byte[] bytes) throws IOException { 150 return new PreKeyRecord(bytes); 151 } 152 153 @Override 154 public PreKeyBundle bundleFromOmemoBundle(OmemoBundleVAxolotlElement bundle, OmemoDevice contact, int preKeyId) throws CorruptedOmemoKeyException { 155 return new PreKeyBundle(0, 156 contact.getDeviceId(), 157 preKeyId, 158 BUNDLE.preKeyPublic(bundle, preKeyId), 159 BUNDLE.signedPreKeyId(bundle), 160 BUNDLE.signedPreKeyPublic(bundle), 161 BUNDLE.signedPreKeySignature(bundle), 162 BUNDLE.identityKey(bundle)); 163 } 164 165 @Override 166 public byte[] signedPreKeySignatureFromKey(SignedPreKeyRecord signedPreKey) { 167 return signedPreKey.getSignature(); 168 } 169 170 @Override 171 public int signedPreKeyIdFromKey(SignedPreKeyRecord signedPreKey) { 172 return signedPreKey.getId(); 173 } 174 175 @Override 176 public byte[] identityKeyPairToBytes(IdentityKeyPair identityKeyPair) { 177 return identityKeyPair.serialize(); 178 } 179 180 @Override 181 public IdentityKey identityKeyFromPair(IdentityKeyPair identityKeyPair) { 182 return identityKeyPair.getPublicKey(); 183 } 184 185 @Override 186 public byte[] identityKeyForBundle(IdentityKey identityKey) { 187 return identityKey.getPublicKey().serialize(); 188 } 189 190 @Override 191 public byte[] identityKeyToBytes(IdentityKey identityKey) { 192 return identityKey.serialize(); 193 } 194 195 @Override 196 public byte[] preKeyPublicKeyForBundle(ECPublicKey preKey) { 197 return preKey.serialize(); 198 } 199 200 @Override 201 public byte[] preKeyForBundle(PreKeyRecord preKeyRecord) { 202 return preKeyRecord.getKeyPair().getPublicKey().serialize(); 203 } 204 205 @Override 206 public byte[] signedPreKeyPublicForBundle(SignedPreKeyRecord signedPreKey) { 207 return signedPreKey.getKeyPair().getPublicKey().serialize(); 208 } 209 210 @Override 211 public OmemoFingerprint getFingerprint(IdentityKey identityKey) { 212 String fp = identityKey.getFingerprint(); 213 // Cut "(byte)0x" prefixes, remove spaces and commas, cut first two digits. 214 fp = fp.replace("(byte)0x", "").replace(",", "").replace(" ", "").substring(2); 215 return new OmemoFingerprint(fp); 216 } 217 218 @Override 219 public SignalProtocolAddress omemoDeviceAsAddress(OmemoDevice contact) { 220 return new SignalProtocolAddress(contact.getJid().asBareJid().toString(), contact.getDeviceId()); 221 } 222 223 @Override 224 public OmemoDevice addressAsOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException { 225 return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId()); 226 } 227}