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 }