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.List; 025import java.util.TreeMap; 026 027import org.jivesoftware.smackx.omemo.element.OmemoBundleElement; 028import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; 029import org.jivesoftware.smackx.omemo.internal.OmemoDevice; 030import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; 031import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil; 032 033import org.whispersystems.libsignal.IdentityKey; 034import org.whispersystems.libsignal.IdentityKeyPair; 035import org.whispersystems.libsignal.InvalidKeyException; 036import org.whispersystems.libsignal.ecc.Curve; 037import org.whispersystems.libsignal.ecc.ECPublicKey; 038import org.whispersystems.libsignal.state.PreKeyBundle; 039import org.whispersystems.libsignal.state.PreKeyRecord; 040import org.whispersystems.libsignal.state.SessionRecord; 041import org.whispersystems.libsignal.state.SignedPreKeyRecord; 042import org.whispersystems.libsignal.util.KeyHelper; 043 044/** 045 * Concrete implementation of the KeyUtil for an implementation using the Signal library. 046 * 047 * @author Paul Schaub 048 */ 049public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, 050 SessionRecord, ECPublicKey, PreKeyBundle> { 051 052 @Override 053 public IdentityKeyPair generateOmemoIdentityKeyPair() { 054 return KeyHelper.generateIdentityKeyPair(); 055 } 056 057 @Override 058 @SuppressWarnings("NonApiType") 059 public TreeMap<Integer, PreKeyRecord> generateOmemoPreKeys(int currentPreKeyId, int count) { 060 List<PreKeyRecord> preKeyRecords = KeyHelper.generatePreKeys(currentPreKeyId, count); 061 TreeMap<Integer, PreKeyRecord> map = new TreeMap<>(); 062 for (PreKeyRecord p : preKeyRecords) { 063 map.put(p.getId(), p); 064 } 065 return map; 066 } 067 068 @Override 069 public SignedPreKeyRecord generateOmemoSignedPreKey(IdentityKeyPair identityKeyPair, int currentPreKeyId) 070 throws CorruptedOmemoKeyException { 071 try { 072 return KeyHelper.generateSignedPreKey(identityKeyPair, currentPreKeyId); 073 } catch (InvalidKeyException e) { 074 throw new CorruptedOmemoKeyException(e); 075 } 076 } 077 078 @Override 079 public SignedPreKeyRecord signedPreKeyFromBytes(byte[] data) throws IOException { 080 if (data == null) return null; 081 return new SignedPreKeyRecord(data); 082 } 083 084 @Override 085 public byte[] signedPreKeyToBytes(SignedPreKeyRecord signedPreKeyRecord) { 086 return signedPreKeyRecord.serialize(); 087 } 088 089 @Override 090 public SessionRecord rawSessionFromBytes(byte[] data) throws IOException { 091 if (data == null) return null; 092 return new SessionRecord(data); 093 } 094 095 @Override 096 public byte[] rawSessionToBytes(SessionRecord session) { 097 return session.serialize(); 098 } 099 100 @Override 101 public IdentityKeyPair identityKeyPairFromBytes(byte[] data) throws CorruptedOmemoKeyException { 102 if (data == null) return null; 103 try { 104 return new IdentityKeyPair(data); 105 } catch (InvalidKeyException e) { 106 throw new CorruptedOmemoKeyException(e); 107 } 108 } 109 110 @Override 111 public IdentityKey identityKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException { 112 if (data == null) return null; 113 try { 114 return new IdentityKey(data, 0); 115 } catch (InvalidKeyException e) { 116 throw new CorruptedOmemoKeyException(e); 117 } 118 } 119 120 @Override 121 public ECPublicKey ellipticCurvePublicKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException { 122 if (data == null) return null; 123 try { 124 return Curve.decodePoint(data, 0); 125 } catch (InvalidKeyException e) { 126 throw new CorruptedOmemoKeyException(e); 127 } 128 } 129 130 @Override 131 public byte[] preKeyToBytes(PreKeyRecord preKeyRecord) { 132 return preKeyRecord.serialize(); 133 } 134 135 @Override 136 public PreKeyRecord preKeyFromBytes(byte[] bytes) throws IOException { 137 if (bytes == null) return null; 138 return new PreKeyRecord(bytes); 139 } 140 141 @Override 142 public PreKeyBundle bundleFromOmemoBundle(OmemoBundleElement bundle, OmemoDevice contact, int preKeyId) 143 throws CorruptedOmemoKeyException { 144 return new PreKeyBundle(0, 145 contact.getDeviceId(), 146 preKeyId, 147 BUNDLE.preKeyPublic(bundle, preKeyId), 148 BUNDLE.signedPreKeyId(bundle), 149 BUNDLE.signedPreKeyPublic(bundle), 150 BUNDLE.signedPreKeySignature(bundle), 151 BUNDLE.identityKey(bundle)); 152 } 153 154 @Override 155 public byte[] signedPreKeySignatureFromKey(SignedPreKeyRecord signedPreKey) { 156 return signedPreKey.getSignature(); 157 } 158 159 @Override 160 public int signedPreKeyIdFromKey(SignedPreKeyRecord signedPreKey) { 161 return signedPreKey.getId(); 162 } 163 164 @Override 165 public byte[] identityKeyPairToBytes(IdentityKeyPair identityKeyPair) { 166 return identityKeyPair.serialize(); 167 } 168 169 @Override 170 public IdentityKey identityKeyFromPair(IdentityKeyPair identityKeyPair) { 171 return identityKeyPair.getPublicKey(); 172 } 173 174 @Override 175 public byte[] identityKeyForBundle(IdentityKey identityKey) { 176 return identityKey.getPublicKey().serialize(); 177 } 178 179 @Override 180 public byte[] identityKeyToBytes(IdentityKey identityKey) { 181 return identityKey.serialize(); 182 } 183 184 @Override 185 public byte[] preKeyPublicKeyForBundle(ECPublicKey preKey) { 186 return preKey.serialize(); 187 } 188 189 @Override 190 public byte[] preKeyForBundle(PreKeyRecord preKeyRecord) { 191 return preKeyRecord.getKeyPair().getPublicKey().serialize(); 192 } 193 194 @Override 195 public byte[] signedPreKeyPublicForBundle(SignedPreKeyRecord signedPreKey) { 196 return signedPreKey.getKeyPair().getPublicKey().serialize(); 197 } 198 199 @Override 200 public OmemoFingerprint getFingerprintOfIdentityKey(IdentityKey identityKey) { 201 if (identityKey == null) { 202 return null; 203 } 204 205 String fp = identityKey.getFingerprint(); 206 // Cut "(byte)0x" prefixes, remove spaces and commas, cut first two digits. 207 fp = fp.replace("(byte)0x", "").replace(",", "") 208 .replace(" ", "").substring(2); 209 return new OmemoFingerprint(fp); 210 } 211 212 @Override 213 public OmemoFingerprint getFingerprintOfIdentityKeyPair(IdentityKeyPair identityKeyPair) { 214 if (identityKeyPair == null) { 215 return null; 216 } 217 return getFingerprintOfIdentityKey(identityKeyPair.getPublicKey()); 218 } 219}