001/** 002 * 003 * Copyright 2018 Paul Schaub. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.ox.store.abstr; 018 019import java.io.IOException; 020import java.security.InvalidAlgorithmParameterException; 021import java.security.NoSuchAlgorithmException; 022import java.util.Collections; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.logging.Level; 027import java.util.logging.Logger; 028 029import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; 030import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore; 031 032import org.bouncycastle.openpgp.PGPException; 033import org.bouncycastle.openpgp.PGPPublicKeyRing; 034import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 035import org.bouncycastle.openpgp.PGPSecretKeyRing; 036import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 037import org.jxmpp.jid.BareJid; 038import org.pgpainless.PGPainless; 039import org.pgpainless.key.OpenPgpV4Fingerprint; 040import org.pgpainless.key.info.KeyRingInfo; 041 042public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore { 043 044 protected static final Logger LOGGER = Logger.getLogger(AbstractOpenPgpKeyStore.class.getName()); 045 046 protected Map<BareJid, PGPPublicKeyRingCollection> publicKeyRingCollections = new HashMap<>(); 047 protected Map<BareJid, PGPSecretKeyRingCollection> secretKeyRingCollections = new HashMap<>(); 048 protected Map<BareJid, Map<OpenPgpV4Fingerprint, Date>> keyFetchDates = new HashMap<>(); 049 050 /** 051 * Read a {@link PGPPublicKeyRingCollection} from local storage. 052 * This method returns null, if no keys were found. 053 * 054 * @param owner owner of the keys 055 * @return public keys 056 * 057 * @throws IOException IO is dangerous 058 * @throws PGPException PGP is brittle 059 */ 060 protected abstract PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) throws IOException, PGPException; 061 062 /** 063 * Write the {@link PGPPublicKeyRingCollection} of a user to local storage. 064 * 065 * @param owner owner of the keys 066 * @param publicKeys keys 067 * 068 * @throws IOException IO is dangerous 069 */ 070 protected abstract void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException; 071 072 /** 073 * Read a {@link PGPSecretKeyRingCollection} from local storage. 074 * This method returns null, if no keys were found. 075 * 076 * @param owner owner of the keys 077 * @return secret keys 078 * 079 * @throws IOException IO is dangerous 080 * @throws PGPException PGP is brittle 081 */ 082 protected abstract PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) throws IOException, PGPException; 083 084 /** 085 * Write the {@link PGPSecretKeyRingCollection} of a user to local storage. 086 * 087 * @param owner owner of the keys 088 * @param secretKeys secret keys 089 * 090 * @throws IOException IO is dangerous 091 */ 092 protected abstract void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException; 093 094 /** 095 * Read the key fetch dates for a users keys from local storage. 096 * 097 * @param owner owner 098 * @return fetch dates for the owners keys 099 * 100 * @throws IOException IO is dangerous 101 */ 102 protected abstract Map<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner) throws IOException; 103 104 /** 105 * Write the key fetch dates for a users keys to local storage. 106 * 107 * @param owner owner 108 * @param dates fetch dates for the owners keys 109 * 110 * @throws IOException IO is dangerous 111 */ 112 protected abstract void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> dates) throws IOException; 113 114 @Override 115 public Map<OpenPgpV4Fingerprint, Date> getPublicKeyFetchDates(BareJid contact) throws IOException { 116 Map<OpenPgpV4Fingerprint, Date> dates = keyFetchDates.get(contact); 117 if (dates == null) { 118 dates = readKeyFetchDates(contact); 119 keyFetchDates.put(contact, dates); 120 } 121 return dates; 122 } 123 124 @Override 125 public void setPublicKeyFetchDates(BareJid contact, Map<OpenPgpV4Fingerprint, Date> dates) throws IOException { 126 keyFetchDates.put(contact, dates); 127 writeKeyFetchDates(contact, dates); 128 } 129 130 @Override 131 public PGPPublicKeyRingCollection getPublicKeysOf(BareJid owner) throws IOException, PGPException { 132 PGPPublicKeyRingCollection keys = publicKeyRingCollections.get(owner); 133 if (keys == null) { 134 keys = readPublicKeysOf(owner); 135 if (keys != null) { 136 publicKeyRingCollections.put(owner, keys); 137 } 138 } 139 return keys; 140 } 141 142 @Override 143 public PGPSecretKeyRingCollection getSecretKeysOf(BareJid owner) throws IOException, PGPException { 144 PGPSecretKeyRingCollection keys = secretKeyRingCollections.get(owner); 145 if (keys == null) { 146 keys = readSecretKeysOf(owner); 147 if (keys != null) { 148 secretKeyRingCollections.put(owner, keys); 149 } 150 } 151 return keys; 152 } 153 154 @Override 155 public void importSecretKey(BareJid owner, PGPSecretKeyRing secretKeys) 156 throws IOException, PGPException, MissingUserIdOnKeyException { 157 158 if (!new KeyRingInfo(secretKeys).isUserIdValid("xmpp:" + owner.toString())) { 159 throw new MissingUserIdOnKeyException(owner, new OpenPgpV4Fingerprint(secretKeys)); 160 } 161 162 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 163 try { 164 if (secretKeyRings != null) { 165 secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(secretKeyRings, secretKeys); 166 } else { 167 secretKeyRings = new PGPSecretKeyRingCollection(Collections.singleton(secretKeys)); 168 } 169 } catch (IllegalArgumentException e) { 170 LOGGER.log(Level.INFO, "Skipping secret key ring " + Long.toHexString(secretKeys.getPublicKey().getKeyID()) + 171 " as it is already in the key ring of " + owner.toString()); 172 } 173 this.secretKeyRingCollections.put(owner, secretKeyRings); 174 writeSecretKeysOf(owner, secretKeyRings); 175 } 176 177 @Override 178 public void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException { 179 180 if (!new KeyRingInfo(publicKeys).isUserIdValid("xmpp:" + owner.toString())) { 181 throw new MissingUserIdOnKeyException(owner, new OpenPgpV4Fingerprint(publicKeys)); 182 } 183 184 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 185 try { 186 if (publicKeyRings != null) { 187 publicKeyRings = PGPPublicKeyRingCollection.addPublicKeyRing(publicKeyRings, publicKeys); 188 } else { 189 publicKeyRings = new PGPPublicKeyRingCollection(Collections.singleton(publicKeys)); 190 } 191 } catch (IllegalArgumentException e) { 192 LOGGER.log(Level.FINE, "Skipping public key ring " + Long.toHexString(publicKeys.getPublicKey().getKeyID()) + 193 " as it is already in the key ring of " + owner.toString(), e); 194 } 195 this.publicKeyRingCollections.put(owner, publicKeyRings); 196 writePublicKeysOf(owner, publicKeyRings); 197 } 198 199 @Override 200 public PGPPublicKeyRing getPublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 201 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 202 203 if (publicKeyRings != null) { 204 return publicKeyRings.getPublicKeyRing(fingerprint.getKeyId()); 205 } 206 207 return null; 208 } 209 210 @Override 211 public PGPSecretKeyRing getSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 212 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 213 214 if (secretKeyRings != null) { 215 return secretKeyRings.getSecretKeyRing(fingerprint.getKeyId()); 216 } 217 218 return null; 219 } 220 221 @Override 222 public void deletePublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 223 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 224 if (publicKeyRings.contains(fingerprint.getKeyId())) { 225 publicKeyRings = PGPPublicKeyRingCollection.removePublicKeyRing(publicKeyRings, publicKeyRings.getPublicKeyRing(fingerprint.getKeyId())); 226 if (!publicKeyRings.iterator().hasNext()) { 227 publicKeyRings = null; 228 } 229 this.publicKeyRingCollections.put(owner, publicKeyRings); 230 writePublicKeysOf(owner, publicKeyRings); 231 } 232 } 233 234 @Override 235 public void deleteSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 236 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 237 if (secretKeyRings.contains(fingerprint.getKeyId())) { 238 secretKeyRings = PGPSecretKeyRingCollection.removeSecretKeyRing(secretKeyRings, secretKeyRings.getSecretKeyRing(fingerprint.getKeyId())); 239 if (!secretKeyRings.iterator().hasNext()) { 240 secretKeyRings = null; 241 } 242 this.secretKeyRingCollections.put(owner, secretKeyRings); 243 writeSecretKeysOf(owner, secretKeyRings); 244 } 245 } 246 247 @Override 248 public PGPSecretKeyRing generateKeyRing(BareJid owner) 249 throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { 250 return PGPainless.generateKeyRing().modernKeyRing("xmpp:" + owner.toString()); 251 } 252}