001 package org.cumulus4j.store.model; 002 003 import javax.jdo.JDOObjectNotFoundException; 004 import javax.jdo.PersistenceManager; 005 import javax.jdo.annotations.IdGeneratorStrategy; 006 import javax.jdo.annotations.IdentityType; 007 import javax.jdo.annotations.NullValue; 008 import javax.jdo.annotations.PersistenceCapable; 009 import javax.jdo.annotations.Persistent; 010 import javax.jdo.annotations.PrimaryKey; 011 import javax.jdo.annotations.Queries; 012 import javax.jdo.annotations.Query; 013 import javax.jdo.annotations.Unique; 014 import javax.jdo.annotations.Version; 015 import javax.jdo.annotations.VersionStrategy; 016 import javax.jdo.identity.IntIdentity; 017 018 import org.cumulus4j.crypto.Cipher; 019 import org.cumulus4j.crypto.CryptoRegistry; 020 import org.cumulus4j.store.EncryptionCoordinateSetManager; 021 import org.cumulus4j.store.crypto.CryptoManager; 022 import org.cumulus4j.store.crypto.CryptoSession; 023 024 /** 025 * <p> 026 * Encryption coordinates used to encrypt a persistent record. 027 * </p> 028 * <p> 029 * Via the {@link EncryptionCoordinateSetManager}, the {@link CryptoManager} 030 * (or {@link CryptoSession}) implementation can map the {@link Cipher#getTransformation() 031 * cipher-transformation} and other encryption-coordinates (e.g. the {@link #getMACAlgorithm() MAC algorithm}) 032 * to a number in order to save space in each persistent record. 033 * </p> 034 * 035 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 036 */ 037 @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true") 038 @Version(strategy=VersionStrategy.VERSION_NUMBER) 039 @Unique(name="EncryptionCoordinateSet_allAlgorithms", members={"cipherTransformation", "macAlgorithm"}) 040 @Queries({ 041 @Query( 042 name="getEncryptionCoordinateSetByAllAlgorithms", 043 value="SELECT UNIQUE WHERE this.cipherTransformation == :cipherTransformation && this.macAlgorithm == :macAlgorithm" 044 ) 045 }) 046 public class EncryptionCoordinateSet 047 { 048 /** 049 * <p> 050 * Constant for deactivating the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>. 051 * </p> 052 * <p> 053 * <b>Important: Deactivating the MAC is dangerous!</b> Choose this value only, if you are absolutely 054 * sure that your {@link #getCipherTransformation() cipher-transformation} already 055 * provides authentication - like <a target="_blank" href="http://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a> 056 * does for example. 057 * </p> 058 */ 059 public static final String MAC_ALGORITHM_NONE = "NONE"; 060 061 /** 062 * Get an existing <code>EncryptionCoordinateSet</code> identified by its {@link #getEncryptionCoordinateSetID() encryptionCoordinateSetID}. 063 * @param pm the backend-{@link PersistenceManager} (the one used for data, if there is a separate index-DB used). 064 * @param encryptionCoordinateSetID the {@link #getEncryptionCoordinateSetID() identifier} of the searched instance. 065 * @return the <code>EncryptionCoordinateSet</code> identified by the given <code>encryptionCoordinateSetID</code> or 066 * <code>null</code>, if no such instance exists in the datastore. 067 */ 068 public static EncryptionCoordinateSet getEncryptionCoordinateSet(PersistenceManager pm, int encryptionCoordinateSetID) 069 { 070 IntIdentity id = new IntIdentity(EncryptionCoordinateSet.class, encryptionCoordinateSetID); 071 try { 072 EncryptionCoordinateSet encryptionCoordinateSet = (EncryptionCoordinateSet) pm.getObjectById(id); 073 return encryptionCoordinateSet; 074 } catch (JDOObjectNotFoundException x) { 075 return null; 076 } 077 } 078 079 /** 080 * <p> 081 * Get an existing <code>EncryptionCoordinateSet</code> identified by its unique properties. 082 * </p> 083 * <p> 084 * As each <code>EncryptionCoordinateSet</code> maps all encryption settings to an ID, all 085 * properties of this class except for the ID form a unique index together. At the moment, 086 * these are: {@link #getCipherTransformation() cipher-transformation} and {@link #getMACAlgorithm() MAC-algorithm}. 087 * </p> 088 * 089 * @param pm the backend-{@link PersistenceManager} (the one used for data, if there is a separate index-DB used). 090 * @param cipherTransformation the {@link #getCipherTransformation() cipher-transformation} of the searched instance. 091 * Must not be <code>null</code>. 092 * @param macAlgorithm the {@link #getMACAlgorithm()} of the searched instance. Must not be <code>null</code> 093 * (use {@value #MAC_ALGORITHM_NONE} for no MAC). 094 * @return the <code>EncryptionCoordinateSet</code> identified by the given properties or 095 * <code>null</code>, if no such instance exists in the datastore. 096 * @see #createEncryptionCoordinateSet(PersistenceManager, String, String) 097 */ 098 public static EncryptionCoordinateSet getEncryptionCoordinateSet(PersistenceManager pm, String cipherTransformation, String macAlgorithm) 099 { 100 if (cipherTransformation == null) 101 throw new IllegalArgumentException("cipherTransformation == null"); 102 103 if (macAlgorithm == null) 104 throw new IllegalArgumentException("macAlgorithm == null"); 105 106 javax.jdo.Query q = pm.newNamedQuery(EncryptionCoordinateSet.class, "getEncryptionCoordinateSetByAllAlgorithms"); 107 return (EncryptionCoordinateSet) q.execute(cipherTransformation, macAlgorithm); 108 // UNIQUE query does not need to be closed, because there is no result list lingering. 109 } 110 111 /** 112 * <p> 113 * Get an existing <code>EncryptionCoordinateSet</code> identified by its unique properties or create one 114 * if necessary. 115 * </p> 116 * <p> 117 * This method is similar to {@link #getEncryptionCoordinateSet(PersistenceManager, String, String)}, but 118 * creates a new <code>EncryptionCoordinateSet</code> instead of returning <code>null</code>, if there is 119 * no existing instance, yet. 120 * </p> 121 * 122 * @param pm the backend-{@link PersistenceManager} (the one used for data, if there is a separate index-DB used). 123 * @param cipherTransformation the {@link #getCipherTransformation() cipher-transformation} of the searched instance. 124 * Must not be <code>null</code>. 125 * @param macAlgorithm the {@link #getMACAlgorithm()} of the searched instance. Must not be <code>null</code> 126 * (use {@value #MAC_ALGORITHM_NONE} for no MAC). 127 * @return the <code>EncryptionCoordinateSet</code> identified by the given properties. This method never returns 128 * <code>null</code>, but instead creates and persists a new instance if needed. 129 * @see #getEncryptionCoordinateSet(PersistenceManager, String, String) 130 */ 131 public static EncryptionCoordinateSet createEncryptionCoordinateSet(PersistenceManager pm, String cipherTransformation, String macAlgorithm) 132 { 133 EncryptionCoordinateSet encryptionCoordinateSet = getEncryptionCoordinateSet(pm, cipherTransformation, macAlgorithm); 134 if (encryptionCoordinateSet == null) 135 encryptionCoordinateSet = pm.makePersistent(new EncryptionCoordinateSet(cipherTransformation, macAlgorithm)); 136 137 return encryptionCoordinateSet; 138 } 139 140 @PrimaryKey 141 @Persistent(valueStrategy=IdGeneratorStrategy.NATIVE) 142 private int encryptionCoordinateSetID = -1; 143 144 @Persistent(nullValue=NullValue.EXCEPTION) 145 private String cipherTransformation; 146 147 @Persistent(nullValue=NullValue.EXCEPTION) 148 private String macAlgorithm; 149 150 /** 151 * Create a new <code>EncryptionCoordinateSet</code>. This default constructor only exists 152 * for JDO and should never be used directly! 153 */ 154 protected EncryptionCoordinateSet() { } 155 156 /** 157 * Create a new <code>EncryptionCoordinateSet</code>. Instead of using this constructor, 158 * you should use {@link #createEncryptionCoordinateSet(PersistenceManager, String, String)}! 159 * 160 * @param cipherTransformation the cipher-transformation. 161 * @param macAlgorithm the MAC-algorithm. 162 */ 163 protected EncryptionCoordinateSet(String cipherTransformation, String macAlgorithm) 164 { 165 if (cipherTransformation == null) 166 throw new IllegalArgumentException("cipherTransformation == null"); 167 168 if (macAlgorithm == null) 169 throw new IllegalArgumentException("macAlgorithm == null"); 170 171 this.cipherTransformation = cipherTransformation; 172 this.macAlgorithm = macAlgorithm; 173 } 174 175 /** 176 * <p> 177 * Get the unique numeric identifier of this <code>EncryptionCoordinateSet</code>. 178 * </p> 179 * <p> 180 * Note: Implementors of {@link CryptoManager} (or {@link CryptoSession} respectively) might 181 * choose not to store the entire int value (4 bytes), but reduce the size. Every time the 182 * encryption configuration is changed, a new instance of this class is persisted. Restricting 183 * the size to 2 bytes, for example, still gives the administrator the possibility to change 184 * the configuration 65535 times - which is likely enough. 185 * </p> 186 * 187 * @return the unique numeric identifier (primary key). 188 */ 189 public int getEncryptionCoordinateSetID() { 190 return encryptionCoordinateSetID; 191 } 192 193 /** 194 * Get the {@link Cipher#getTransformation() cipher-transformation} that identifies the encryption 195 * algorithm, the mode and the padding used to encrypt a record. The system usually passes 196 * this value to {@link CryptoRegistry#createCipher(String)}. 197 * @return the {@link Cipher#getTransformation() cipher-transformation}. Never <code>null</code>. 198 */ 199 public String getCipherTransformation() { 200 return cipherTransformation; 201 } 202 /** 203 * <p> 204 * Get the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>-algorithm 205 * used to protect a record against corruption/manipulation. 206 * </p> 207 * <p> 208 * Implementors of {@link CryptoManager}/{@link CryptoSession} should take {@link #MAC_ALGORITHM_NONE} 209 * into account! If this value equals that constant, MAC calculation and storage should be skipped. 210 * </p> 211 * @return the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>-algorithm. 212 */ 213 public String getMACAlgorithm() { 214 return macAlgorithm; 215 } 216 }