11using System ;
22using System . Globalization ;
33using Renci . SshNet . Common ;
4+ using csp = System . Security . Cryptography ;
45
56namespace Renci . SshNet . Security . Cryptography . Ciphers
67{
@@ -23,6 +24,19 @@ public sealed class AesCipher : BlockCipher
2324
2425 private uint C0 , C1 , C2 , C3 ;
2526
27+ #if FEATURE_AES_CSP
28+
29+ private csp . ICryptoTransform aesDecryptor ;
30+
31+ private csp . ICryptoTransform aesEncryptor ;
32+
33+ private bool useCSP ; // set to false when CSP is not available for a given mode; falls back to legacy code
34+
35+ private bool isCTRMode ;
36+
37+ private uint [ ] _ctrIV ;
38+ #endif
39+
2640 #region Static Definition Tables
2741
2842 private static readonly byte [ ] S =
@@ -571,6 +585,11 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding)
571585
572586 if ( ! ( keySize == 256 || keySize == 192 || keySize == 128 ) )
573587 throw new ArgumentException ( string . Format ( CultureInfo . CurrentCulture , "KeySize '{0}' is not valid for this algorithm." , keySize ) ) ;
588+
589+ #if FEATURE_AES_CSP
590+ // initialize AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage)
591+ useCSP = initCryptoServiceProvider ( mode , padding ) ;
592+ #endif
574593 }
575594
576595 /// <summary>
@@ -663,6 +682,204 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
663682 return BlockSize ;
664683 }
665684
685+ #if FEATURE_AES_CSP
686+
687+ /// <summary>
688+ /// Encrypts the specified data using AesCryptoServiceProvider
689+ /// </summary>
690+ /// <param name="data">The data.</param>
691+ /// <param name="offset">The zero-based offset in <paramref name="data"/> at which to begin encrypting.</param>
692+ /// <param name="length">The number of bytes to encrypt from <paramref name="data"/>.</param>
693+ /// <returns>Encrypted data</returns>
694+ public override byte [ ] Encrypt ( byte [ ] data , int offset , int length )
695+ {
696+ if ( useCSP )
697+ {
698+ if ( isCTRMode )
699+ return CTREncryptDecrypt ( data , offset , length ) ;
700+ else
701+ {
702+ if ( length % BlockSize == 0 )
703+ {
704+ byte [ ] output = new byte [ length ] ;
705+ aesEncryptor . TransformBlock ( data , offset , length , output , 0 ) ;
706+ return output ;
707+ }
708+ else
709+ {
710+ // adds padding
711+ byte [ ] output = aesEncryptor . TransformFinalBlock ( data , offset , length ) ;
712+ return output ;
713+ }
714+ }
715+ }
716+ else
717+ return base . Encrypt ( data , offset , length ) ;
718+ }
719+
720+ /// <summary>
721+ /// Decrypts the specified input using AesCryptoServiceProvider
722+ /// </summary>
723+ /// <param name="data">The input.</param>
724+ /// <param name="offset">The zero-based offset in <paramref name="data"/> at which to begin decrypting.</param>
725+ /// <param name="length">The number of bytes to decrypt from <paramref name="data"/>.</param>
726+ /// <returns>
727+ /// The decrypted data.
728+ /// </returns>
729+ public override byte [ ] Decrypt ( byte [ ] data , int offset , int length )
730+ {
731+ if ( useCSP )
732+ {
733+ if ( isCTRMode )
734+ return CTREncryptDecrypt ( data , offset , length ) ;
735+ else
736+ {
737+ if ( length % BlockSize == 0 )
738+ {
739+ byte [ ] output = new byte [ length ] ;
740+ aesDecryptor . TransformBlock ( data , offset , length , output , 0 ) ;
741+ return output ;
742+ }
743+ else
744+ {
745+ // handles padding
746+ byte [ ] output = aesDecryptor . TransformFinalBlock ( data , offset , length ) ;
747+ return output ;
748+ }
749+
750+
751+ //byte[] ok = base.Decrypt(data, offset, length);
752+ //for (int i = 0; i < a1.Length; i++)
753+ // if (a1[i] != ok[i] || a1.Length != ok.Length)
754+ // return null;
755+
756+ //for (int i = 0; i < a1.Length; i++)
757+ // if (a2[i] != ok[i] || a1.Length != ok.Length)
758+ // return null;
759+
760+ //return a1;
761+ }
762+ }
763+ else
764+ return base . Encrypt ( data , offset , length ) ;
765+ }
766+
767+ // initialize AesCryptoServiceProvider
768+ private bool initCryptoServiceProvider ( CipherMode mode , CipherPadding padding )
769+ {
770+ try
771+ {
772+ csp . PaddingMode cspPadding = padding == null ? csp . PaddingMode . None : csp . PaddingMode . PKCS7 ; // PKCS5 is same as PKCS7
773+ csp . CipherMode cspMode = 0 ;
774+ isCTRMode = mode is Modes . CtrCipherMode ;
775+
776+ if ( mode is Modes . CbcCipherMode )
777+ cspMode = csp . CipherMode . CBC ;
778+ else if ( isCTRMode )
779+ cspMode = csp . CipherMode . ECB ; // CTR uses ECB
780+ else
781+ return false ; // OFB and CFB not supported, fallback to managed code
782+
783+ // prepare IV array for CTR mode
784+ if ( isCTRMode )
785+ _ctrIV = GetPackedIV ( mode . IV ) ;
786+
787+ // create ICryptoTransform instances
788+ var aesProvider = new csp . AesCryptoServiceProvider ( )
789+ {
790+ BlockSize = BlockSize * 8 ,
791+ KeySize = Key . Length * 8 ,
792+ Mode = cspMode ,
793+ Padding = cspPadding ,
794+ Key = Key ,
795+ IV = mode . IV ,
796+ } ;
797+ aesEncryptor = aesProvider . CreateEncryptor ( Key , mode . IV ) ;
798+ aesDecryptor = aesProvider . CreateDecryptor ( Key , mode . IV ) ;
799+ return true ;
800+ }
801+ catch { } // fallback for unsupported key/iv/blocksize combinations
802+ return false ;
803+ }
804+
805+ // convert the IV into an array of uint[4]
806+ private uint [ ] GetPackedIV ( byte [ ] iv )
807+ {
808+ uint [ ] packedIV = new uint [ 4 ] ;
809+ packedIV [ 0 ] = ( uint ) ( ( iv [ 0 ] << 24 ) | ( iv [ 1 ] << 16 ) | ( iv [ 2 ] << 8 ) | ( iv [ 3 ] ) ) ;
810+ packedIV [ 1 ] = ( uint ) ( ( iv [ 4 ] << 24 ) | ( iv [ 5 ] << 16 ) | ( iv [ 6 ] << 8 ) | ( iv [ 7 ] ) ) ;
811+ packedIV [ 2 ] = ( uint ) ( ( iv [ 8 ] << 24 ) | ( iv [ 9 ] << 16 ) | ( iv [ 10 ] << 8 ) | ( iv [ 11 ] ) ) ;
812+ packedIV [ 3 ] = ( uint ) ( ( iv [ 12 ] << 24 ) | ( iv [ 13 ] << 16 ) | ( iv [ 14 ] << 8 ) | ( iv [ 15 ] ) ) ;
813+ return packedIV ;
814+ }
815+
816+ // Perform AES-CTR encryption/decryption
817+ private byte [ ] CTREncryptDecrypt ( byte [ ] data , int offset , int length )
818+ {
819+ int count = length / BlockSize ;
820+ if ( length % BlockSize != 0 ) count ++ ;
821+
822+ byte [ ] counter = CTRCreateCounterArray ( count ) ;
823+ byte [ ] aesCounter = aesEncryptor . TransformFinalBlock ( counter , 0 , counter . Length ) ;
824+ byte [ ] output = CTRArrayXOR ( aesCounter , data , offset , length ) ;
825+
826+ return output ;
827+ }
828+
829+ // creates the Counter array filled with incrementing copies of IV
830+ private byte [ ] CTRCreateCounterArray ( int blocks )
831+ {
832+ // fill an array with IV, increment by 1 for each copy
833+ uint [ ] counter = new uint [ blocks * 4 ] ;
834+ for ( int i = 0 ; i < counter . Length ; i += 4 )
835+ {
836+ // write IV to buffer (big endian)
837+ counter [ i ] = ( _ctrIV [ 0 ] << 24 ) | ( ( _ctrIV [ 0 ] << 8 ) & 0x00FF0000 ) | ( ( _ctrIV [ 0 ] >> 8 ) & 0x0000FF00 ) | ( _ctrIV [ 0 ] >> 24 ) ;
838+ counter [ i + 1 ] = ( _ctrIV [ 1 ] << 24 ) | ( ( _ctrIV [ 1 ] << 8 ) & 0x00FF0000 ) | ( ( _ctrIV [ 1 ] >> 8 ) & 0x0000FF00 ) | ( _ctrIV [ 1 ] >> 24 ) ;
839+ counter [ i + 2 ] = ( _ctrIV [ 2 ] << 24 ) | ( ( _ctrIV [ 2 ] << 8 ) & 0x00FF0000 ) | ( ( _ctrIV [ 2 ] >> 8 ) & 0x0000FF00 ) | ( _ctrIV [ 2 ] >> 24 ) ;
840+ counter [ i + 3 ] = ( _ctrIV [ 3 ] << 24 ) | ( ( _ctrIV [ 3 ] << 8 ) & 0x00FF0000 ) | ( ( _ctrIV [ 3 ] >> 8 ) & 0x0000FF00 ) | ( _ctrIV [ 3 ] >> 24 ) ;
841+
842+ // increment IV (little endian)
843+ for ( int j = 3 ; j >= 0 && ++ _ctrIV [ j ] == 0 ; j -- ) ;
844+ }
845+
846+ // copy uint[] to byte[]
847+ byte [ ] counterBytes = new byte [ blocks * 16 ] ;
848+ System . Buffer . BlockCopy ( counter , 0 , counterBytes , 0 , counterBytes . Length ) ;
849+ return counterBytes ;
850+ }
851+
852+ // XORs the input data with the encrypted Counter array to produce the final output
853+ // uses uint arrays for speed
854+ private byte [ ] CTRArrayXOR ( byte [ ] counter , byte [ ] data , int offset , int length )
855+ {
856+ int words = length / 4 ;
857+ if ( length % 4 != 0 ) words ++ ;
858+
859+ // convert original data to words
860+ uint [ ] datawords = new uint [ words ] ;
861+ System . Buffer . BlockCopy ( data , offset , datawords , 0 , length ) ;
862+
863+ // convert encrypted IV counter to words
864+ uint [ ] counterwords = new uint [ words ] ;
865+ System . Buffer . BlockCopy ( counter , 0 , counterwords , 0 , length ) ;
866+
867+ // XOR encrypted Counter with input data
868+ for ( int i = 0 ; i < words ; i ++ )
869+ counterwords [ i ] = counterwords [ i ] ^ datawords [ i ] ;
870+
871+ // copy uint[] to byte[]
872+ byte [ ] output = counter ;
873+ System . Buffer . BlockCopy ( counterwords , 0 , output , 0 , length ) ;
874+
875+ // adjust output for non-aligned lengths
876+ if ( output . Length > length )
877+ Array . Resize ( ref output , length ) ;
878+
879+ return output ;
880+ }
881+ #endif
882+
666883 private uint [ ] GenerateWorkingKey ( bool isEncryption , byte [ ] key )
667884 {
668885 int KC = key . Length / 4 ; // key length in words
0 commit comments