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