001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library 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 GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.security;
020
021import java.util.Random;
022
023import lucee.runtime.crypt.BinConverter;
024import lucee.runtime.crypt.BlowfishCBC;
025import lucee.runtime.crypt.BlowfishECB;
026import lucee.runtime.crypt.SHA1;
027
028/**
029  * support class for easy string encryption with the Blowfish algorithm,
030  * now in CBC mode with a SHA-1 key setup and correct padding
031  */
032public final class SerialDecoder {
033
034  BlowfishCBC m_bfish;
035
036
037
038  // one random generator for all simple callers...
039
040  static Random m_rndGen;
041
042
043
044  // ...and created early
045
046  static {
047
048    m_rndGen = new Random();
049
050  };
051
052
053
054
055
056
057
058  /**
059
060    * constructor to set up a string as the key (oversized password will be cut)
061
062    * @param sPassword the password (treated as a real unicode array)
063
064    */
065
066  public SerialDecoder(String sPassword) {
067
068
069
070    // hash down the password to a 160bit key
071
072    SHA1 hasher = new SHA1();
073
074    hasher.update(sPassword);
075
076    hasher.finalize();
077
078
079
080    // setup the encryptor (use a dummy IV)
081
082    m_bfish = new BlowfishCBC(hasher.getDigest(), 0);
083
084    hasher.clear();
085
086  };
087
088  /**
089
090    * decrypts a hexbin string (handling is case sensitive)
091
092    * @param sCipherText hexbin string to decrypt
093
094    * @return decrypted string (null equals an error)
095
096    */
097
098  public String decrypt(String sCipherText) {
099
100
101
102    // get the number of estimated bytes in the string (cut off broken blocks)
103
104    int nLen = (sCipherText.length() >> 1) & ~7;
105
106
107
108    // does the given stuff make sense (at least the CBC IV)?
109
110    if (nLen < BlowfishECB.BLOCKSIZE)
111
112      return null;
113
114
115
116    // get the CBC IV
117
118    byte[] cbciv = new byte[BlowfishECB.BLOCKSIZE];
119
120    int nNumOfBytes = BinConverter.binHexToBytes(sCipherText,
121
122                                                 cbciv,
123
124                                                 0,
125
126                                                 0,
127
128                                                 BlowfishECB.BLOCKSIZE);
129
130    if (nNumOfBytes < BlowfishECB.BLOCKSIZE)
131
132      return null;
133
134    // (got it)
135
136    m_bfish.setCBCIV(cbciv);
137
138
139
140    // something left to decrypt?
141
142    nLen -= BlowfishECB.BLOCKSIZE;
143
144    if (nLen == 0)
145
146      return "";
147
148
149
150    // get all data bytes now
151
152    byte[] buf = new byte[nLen];
153
154    nNumOfBytes = BinConverter.binHexToBytes(sCipherText,
155
156                                             buf,
157
158                                             BlowfishECB.BLOCKSIZE * 2,
159
160                                             0,
161
162                                             nLen);
163
164
165
166    // we cannot accept broken binhex sequences due to padding
167
168    // and decryption
169
170    if (nNumOfBytes < nLen)
171
172      return null;
173
174
175
176    // decrypt the buffer
177
178    m_bfish.decrypt(buf);
179
180
181
182    // get the last padding byte
183
184    int nPadByte = buf[buf.length - 1] & 0x0ff;
185
186    // ( try to get all information if the padding doesn't seem to be correct)
187
188    if ((nPadByte > 8) || (nPadByte < 0))
189
190      nPadByte = 0;
191
192
193
194    // calculate the real size of this message
195
196    nNumOfBytes -= nPadByte;
197
198    if (nNumOfBytes < 0)
199
200      return "";
201
202
203
204    // success
205
206    return BinConverter.byteArrayToUNCString(buf, 0, nNumOfBytes);
207
208  };
209
210
211
212
213
214  /**
215
216    * destroys (clears) the encryption engine,
217
218    * after that the instance is not valid anymore
219
220    */
221
222  public void destroy() {
223
224    m_bfish.cleanUp();
225
226  };
227
228};
229
230