1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.crypto.DefaultBlockCipherService; 20 21 import hunt.shiro.crypto.AbstractSymmetricCipherService; 22 import hunt.shiro.crypto.OperationMode; 23 import hunt.shiro.crypto.PaddingScheme; 24 // import hunt.shiro.util.StringUtils; 25 26 import hunt.Exceptions; 27 import hunt.util.StringBuilder; 28 29 import std.algorithm; 30 import std.conv; 31 import std.string; 32 33 34 /** 35 * Base abstract class for block cipher algorithms. 36 * 37 * <h2>Usage</h2> 38 * Note that this class exists mostly to simplify algorithm-specific subclasses. Unless you understand the concepts of 39 * cipher modes of operation, block sizes, and padding schemes, and you want direct control of these things, you should 40 * typically not uses instances of this class directly. Instead, algorithm-specific subclasses, such as 41 * {@link AesCipherService}, {@link BlowfishCipherService}, and others are usually better suited for regular use. 42 * <p/> 43 * However, if you have the need to create a custom block cipher service where no sufficient algorithm-specific subclass 44 * exists in Shiro, this class would be very useful. 45 * 46 * <h2>Configuration</h2> 47 * Block ciphers can accept configuration parameters that direct how they operate. These parameters concatenated 48 * together in a single string comprise what the JDK JCA documentation calls a 49 * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#trans">transformation 50 * string</a>. We think that it is better for Shiro to construct this transformation string automatically based on its 51 * constituent parts instead of having the end-user construct the string manually, which may be error prone or 52 * confusing. To that end, Shiro {@link DefaultBlockCipherService}s have attributes that can be set individually in 53 * a type-safe manner based on your configuration needs, and Shiro will build the transformation string for you. 54 * <p/> 55 * The following sections typically document the configuration options for block (byte array) 56 * {@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])} method invocations. Streaming configuration 57 * for those same attributes are done via mirrored {@code streaming}* attributes, and their purpose is identical, but 58 * they're only used during streaming {@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} and 59 * {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])} methods. See the "Streaming" 60 * section below for more. 61 * 62 * <h3>Block Size</h3> 63 * The block size specifies the number of bits (not bytes) that the cipher operates on when performing an operation. 64 * It can be specified explicitly via the {@link #setBlockSize blockSize} attribute. If not set, the JCA Provider 65 * default will be used based on the cipher algorithm. Block sizes are usually very algorithm specific, so set this 66 * value only if you know you don't want the JCA Provider's default for the desired algorithm. For example, the 67 * AES algorithm's Rijndael implementation <em>only</em> supports a 128 bit block size and will not work with any other 68 * size. 69 * <p/> 70 * Also note that the {@link #setInitializationVectorSize initializationVectorSize} is usually the same as the 71 * {@link #setBlockSize blockSize} in block ciphers. If you change either attribute, you should ensure that the other 72 * attribute is correct for the target cipher algorithm. 73 * 74 * <h3>Operation Mode</h3> 75 * You may set the block cipher's<a href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">mode of 76 * operation</a> via the {@link #setMode(OperationMode) mode} attribute, which accepts a type-safe 77 * {@link OperationMode OperationMode} enum instance. This type safety helps avoid typos when specifying the mode and 78 * guarantees that the mode name will be recognized by the underlying JCA Provider. 79 * <p/> 80 * <b>*</b>If no operation mode is specified, Shiro defaults all of its block {@code CipherService} instances to the 81 * {@link OperationMode#CBC CBC} mode, specifically to support auto-generation of initialization vectors during 82 * encryption. This is different than the JDK's default {@link OperationMode#ECB ECB} mode because {@code ECB} does 83 * not support initialization vectors, which are necessary for strong encryption. See the 84 * {@link hunt.shiro.crypto.JcaCipherService JcaCipherService parent class} class JavaDoc for an extensive 85 * explanation on why we do this and why we do not use the Sun {@code ECB} default. You also might also want read 86 * the <a href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29">Wikipedia 87 * section on ECB<a/> and look at the encrypted image to see an example of why {@code ECB} should not be used in 88 * security-sensitive environments. 89 * <p/> 90 * In the rare case that you need to override the default with a mode not represented 91 * by the {@link OperationMode} enum, you may specify the raw mode name string that will be recognized by your JCA 92 * provider via the {@link #setModeName modeName} attribute. Because this is not type-safe, it is recommended only to 93 * use this attribute if the {@link OperationMode} enum does not represent your desired mode. 94 * <p/> 95 * <b>NOTE:</b> If you change the mode to one that does not support initialization vectors (such as 96 * {@link OperationMode#ECB ECB} or {@link OperationMode#NONE NONE}), you <em>must</em> turn off auto-generated 97 * initialization vectors by setting {@link #setGenerateInitializationVectors(bool) generateInitializationVectors} 98 * to {@code false}. Abandoning initialization vectors significantly weakens encryption, so think twice before 99 * disabling this feature. 100 * 101 * <h3>Padding Scheme</h3> 102 * Because block ciphers process messages in fixed-length blocks, if the final block in a message is not equal to the 103 * block length, <a href="http://en.wikipedia.org/wiki/Padding_(cryptography)">padding</a> is applied to match that 104 * size to maintain the total length of the message. This is good because it protects data patterns from being 105 * identified - when all chunks look the same length, it is much harder to infer what that data might be. 106 * <p/> 107 * You may set a padding scheme via the {@link #setPaddingScheme(PaddingScheme) paddingScheme} attribute, which 108 * accepts a type-safe {@link PaddingScheme PaddingScheme} enum instance. Like the {@link OperationMode} enum, 109 * this enum offers type safety to help avoid typos and guarantees that the mode will be recongized by the underlying 110 * JCA provider. 111 * <p/> 112 * <b>*</b>If no padding scheme is specified, this class defaults to the {@link PaddingScheme#PKCS5} scheme, specifically 113 * to be compliant with the default behavior of auto-generating initialization vectors during encryption (see the 114 * {@link hunt.shiro.crypto.JcaCipherService JcaCipherService parent class} class JavaDoc for why). 115 * <p/> 116 * In the rare case that you need to override the default with a scheme not represented by the {@link PaddingScheme} 117 * enum, you may specify the raw padding scheme name string that will be recognized by your JCA provider via the 118 * {@link #setPaddingScheme paddingSchemeName} attribute. Because this is not type-safe, it is recommended only to 119 * use this attribute if the {@link PaddingScheme} enum does not represent your desired scheme. 120 * 121 * <h2>Streaming</h2> 122 * Most people don't think of using block ciphers as stream ciphers, since their name implies working 123 * with block data (i.e. byte arrays) only. However, block ciphers can be turned into byte-oriented stream ciphers by 124 * using an appropriate {@link OperationMode operation mode} with a {@link #getStreamingBlockSize() streaming block size} 125 * of 8 bits. This is why the {@link CipherService} interface provides both block and streaming operations. 126 * <p/> 127 * Because this streaming 8-bit block size rarely changes across block-cipher algorithms, default values have been set 128 * for all three streaming configuration parameters. The defaults are: 129 * <ul> 130 * <li>{@link #setStreamingBlockSize(int) streamingBlockSize} = {@code 8} (bits)</li> 131 * <li>{@link #setStreamingMode streamingMode} = {@link OperationMode#CBC CBC}</li> 132 * <li>{@link #setStreamingPaddingScheme(PaddingScheme) streamingPaddingScheme} = {@link PaddingScheme#PKCS5 PKCS5}</li> 133 * </ul> 134 * <p/> 135 * These attributes have the same meaning as the {@code mode}, {@code blockSize}, and {@code paddingScheme} attributes 136 * described above, but they are applied during streaming method invocations only ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} 137 * and {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])}). 138 * 139 * @see BlowfishCipherService 140 * @see AesCipherService 141 * @see <a href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">Wikipedia: Block Cipher Modes of Operation</a> 142 * @since 1.0 143 */ 144 class DefaultBlockCipherService : AbstractSymmetricCipherService { 145 146 private enum int DEFAULT_BLOCK_SIZE = 0; 147 148 private enum string TRANSFORMATION_STRING_DELIMITER = "/"; 149 private enum int DEFAULT_STREAMING_BLOCK_SIZE = 8; //8 bits (1 byte) 150 151 private string modeName; 152 private int blockSize; //size in bits (not bytes) - i.e. a blockSize of 8 equals 1 byte. negative or zero value = use system default 153 private string paddingSchemeName; 154 155 private string streamingModeName; 156 private int streamingBlockSize; 157 private string streamingPaddingSchemeName; 158 159 private string transformationString; //cached value - rebuilt whenever any of its constituent parts change 160 private string streamingTransformationString; //cached value - rebuilt whenever any of its constituent parts change 161 162 163 /** 164 * Creates a new {@link DefaultBlockCipherService} using the specified block cipher {@code algorithmName}. Per this 165 * class's JavaDoc, this constructor also sets the following defaults: 166 * <ul> 167 * <li>{@code streamingMode} = {@link OperationMode#CBC CBC}</li> 168 * <li>{@code streamingPaddingScheme} = {@link PaddingScheme#NONE none}</li> 169 * <li>{@code streamingBlockSize} = 8</li> 170 * </ul> 171 * All other attributes are null/unset, indicating the JCA Provider defaults will be used. 172 * 173 * @param algorithmName the block cipher algorithm to use when encrypting and decrypting 174 */ 175 this(string algorithmName) { 176 super(algorithmName); 177 178 this.modeName = OperationMode.CBC.to!string(); 179 this.paddingSchemeName = PaddingScheme.PKCS5; 180 this.blockSize = DEFAULT_BLOCK_SIZE; //0 = use the JCA provider's default 181 182 this.streamingModeName = OperationMode.CBC.to!string(); 183 this.streamingPaddingSchemeName = PaddingScheme.PKCS5; 184 this.streamingBlockSize = DEFAULT_STREAMING_BLOCK_SIZE; 185 } 186 187 /** 188 * Returns the cipher operation mode name (as a string) to be used when constructing 189 * {@link javax.crypto.Cipher Cipher} transformation string or {@code null} if the JCA Provider default mode for 190 * the specified {@link #getAlgorithmName() algorithm} should be used. 191 * <p/> 192 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 193 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 194 * {@link #getStreamingModeName() streamingModeName} attribute is used when the block cipher is used for 195 * streaming operations. 196 * <p/> 197 * The default value is {@code null} to retain the JCA Provider default. 198 * 199 * @return the cipher operation mode name (as a string) to be used when constructing the 200 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default 201 * mode for the specified {@link #getAlgorithmName() algorithm} should be used. 202 */ 203 string getModeName() { 204 return modeName; 205 } 206 207 /** 208 * Sets the cipher operation mode name to be used when constructing the 209 * {@link javax.crypto.Cipher Cipher} transformation string. A {@code null} value indicates that the JCA Provider 210 * default mode for the specified {@link #getAlgorithmName() algorithm} should be used. 211 * <p/> 212 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 213 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 214 * {@link #getStreamingModeName() streamingModeName} attribute is used when the block cipher is used for 215 * streaming operations. 216 * <p/> 217 * The default value is {@code null} to retain the JCA Provider default. 218 * <p/> 219 * <b>NOTE:</b> most standard mode names are represented by the {@link OperationMode OperationMode} enum. That enum 220 * should be used with the {@link #setMode mode} attribute when possible to retain type-safety and reduce the 221 * possibility of errors. This method is better used if the {@link OperationMode} enum does not represent the 222 * necessary mode. 223 * 224 * @param modeName the cipher operation mode name to be used when constructing 225 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider 226 * default mode for the specified {@link #getAlgorithmName() algorithm} should be used. 227 * @see #setMode 228 */ 229 void setModeName(string modeName) { 230 this.modeName = modeName; 231 //clear out the transformation string so the next invocation will rebuild it with the new mode: 232 this.transformationString = null; 233 } 234 235 /** 236 * Sets the cipher operation mode of operation to be used when constructing the 237 * {@link javax.crypto.Cipher Cipher} transformation string. A {@code null} value indicates that the JCA Provider 238 * default mode for the specified {@link #getAlgorithmName() algorithm} should be used. 239 * <p/> 240 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 241 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 242 * {@link #setStreamingMode streamingMode} attribute is used when the block cipher is used for 243 * streaming operations. 244 * <p/> 245 * If the {@link OperationMode} enum cannot represent your desired mode, you can set the name explicitly 246 * via the {@link #setModeName modeName} attribute directly. However, because {@link OperationMode} represents all 247 * standard JDK mode names already, ensure that your underlying JCA Provider supports the non-standard name first. 248 * 249 * @param mode the cipher operation mode to be used when constructing 250 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider 251 * default mode for the specified {@link #getAlgorithmName() algorithm} should be used. 252 */ 253 void setMode(OperationMode mode) { 254 setModeName(mode.to!string()); 255 } 256 257 /** 258 * Returns the cipher algorithm padding scheme name (as a string) to be used when constructing 259 * {@link javax.crypto.Cipher Cipher} transformation string or {@code null} if the JCA Provider default mode for 260 * the specified {@link #getAlgorithmName() algorithm} should be used. 261 * <p/> 262 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 263 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 264 * {@link #getStreamingPaddingSchemeName() streamingPaddingSchemeName} attribute is used when the block cipher is 265 * used for streaming operations. 266 * <p/> 267 * The default value is {@code null} to retain the JCA Provider default. 268 * 269 * @return the padding scheme name (as a string) to be used when constructing the 270 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default 271 * padding scheme for the specified {@link #getAlgorithmName() algorithm} should be used. 272 */ 273 string getPaddingSchemeName() { 274 return paddingSchemeName; 275 } 276 277 /** 278 * Sets the padding scheme name to be used when constructing the 279 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default mode for 280 * the specified {@link #getAlgorithmName() algorithm} should be used. 281 * <p/> 282 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 283 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 284 * {@link #getStreamingPaddingSchemeName() streamingPaddingSchemeName} attribute is used when the block cipher is 285 * used for streaming operations. 286 * <p/> 287 * The default value is {@code null} to retain the JCA Provider default. 288 * <p/> 289 * <b>NOTE:</b> most standard padding schemes are represented by the {@link PaddingScheme PaddingScheme} enum. 290 * That enum should be used with the {@link #setPaddingScheme paddingScheme} attribute when possible to retain 291 * type-safety and reduce the possibility of errors. Calling this method however is suitable if the 292 * {@code PaddingScheme} enum does not represent the desired scheme. 293 * 294 * @param paddingSchemeName the padding scheme name to be used when constructing 295 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA 296 * Provider default padding scheme for the specified {@link #getAlgorithmName() algorithm} 297 * should be used. 298 * @see #setPaddingScheme 299 */ 300 void setPaddingSchemeName(string paddingSchemeName) { 301 this.paddingSchemeName = paddingSchemeName; 302 //clear out the transformation string so the next invocation will rebuild it with the new padding scheme: 303 this.transformationString = null; 304 } 305 306 /** 307 * Sets the padding scheme to be used when constructing the 308 * {@link javax.crypto.Cipher Cipher} transformation string. A {@code null} value indicates that the JCA Provider 309 * default padding scheme for the specified {@link #getAlgorithmName() algorithm} should be used. 310 * <p/> 311 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 312 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 313 * {@link #setStreamingPaddingScheme streamingPaddingScheme} attribute is used when the block cipher is used for 314 * streaming operations. 315 * <p/> 316 * If the {@link PaddingScheme PaddingScheme} enum does represent your desired scheme, you can set the name explicitly 317 * via the {@link #setPaddingSchemeName paddingSchemeName} attribute directly. However, because 318 * {@code PaddingScheme} represents all standard JDK scheme names already, ensure that your underlying JCA Provider 319 * supports the non-standard name first. 320 * 321 * @param paddingScheme the padding scheme to be used when constructing 322 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider 323 * default padding scheme for the specified {@link #getAlgorithmName() algorithm} should be used. 324 */ 325 void setPaddingScheme(PaddingScheme paddingScheme) { 326 setPaddingSchemeName(paddingScheme); 327 } 328 329 /** 330 * Returns the block cipher's block size to be used when constructing 331 * {@link javax.crypto.Cipher Cipher} transformation string or {@code 0} if the JCA Provider default block size 332 * for the specified {@link #getAlgorithmName() algorithm} should be used. 333 * <p/> 334 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 335 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 336 * {@link #getStreamingBlockSize() streamingBlockSize} attribute is used when the block cipher is used for 337 * streaming operations. 338 * <p/> 339 * The default value is {@code 0} which retains the JCA Provider default. 340 * 341 * @return the block cipher block size to be used when constructing the 342 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider default 343 * block size for the specified {@link #getAlgorithmName() algorithm} should be used. 344 */ 345 int getBlockSize() { 346 return blockSize; 347 } 348 349 /** 350 * Sets the block cipher's block size to be used when constructing 351 * {@link javax.crypto.Cipher Cipher} transformation string. {@code 0} indicates that the JCA Provider default 352 * block size for the specified {@link #getAlgorithmName() algorithm} should be used. 353 * <p/> 354 * This attribute is used <em>only</em> when constructing the transformation string for block (byte array) 355 * operations ({@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}). The 356 * {@link #getStreamingBlockSize() streamingBlockSize} attribute is used when the block cipher is used for 357 * streaming operations. 358 * <p/> 359 * The default value is {@code 0} which retains the JCA Provider default. 360 * <p/> 361 * <b>NOTE:</b> block cipher block sizes are very algorithm-specific. If you change this value, ensure that it 362 * will work with the specified {@link #getAlgorithmName() algorithm}. 363 * 364 * @param blockSize the block cipher block size to be used when constructing the 365 * {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider 366 * default block size for the specified {@link #getAlgorithmName() algorithm} should be used. 367 */ 368 void setBlockSize(int blockSize) { 369 this.blockSize = max(DEFAULT_BLOCK_SIZE, blockSize); 370 //clear out the transformation string so the next invocation will rebuild it with the new block size: 371 this.transformationString = null; 372 } 373 374 /** 375 * Same purpose as the {@link #getModeName modeName} attribute, but is used instead only for for streaming 376 * operations ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} and 377 * {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])}). 378 * <p/> 379 * Note that unlike the {@link #getModeName modeName} attribute, the default value of this attribute is not 380 * {@code null} - it is {@link OperationMode#CBC CBC} for reasons described in the class-level JavaDoc in the 381 * {@code Streaming} section. 382 * 383 * @return the transformation string mode name to be used for streaming operations only. 384 */ 385 string getStreamingModeName() { 386 return streamingModeName; 387 } 388 389 private bool isModeStreamingCompatible(string modeName) { 390 return !modeName.empty() && 391 icmp(modeName, OperationMode.ECB.to!string()) != 0 && 392 icmp(modeName, OperationMode.NONE.to!string()) != 0; 393 } 394 395 /** 396 * Sets the transformation string mode name to be used for streaming operations only. The default value is 397 * {@link OperationMode#CBC CBC} for reasons described in the class-level JavaDoc in the {@code Streaming} section. 398 * 399 * @param streamingModeName transformation string mode name to be used for streaming operations only 400 */ 401 void setStreamingModeName(string streamingModeName) { 402 if (!isModeStreamingCompatible(streamingModeName)) { 403 string msg = "mode [" ~ streamingModeName ~ "] is not a valid operation mode for block cipher streaming."; 404 throw new IllegalArgumentException(msg); 405 } 406 this.streamingModeName = streamingModeName; 407 //clear out the streaming transformation string so the next invocation will rebuild it with the new mode: 408 this.streamingTransformationString = null; 409 } 410 411 /** 412 * Sets the transformation string mode to be used for streaming operations only. The default value is 413 * {@link OperationMode#CBC CBC} for reasons described in the class-level JavaDoc in the {@code Streaming} section. 414 * 415 * @param mode the transformation string mode to be used for streaming operations only 416 */ 417 void setStreamingMode(OperationMode mode) { 418 setStreamingModeName(mode.to!string()); 419 } 420 421 string getStreamingPaddingSchemeName() { 422 return streamingPaddingSchemeName; 423 } 424 425 void setStreamingPaddingSchemeName(string streamingPaddingSchemeName) { 426 this.streamingPaddingSchemeName = streamingPaddingSchemeName; 427 //clear out the streaming transformation string so the next invocation will rebuild it with the new scheme: 428 this.streamingTransformationString = null; 429 } 430 431 void setStreamingPaddingScheme(PaddingScheme scheme) { 432 setStreamingPaddingSchemeName(scheme); 433 } 434 435 int getStreamingBlockSize() { 436 return streamingBlockSize; 437 } 438 439 void setStreamingBlockSize(int streamingBlockSize) { 440 this.streamingBlockSize = max(DEFAULT_BLOCK_SIZE, streamingBlockSize); 441 //clear out the streaming transformation string so the next invocation will rebuild it with the new block size: 442 this.streamingTransformationString = null; 443 } 444 445 /** 446 * Returns the transformation string to use with the {@link javax.crypto.Cipher#getInstance} call. If 447 * {@code streaming} is {@code true}, a block-cipher transformation string compatible with streaming operations will 448 * be constructed and cached for re-use later (see the class-level JavaDoc for more on using block ciphers 449 * for streaming). If {@code streaming} is {@code false} a normal block-cipher transformation string will 450 * be constructed and cached for later re-use. 451 * 452 * @param streaming if the transformation string is going to be used for a Cipher performing stream-based encryption or not. 453 * @return the transformation string 454 */ 455 protected string getTransformationString(bool streaming) { 456 if (streaming) { 457 if (this.streamingTransformationString is null) { 458 this.streamingTransformationString = buildStreamingTransformationString(); 459 } 460 return this.streamingTransformationString; 461 } else { 462 if (this.transformationString is null) { 463 this.transformationString = buildTransformationString(); 464 } 465 return this.transformationString; 466 } 467 } 468 469 private string buildTransformationString() { 470 return buildTransformationString(getModeName(), getPaddingSchemeName(), getBlockSize()); 471 } 472 473 private string buildStreamingTransformationString() { 474 return buildTransformationString(getStreamingModeName(), getStreamingPaddingSchemeName(), 0); 475 } 476 477 private string buildTransformationString(string modeName, string paddingSchemeName, int blockSize) { 478 StringBuilder sb = new StringBuilder(getAlgorithmName()); 479 if (!modeName.empty()) { 480 sb.append(TRANSFORMATION_STRING_DELIMITER).append(modeName); 481 } 482 if (blockSize > 0) { 483 sb.append(blockSize); 484 } 485 if (!paddingSchemeName.empty()) { 486 sb.append(TRANSFORMATION_STRING_DELIMITER).append(paddingSchemeName); 487 } 488 return sb.toString(); 489 } 490 491 /** 492 * Returns {@code true} if the specified cipher operation mode name supports initialization vectors, 493 * {@code false} otherwise. 494 * 495 * @param modeName the raw text name of the mode of operation 496 * @return {@code true} if the specified cipher operation mode name supports initialization vectors, 497 * {@code false} otherwise. 498 */ 499 private bool isModeInitializationVectorCompatible(string modeName) { 500 return !modeName.empty() && 501 icmp(modeName, OperationMode.ECB.to!string()) != 0 && 502 icmp(modeName, OperationMode.NONE.to!string()) != 0; 503 } 504 505 /** 506 * Overrides the parent implementation to ensure initialization vectors are always generated if streaming is 507 * enabled (block ciphers <em>must</em> use initialization vectors if they are to be used as a stream cipher). If 508 * not being used as a stream cipher, then the value is computed based on whether or not the currently configured 509 * {@link #getModeName modeName} is compatible with initialization vectors as well as the result of the configured 510 * {@link #setGenerateInitializationVectors(bool) generateInitializationVectors} value. 511 * 512 * @param streaming whether or not streaming is being performed 513 * @return {@code true} if streaming or a value computed based on if the currently configured mode is compatible 514 * with initialization vectors. 515 */ 516 override 517 protected bool isGenerateInitializationVectors(bool streaming) { 518 return streaming || super.isGenerateInitializationVectors() && 519 isModeInitializationVectorCompatible(getModeName()); 520 } 521 522 override 523 protected byte[] generateInitializationVector(bool streaming) { 524 if (streaming) { 525 string streamingModeName = getStreamingModeName(); 526 if (!isModeInitializationVectorCompatible(streamingModeName)) { 527 string msg = "streamingMode attribute value [" ~ streamingModeName ~ "] does not support " ~ 528 "Initialization Vectors. Ensure the streamingMode value represents an operation mode " ~ 529 "that is compatible with initialization vectors."; 530 throw new IllegalStateException(msg); 531 } 532 } else { 533 string modeName = getModeName(); 534 if (!isModeInitializationVectorCompatible(modeName)) { 535 string msg = "mode attribute value [" ~ modeName ~ "] does not support " ~ 536 "Initialization Vectors. Ensure the mode value represents an operation mode " ~ 537 "that is compatible with initialization vectors."; 538 throw new IllegalStateException(msg); 539 } 540 } 541 return super.generateInitializationVector(streaming); 542 } 543 }