1

I am trying to perform encryption and decryption using the plug-in that I will show you below.

The decryption works perfectly for me as I have used an AES cipher from my backend and it converts it perfectly.

The problem is the following: When you encrypt, get the result and want to decrypt in dart it does not give the expected result.

I guess the IV and SALT I'm doing it wrong, I hope to get some advice or help, thanks.

PointyCastle in pub.dev

crypto.dart

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';

class Crypto {
  static const String password = "password_here";
  static const String algorithm = "AES";
  static FortunaRandom? _secureRandom;

  /// [decrypt] Method
  /// Requires one parameter: ```cipherText```
  static String decrypt( String cipherText ) {
    CBCBlockCipher cipher = CBCBlockCipher(BlockCipher( algorithm ));
    
    Uint8List ciphertextlist = base64.decode(cipherText);
    Uint8List salt = generateRandomBytes(32);
    Uint8List key = _generateKey(password, salt);
    Uint8List iv = generateRandomBytes(128 ~/ 8);
    Uint8List encrypted = ciphertextlist.sublist(20 + 16);

    ParametersWithIV<KeyParameter> params = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
    PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter> paddingParams = PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter>(params, null);
    PaddedBlockCipherImpl paddingCipher = PaddedBlockCipherImpl(PKCS7Padding(), cipher);
    paddingCipher.init(false, paddingParams);
    var val = paddingCipher.process(encrypted);
    String decrypted = String.fromCharCodes(val);
    return decrypted;
  }

  /// [encrypt] Method
  /// Requieres one parameter: ```plainText```
  static String encrypt( String plainText ) {
    final CBCBlockCipher cbcCipher = CBCBlockCipher(BlockCipher( algorithm ));
    List<int> data = utf8.encode( plainText );
    Uint8List iv = generateRandomBytes(128 ~/ 8);
    Uint8List salt = generateRandomBytes(32);
    Uint8List key = _generateKey(password, Uint8List.fromList(salt));
    final ParametersWithIV<KeyParameter> ivParams = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
    final PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter> paddingParams =PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter>(ivParams, null);
    final PaddedBlockCipherImpl paddedCipher = PaddedBlockCipherImpl(PKCS7Padding(), cbcCipher);
    paddedCipher.init(true, paddingParams);

    try {
      
      return base64.encode(paddedCipher.process(Uint8List.fromList(data)));
    } catch (e) {
      return '';
    }
  }

  /// [_generateKey] Method
  /// Generates the key to Uint8List for encryption and description.
  static Uint8List _generateKey(String passphrase, Uint8List salt) {    
    Uint8List passphraseInt8List = Uint8List.fromList(passphrase.codeUnits);
    KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64));    
    Pbkdf2Parameters params = Pbkdf2Parameters(salt, 65556, 32);            
    derivator.init(params);
    return derivator.process(passphraseInt8List);
  }

  
  static Uint8List generateRandomBytes(int numBytes) {
    if (_secureRandom == null) {
      _secureRandom = FortunaRandom();

      final seedSource = Random.secure();
      final seeds = <int>[];
      for (int i = 0; i < 32; i++) {
        seeds.add(seedSource.nextInt(256));
      }
      _secureRandom!.seed(KeyParameter(Uint8List.fromList(seeds)));
    }
    return _secureRandom!.nextBytes(numBytes);
  }
}

crypto_test.dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'models/crypto.dart';

void main() {
    test('Descrypt Data', () async {
      if( kDebugMode ){    
        String cipherText = "2FA4qsjxXzk5kp6cnbv8QrlcKPyxO/PihUky8HVkNf2hRlIAq25yCc1RbidvM7chE7JzuZUuNLIeoiEKr+vWx4AhSQclh94iC4eZMIGjyFllOn8qZl2zM+cYMuVp0zS5klVIgefBDd+SJSvsIElCBIAmsnY=";
        final decryptToHuman = Crypto.decrypt( cipherText );
        print( decryptToHuman );
      }
    });

    test('Encrypt Data', () async {
      if( kDebugMode ){
        Map<String, dynamic> toCipherData = {
          'email': 'Example@example.com',
          'passowrd': '12345678'
        };
        final String json = jsonEncode( toCipherData );
        final cipherText = Crypto.encrypt( json );
        print( "Texto cifrado: $cipherText" );
      }
    });
}

Error debug console:

Invalid argument(s): Input data length must be a multiple of cipher's block size
PaddedBlockCipherImpl.process
package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart:60
Crypto.decrypt
test\…\models\crypto.dart:38
main.<fn>
test\…\crypto\encrypt_test.dart:10
2

✖ Descrypt Data
Exited (1)
DomingoMG
  • 1,827
  • 2
  • 23
  • 44
  • When you decrypt, your binary form is saly+iv+ct, but when you encrypt you are just returning the ciphertext. Did you forget to prepend the salt and iv? Also, on encrypt you are using a 1 byte salt (where that one byte = 20) rather than a 20 byte salt! – Richard Heap Nov 25 '22 at 13:59
  • The problem is in the SALT and the IV I don't know how to pass it. – DomingoMG Nov 25 '22 at 14:11
  • Is it true, then I will have to pass 20 bytes instead of 1 byte? – DomingoMG Nov 25 '22 at 14:12
  • 2
    You should create a *random* salt and a *random* IV when encrypting. Using static values (as in the current code) is insecure. So that the values are known for decryption, they are usually concatenated during encryption, e.g. *salt|IV|ciphertext*. On the decrypting side they are separated (which is already implemented). By the way, sending salt and IV is not critical, since both are not secret. – Topaco Nov 25 '22 at 14:40
  • I have incorporated a method named generateRandomBytes I have added it to the IV and SALT but I still have the same problem, I will have to pass the IV and SALT that I used to decrypt the data? – DomingoMG Nov 25 '22 at 14:59
  • 1
    Aside: change `nextInt(255)` to `nextInt(256)` (read the doc of nextInt to see that the top bound is exclusive) – Richard Heap Nov 25 '22 at 15:02
  • What's the point of `latin1.decode(generateRandomBytes(32));` and then trying to reverse that the next line? Leave salt in binary form. The main problem is that you still are not returning salt+iv+ct from encrypt. – Richard Heap Nov 25 '22 at 15:05
  • You are right, I was experimenting, I have put it as you indicated – DomingoMG Nov 25 '22 at 15:09

1 Answers1

0

The solution to my problem was solved in this way.

Thanks for your help.

In encrypt method

    Uint8List encryptedTextBytes = paddingCipher.process( plainTextBytes );
    final buffer = Uint8List(salt.length + iv.length + encryptedTextBytes.length);
    List.copyRange(buffer, 0, salt, 0, salt.length);
    List.copyRange(buffer, salt.length, iv, 0, iv.length);
    List.copyRange(buffer, salt.length+iv.length, encryptedTextBytes, 0, encryptedTextBytes.length);
DomingoMG
  • 1,827
  • 2
  • 23
  • 44