001    package org.cumulus4j.store.model;
002    
003    import javax.jdo.PersistenceManager;
004    import javax.jdo.annotations.IdGeneratorStrategy;
005    import javax.jdo.annotations.IdentityType;
006    import javax.jdo.annotations.NullValue;
007    import javax.jdo.annotations.PersistenceCapable;
008    import javax.jdo.annotations.Persistent;
009    import javax.jdo.annotations.PrimaryKey;
010    import javax.jdo.annotations.Queries;
011    import javax.jdo.annotations.Query;
012    import javax.jdo.annotations.Sequence;
013    import javax.jdo.annotations.SequenceStrategy;
014    import javax.jdo.annotations.Unique;
015    import javax.jdo.annotations.Version;
016    import javax.jdo.annotations.VersionStrategy;
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    @Sequence(name="EncryptionCoordinateSetSequence", datastoreSequence="EncryptionCoordinateSetSequence", initialValue=0, strategy=SequenceStrategy.CONTIGUOUS)
047    public class EncryptionCoordinateSet
048    {
049            /**
050             * <p>
051             * Constant for deactivating the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>.
052             * </p>
053             * <p>
054             * <b>Important: Deactivating the MAC is dangerous!</b> Choose this value only, if you are absolutely
055             * sure that your {@link #getCipherTransformation() cipher-transformation} already
056             * provides authentication - like <a target="_blank" href="http://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a>
057             * does for example.
058             * </p>
059             */
060            public static final String MAC_ALGORITHM_NONE = "NONE";
061    
062            @PrimaryKey
063            @Persistent(valueStrategy=IdGeneratorStrategy.NATIVE, sequence="EncryptionCoordinateSetSequence")
064            private Long encryptionCoordinateSetID;
065    
066            @Persistent(nullValue=NullValue.EXCEPTION)
067            private String cipherTransformation;
068    
069            @Persistent(nullValue=NullValue.EXCEPTION)
070            private String macAlgorithm;
071    
072            /**
073             * Create a new <code>EncryptionCoordinateSet</code>. This default constructor only exists
074             * for JDO and should never be used directly!
075             */
076            protected EncryptionCoordinateSet() { }
077    
078            /**
079             * Create a new <code>EncryptionCoordinateSet</code>. Instead of using this constructor,
080             * you should use {@link #createEncryptionCoordinateSet(PersistenceManager, String, String)}!
081             *
082             * @param cipherTransformation the cipher-transformation.
083             * @param macAlgorithm the MAC-algorithm.
084             */
085            protected EncryptionCoordinateSet(String cipherTransformation, String macAlgorithm)
086            {
087                    if (cipherTransformation == null)
088                            throw new IllegalArgumentException("cipherTransformation == null");
089    
090                    if (macAlgorithm == null)
091                            throw new IllegalArgumentException("macAlgorithm == null");
092    
093                    this.cipherTransformation = cipherTransformation;
094                    this.macAlgorithm = macAlgorithm;
095            }
096    
097            /**
098             * <p>
099             * Get the unique numeric identifier of this <code>EncryptionCoordinateSet</code>.
100             * </p>
101             * <p>
102             * Note: Implementors of {@link CryptoManager} (or {@link CryptoSession} respectively) might
103             * choose not to store the entire int value (4 bytes), but reduce the size. Every time the
104             * encryption configuration is changed, a new instance of this class is persisted. Restricting
105             * the size to 2 bytes, for example, still gives the administrator the possibility to change
106             * the configuration 65535 times - which is likely enough.
107             * </p>
108             *
109             * @return the unique numeric identifier (primary key).
110             */
111            public int getEncryptionCoordinateSetID() {
112                    if (encryptionCoordinateSetID == null)
113                            return -1;
114    
115                    if (encryptionCoordinateSetID.longValue() > Integer.MAX_VALUE)
116                            throw new IllegalStateException("encryptionCoordinateSetID > Integer.MAX_VALUE :: " + encryptionCoordinateSetID + " > " + Integer.MAX_VALUE);
117    
118                    return encryptionCoordinateSetID.intValue();
119            }
120    
121            /**
122             * Get the {@link Cipher#getTransformation() cipher-transformation} that identifies the encryption
123             * algorithm, the mode and the padding used to encrypt a record. The system usually passes
124             * this value to {@link CryptoRegistry#createCipher(String)}.
125             * @return the {@link Cipher#getTransformation() cipher-transformation}. Never <code>null</code>.
126             */
127            public String getCipherTransformation() {
128                    return cipherTransformation;
129            }
130            /**
131             * <p>
132             * Get the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>-algorithm
133             * used to protect a record against corruption/manipulation.
134             * </p>
135             * <p>
136             * Implementors of {@link CryptoManager}/{@link CryptoSession} should take {@link #MAC_ALGORITHM_NONE}
137             * into account! If this value equals that constant, MAC calculation and storage should be skipped.
138             * </p>
139             * @return the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a>-algorithm.
140             */
141            public String getMACAlgorithm() {
142                    return macAlgorithm;
143            }
144    }