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