001    /*
002     * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003     * Copyright (C) 2011 NightLabs Consulting GmbH
004     *
005     * This program is free software: you can redistribute it and/or modify
006     * it under the terms of the GNU Affero General Public License as
007     * published by the Free Software Foundation, either version 3 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU Affero General Public License for more details.
014     *
015     * You should have received a copy of the GNU Affero General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    package org.cumulus4j.store.crypto;
019    
020    import java.util.Date;
021    
022    import org.slf4j.Logger;
023    import org.slf4j.LoggerFactory;
024    
025    /**
026     * Abstract base-class for implementing {@link CryptoSession}s.
027     *
028     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
029     */
030    public abstract class AbstractCryptoSession implements CryptoSession
031    {
032            private static final Logger logger = LoggerFactory.getLogger(AbstractCryptoSession.class);
033    
034            private Date creationTimestamp = new Date();
035            private volatile Date lastUsageTimestamp = creationTimestamp;
036            private CryptoManager cryptoManager;
037            private String cryptoSessionID;
038            private volatile String keyStoreID;
039    
040            private volatile boolean closed;
041    
042            @Override
043            public CryptoManager getCryptoManager() {
044                    return cryptoManager;
045            }
046    
047            @Override
048            public void setCryptoManager(CryptoManager cryptoManager)
049            {
050                    if (cryptoManager == null)
051                            throw new IllegalArgumentException("cryptoManager == null");
052    
053                    if (cryptoManager == this.cryptoManager)
054                            return;
055    
056                    if (this.cryptoManager != null)
057                            throw new IllegalStateException("this.cryptoManager already assigned! Cannot modify!");
058    
059                    this.cryptoManager = cryptoManager;
060            }
061    
062            @Override
063            public String getCryptoSessionID()
064            {
065                    return cryptoSessionID;
066            }
067    
068            @Override
069            public void setCryptoSessionID(String cryptoSessionID)
070            {
071                    if (cryptoSessionID == null)
072                            throw new IllegalArgumentException("cryptoSessionID == null");
073    
074                    if (cryptoSessionID.equals(this.cryptoSessionID))
075                            return;
076    
077                    if (this.cryptoSessionID != null)
078                            throw new IllegalStateException("this.cryptoSessionID already assigned! Cannot modify!");
079    
080                    this.cryptoSessionID = cryptoSessionID;
081                    this.keyStoreID = null;
082                    logger.trace("setCryptoSessionID: cryptoSessionID={}", cryptoSessionID);
083            }
084    
085            @Override
086            public String getKeyStoreID() {
087                    String keyStoreID = this.keyStoreID;
088                    if (keyStoreID == null) {
089                            String cryptoSessionID = getCryptoSessionID();
090                            if (cryptoSessionID == null)
091                                    throw new IllegalStateException("cryptoSessionID == null :: setCryptoSessionID(...) was not yet called!");
092    
093                            // Our default format for a cryptoSessionID is:
094                            //
095                            // "${cryptoSessionIDPrefix}*${serial}*${random1}"
096                            //
097                            // ${cryptoSessionIDPrefix} is: "${keyStoreID}_${random2}"
098                            // The ${cryptoSessionIDPrefix} is used for routing key-request-messages to the right key manager
099                            // and for determining the key-store-id.
100                            //
101                            // ${serial} is a serial number to guarantee uniqueness (as ${random1} is pretty short).
102                            //
103                            // ${random1} is a random number making it much harder to guess a session-ID.
104    
105                            int colonIndex = cryptoSessionID.indexOf('_');
106                            if (colonIndex < 0)
107                                    throw new IllegalStateException("cryptoSessionID does not contain an underscore ('_'): "+ cryptoSessionID);
108    
109                            keyStoreID = cryptoSessionID.substring(0, colonIndex);
110                            this.keyStoreID = keyStoreID;
111                    }
112                    return keyStoreID;
113            }
114    
115            @Override
116            public Date getCreationTimestamp()
117            {
118                    return creationTimestamp;
119            }
120    
121            @Override
122            public Date getLastUsageTimestamp() {
123                    return lastUsageTimestamp;
124            }
125    
126            @Override
127            public void updateLastUsageTimestamp() {
128                    lastUsageTimestamp = new Date();
129            }
130    
131            /**
132             * {@inheritDoc}
133             * <p>
134             * Implementors should use {@link #assertNotClosed()} in their subclasses to check
135             * whether the operation is still allowed.
136             * </p>
137             */
138            @Override
139            public boolean isClosed() {
140                    return closed;
141            }
142    
143            /**
144             * Throws an {@link IllegalStateException}, if this session is already closed.
145             */
146            protected void assertNotClosed()
147            {
148                    if (isClosed())
149                            throw new IllegalStateException("This session (cryptoSessionID=\"" + cryptoSessionID + "\") is already closed!");
150            }
151    
152            /**
153             * {@inheritDoc}
154             * <p>
155             * When overriding this method, you should first call <code>super.close();</code> and
156             * then perform your own closing operations.
157             * </p>
158             */
159            @Override
160            public void close() {
161                    logger.trace("close: cryptoSessionID={}", cryptoSessionID);
162                    closed = true;
163                    cryptoManager.onCloseCryptoSession(this);
164            }
165    }