1
1
using System ;
2
2
using System . Globalization ;
3
-
4
3
using Renci . SshNet . Common ;
4
+ using csp = System . Security . Cryptography ;
5
5
6
6
namespace Renci . SshNet . Security . Cryptography . Ciphers
7
7
{
@@ -17,10 +17,15 @@ public sealed class AesCipher : BlockCipher
17
17
private int _rounds ;
18
18
private uint [ ] _encryptionKey ;
19
19
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
24
29
25
30
#region Static Definition Tables
26
31
@@ -572,6 +577,11 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding)
572
577
{
573
578
throw new ArgumentException ( string . Format ( CultureInfo . CurrentCulture , "KeySize '{0}' is not valid for this algorithm." , keySize ) ) ;
574
579
}
580
+
581
+ #if FEATURE_AES_CSP
582
+ // initialize AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage)
583
+ useCSP = initCryptoServiceProvider ( mode , padding ) ;
584
+ #endif
575
585
}
576
586
577
587
/// <summary>
@@ -666,6 +676,204 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
666
676
return BlockSize ;
667
677
}
668
678
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
+
669
877
private uint [ ] GenerateWorkingKey ( bool isEncryption , byte [ ] key )
670
878
{
671
879
var KC = key . Length / 4 ; // key length in words
0 commit comments