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 private 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 if (!new BareJidUserId.SecRingSelectionStrategy().accept(owner, secretKeys)) { 161 throw new MissingUserIdOnKeyException(owner, new OpenPgpV4Fingerprint(secretKeys)); 162 } 163 164 PGPSecretKeyRing importKeys = BCUtil.removeUnassociatedKeysFromKeyRing(secretKeys, secretKeys.getPublicKey()); 165 166 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 167 try { 168 if (secretKeyRings != null) { 169 secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(secretKeyRings, importKeys); 170 } else { 171 secretKeyRings = BCUtil.keyRingsToKeyRingCollection(importKeys); 172 } 173 } catch (IllegalArgumentException e) { 174 LOGGER.log(Level.INFO, "Skipping secret key ring " + Long.toHexString(importKeys.getPublicKey().getKeyID()) + 175 " as it is already in the key ring of " + owner.toString()); 176 } 177 this.secretKeyRingCollections.put(owner, secretKeyRings); 178 writeSecretKeysOf(owner, secretKeyRings); 179 } 180 181 @Override 182 public void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException { 183 184 if (!new BareJidUserId.PubRingSelectionStrategy().accept(owner, publicKeys)) { 185 throw new MissingUserIdOnKeyException(owner, new OpenPgpV4Fingerprint(publicKeys)); 186 } 187 188 PGPPublicKeyRing importKeys = BCUtil.removeUnassociatedKeysFromKeyRing(publicKeys, publicKeys.getPublicKey()); 189 190 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 191 try { 192 if (publicKeyRings != null) { 193 publicKeyRings = PGPPublicKeyRingCollection.addPublicKeyRing(publicKeyRings, importKeys); 194 } else { 195 publicKeyRings = BCUtil.keyRingsToKeyRingCollection(importKeys); 196 } 197 } catch (IllegalArgumentException e) { 198 LOGGER.log(Level.INFO, "Skipping public key ring " + Long.toHexString(importKeys.getPublicKey().getKeyID()) + 199 " as it is already in the key ring of " + owner.toString()); 200 } 201 this.publicKeyRingCollections.put(owner, publicKeyRings); 202 writePublicKeysOf(owner, publicKeyRings); 203 } 204 205 @Override 206 public PGPPublicKeyRing getPublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 207 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 208 209 if (publicKeyRings != null) { 210 return publicKeyRings.getPublicKeyRing(fingerprint.getKeyId()); 211 } 212 213 return null; 214 } 215 216 @Override 217 public PGPSecretKeyRing getSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 218 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 219 220 if (secretKeyRings != null) { 221 return secretKeyRings.getSecretKeyRing(fingerprint.getKeyId()); 222 } 223 224 return null; 225 } 226 227 @Override 228 public void deletePublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 229 PGPPublicKeyRingCollection publicKeyRings = getPublicKeysOf(owner); 230 if (publicKeyRings.contains(fingerprint.getKeyId())) { 231 publicKeyRings = PGPPublicKeyRingCollection.removePublicKeyRing(publicKeyRings, publicKeyRings.getPublicKeyRing(fingerprint.getKeyId())); 232 if (!publicKeyRings.iterator().hasNext()) { 233 publicKeyRings = null; 234 } 235 this.publicKeyRingCollections.put(owner, publicKeyRings); 236 writePublicKeysOf(owner, publicKeyRings); 237 } 238 } 239 240 @Override 241 public void deleteSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { 242 PGPSecretKeyRingCollection secretKeyRings = getSecretKeysOf(owner); 243 if (secretKeyRings.contains(fingerprint.getKeyId())) { 244 secretKeyRings = PGPSecretKeyRingCollection.removeSecretKeyRing(secretKeyRings, secretKeyRings.getSecretKeyRing(fingerprint.getKeyId())); 245 if (!secretKeyRings.iterator().hasNext()) { 246 secretKeyRings = null; 247 } 248 this.secretKeyRingCollections.put(owner, secretKeyRings); 249 writeSecretKeysOf(owner, secretKeyRings); 250 } 251 } 252 253 @Override 254 public PGPKeyRing generateKeyRing(BareJid owner) 255 throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { 256 return PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + owner.toString()); 257 } 258}