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