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    }