1
1
using System ;
2
2
using System . Globalization ;
3
3
using Renci . SshNet . Common ;
4
+ using csp = System . Security . Cryptography ;
4
5
5
6
namespace Renci . SshNet . Security . Cryptography . Ciphers
6
7
{
@@ -23,6 +24,19 @@ public sealed class AesCipher : BlockCipher
23
24
24
25
private uint C0 , C1 , C2 , C3 ;
25
26
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
+
26
40
#region Static Definition Tables
27
41
28
42
private static readonly byte [ ] S =
@@ -571,6 +585,11 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding)
571
585
572
586
if ( ! ( keySize == 256 || keySize == 192 || keySize == 128 ) )
573
587
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
574
593
}
575
594
576
595
/// <summary>
@@ -663,6 +682,204 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
663
682
return BlockSize ;
664
683
}
665
684
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
+
666
883
private uint [ ] GenerateWorkingKey ( bool isEncryption , byte [ ] key )
667
884
{
668
885
int KC = key . Length / 4 ; // key length in words
0 commit comments