v2 #12
							
								
								
									
										190
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								README.md
									
									
									
									
									
								
							| @ -1,10 +1,3 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2019-2023 Hugo Pointcheval |  | ||||||
|  *  |  | ||||||
|  * Use of this source code is governed by an MIT-style |  | ||||||
|  * license that can be found in the LICENSE file or at |  | ||||||
|  * https://opensource.org/licenses/MIT. |  | ||||||
|  */ |  | ||||||
| <p align="center"> | <p align="center"> | ||||||
| <img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255, 255, 255)"> | <img width="700px" src="resources/native_crypto.png" style="background-color: rgb(255, 255, 255)"> | ||||||
| <h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5> | <h5 align="center">Fast and powerful cryptographic functions for Flutter.</h5> | ||||||
| @ -42,6 +35,71 @@ For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB** | |||||||
| 
 | 
 | ||||||
| In short, NativeCrypto is incomparable with PointyCastle. | In short, NativeCrypto is incomparable with PointyCastle. | ||||||
| 
 | 
 | ||||||
|  | ## Features | ||||||
|  | 
 | ||||||
|  | * Hash functions | ||||||
|  |     - SHA-256 | ||||||
|  |     - SHA-384 | ||||||
|  |     - SHA-512 | ||||||
|  | * HMAC functions | ||||||
|  |     - HMAC-SHA-256 | ||||||
|  |     - HMAC-SHA-384 | ||||||
|  |     - HMAC-SHA-512 | ||||||
|  | * Secure random | ||||||
|  | * PBKDF2 | ||||||
|  | * AES | ||||||
|  |     - Uint8List encryption/decryption | ||||||
|  |     - File encryption/decryption | ||||||
|  | 
 | ||||||
|  | ## Quick start | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | 
 | ||||||
|  | Future<void> main() async { | ||||||
|  |     // Message to encrypt | ||||||
|  |     final Uint8List message = 'Hello World!'.toBytes(); | ||||||
|  |      | ||||||
|  |     // Ask user for a password | ||||||
|  |     final String password = await getPassword(); | ||||||
|  | 
 | ||||||
|  |     // Initialize a PBKDF2 object | ||||||
|  |     final Pbkdf2 pbkdf2 = Pbkdf2( | ||||||
|  |         length: 32, // 32 bytes | ||||||
|  |         iterations: 1000, | ||||||
|  |         salt: 'salt'.toBytes(), | ||||||
|  |         hashAlgorithm: HashAlgorithm.sha256, | ||||||
|  |     ); | ||||||
|  |      | ||||||
|  |     // Derive a secret key from the password | ||||||
|  |     final SecretKey secretKey = await pbkdf2(password: password); | ||||||
|  | 
 | ||||||
|  |     // Initialize an AES cipher | ||||||
|  |     final AES cipher = AES( | ||||||
|  |         key: secretKey, | ||||||
|  |         mode: AESMode.gcm, | ||||||
|  |         padding: AESPadding.none, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     // Encrypt the message | ||||||
|  |     final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message); | ||||||
|  | 
 | ||||||
|  |     // Decrypt the message | ||||||
|  |     final Uint8List decryptedMessage = await cipher.decrypt(cipherText); | ||||||
|  | 
 | ||||||
|  |     // Verify and print the decrypted message | ||||||
|  |     assert(listEquals(message, decryptedMessage)); | ||||||
|  |      | ||||||
|  |     print(decryptedMessage.toStr()); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Check the [example](./native_crypto/example) for a complete example. | ||||||
|  | 
 | ||||||
|  | Please take a look a the compatibility table below to check if your target is supported. | ||||||
|  | 
 | ||||||
|  | > Note: This **Flutter** example must run on a real device or a simulator. | ||||||
|  | 
 | ||||||
| ## Usage | ## Usage | ||||||
| 
 | 
 | ||||||
| First, check compatibility with your targets. | First, check compatibility with your targets. | ||||||
| @ -50,26 +108,42 @@ First, check compatibility with your targets. | |||||||
| | --- | ------- | ----- | ----- | ------- | --- | | | --- | ------- | ----- | ----- | ------- | --- | | ||||||
| | ✅  | ✅      | ❌     | ❌     | ❌      | ❌  | | | ✅  | ✅      | ❌     | ❌     | ❌      | ❌  | | ||||||
| 
 | 
 | ||||||
|  | > Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md))  | ||||||
|  | 
 | ||||||
| #### Hash | #### Hash | ||||||
| 
 | 
 | ||||||
| To digest a message, you can use the following function: | To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| Uint8List hash = await HashAlgorithm.sha256.digest(message); | Hash hasher = Sha256(); | ||||||
|  | Uint8List digest = await hasher.digest(message); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| > In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512 | > In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512 | ||||||
| 
 | 
 | ||||||
| #### Keys | #### HMAC | ||||||
| 
 | 
 | ||||||
| You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random. | To generate a HMAC, you'll need to initialize a `Hmac` object. Then, you can generate a HMAC from a message and a secret key. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74])); | Hmac hmac = HmacSha256(); | ||||||
|  | Uint8List hmac = await hmac.digest(message, secretKey); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > In NativeCrypto, you can use the following HMAC functions: HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512 | ||||||
|  | 
 | ||||||
|  | #### Keys | ||||||
|  | 
 | ||||||
|  | You can build a `SecretKey` from utf8, utf16, base64, base16 (hex) strings, int list or raw bytes. You can also generate a SecretKey from secure random. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | SecretKey secretKey = SecretKey(bytes); // bytes is a Uint8List | ||||||
| SecretKey secretKey = SecretKey.fromUtf8('secret'); | SecretKey secretKey = SecretKey.fromUtf8('secret'); | ||||||
|  | SecretKet secretKey = SecretKey.fromUtf16('secret'); | ||||||
| SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0'); | SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0'); | ||||||
| SecretKey secretKey = SecretKey.fromBase16('63657274'); | SecretKey secretKey = SecretKey.fromBase16('63657274'); | ||||||
| SecretKey secretKey = await SecretKey.fromSecureRandom(256); | SecretKey secretKey = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]); | ||||||
|  | SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| #### Key derivation | #### Key derivation | ||||||
| @ -79,20 +153,21 @@ You can derive a `SecretKey` using **PBKDF2**. | |||||||
| First, you need to initialize a `Pbkdf2` object. | First, you need to initialize a `Pbkdf2` object. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| Pbkdf2 pbkdf2 = Pbkdf2( | final Pbkdf2 pbkdf2 = Pbkdf2( | ||||||
|     keyBytesCount: 32, |     length: 32, // 32 bytes | ||||||
|     iterations: 1000, |     iterations: 1000, | ||||||
|     algorithm: HashAlgorithm.sha512, |     salt: salt.toBytes(), | ||||||
|  |     hashAlgorithm: HashAlgorithm.sha256, | ||||||
| ); | ); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Then, you can derive a `SecretKey` from a password and salt. | Then, you can derive a `SecretKey` from a password. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt'); | SecretKey secretKey = await pbkdf2(password: password);  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| > In NativeCrypto, you can use the following key derivation function: PBKDF2 | > Note: Pbkdf2 is a callable class. You can use it like a function. | ||||||
| 
 | 
 | ||||||
| #### Cipher | #### Cipher | ||||||
| 
 | 
 | ||||||
| @ -101,44 +176,79 @@ And now, you can use the `SecretKey` to encrypt/decrypt a message. | |||||||
| First, you need to initialize a `Cipher` object. | First, you need to initialize a `Cipher` object. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| AES cipher = AES(secretKey); | final AES cipher = AES( | ||||||
|  |     key: key, | ||||||
|  |     mode: AESMode.gcm, | ||||||
|  |     padding: AESPadding.none, | ||||||
|  | ); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Then, you can encrypt your message. | Then, you can encrypt your message. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| CipherTextWrapper wrapper = await cipher.encrypt(message); | final CipherText<AESCipherChunk> cipherText = await cipher.encrypt(message);  | ||||||
| 
 |  | ||||||
| CipherText cipherText = wrapper.unwrap<CipherText>(); |  | ||||||
| // same as |  | ||||||
| CipherText cipherText = wrapper.single; |  | ||||||
| 
 |  | ||||||
| // or |  | ||||||
| 
 |  | ||||||
| List<CipherText> cipherTexts = wrapper.unwrap<List<CipherText>>(); |  | ||||||
| // same as |  | ||||||
| List<CipherText> cipherTexts = wrapper.list; |  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List<CipherText>` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured. | After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` . | ||||||
| 
 | 
 | ||||||
| Uppon receiving encrypted message, you can decrypt it. | Uppon receiving encrypted message `receivedData` , you can decrypt it. | ||||||
| You have to reconstruct the wrapper before decrypting. | You have to reconstruct the ciphertext and the setup the chunk factory. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| CipherTextWrapper wrapper = CipherTextWrapper.fromBytes( | final CipherText<AESCipherChunk> receivedCipherText CipherText( | ||||||
|     data, |     receivedData, | ||||||
|     ivLength: AESMode.gcm.ivLength, |     chunkFactory: (bytes) => AESCipherChunk( | ||||||
|     tagLength: AESMode.gcm.tagLength, |         bytes, | ||||||
| ); |         ivLength: cipher.mode.ivLength, | ||||||
|  |         tagLength: cipher.mode.tagLength, | ||||||
|  |     ), | ||||||
|  | ), | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Then, you can decrypt your message. | Then, you can decrypt your message. | ||||||
| 
 | 
 | ||||||
| ```dart | ```dart | ||||||
| Uint8List message = await cipher.decrypt(wrapper); | Uint8List message = await cipher.decrypt(receivedCipherText); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | #### Files | ||||||
|  | 
 | ||||||
|  | You can encrypt/decrypt files. | ||||||
|  | 
 | ||||||
|  | First, you need to initialize a `Cipher` object. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final AES cipher = AES( | ||||||
|  |     key: key, | ||||||
|  |     mode: AESMode.gcm, | ||||||
|  |     padding: AESPadding.none, | ||||||
|  | ); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Then, you can encrypt your file. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | await cipher.encryptFile(plainText, cipherText); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > Note: `plainText` and `cipherText` are `File` objects. | ||||||
|  | 
 | ||||||
|  | You can decrypt your file. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | await cipher.decryptFile(cipherText, plainText); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### Advanced | ||||||
|  | 
 | ||||||
|  | You can force the use of a specific IV. Please note that the IV must be unique for each encryption. | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | final CipherText<AESCipherChunk> cipherText = await cipher.encryptWithIV(message, iv); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing. | ||||||
|  | 
 | ||||||
| ## Development | ## Development | ||||||
| 
 | 
 | ||||||
| ### Android | ### Android | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // Copyright 2019-2023 Hugo Pointcheval | // Copyright 2019-2023 Hugo Pointcheval | ||||||
| //  | // | ||||||
| // Use of this source code is governed by an MIT-style | // Use of this source code is governed by an MIT-style | ||||||
| // license that can be found in the LICENSE file or at | // license that can be found in the LICENSE file or at | ||||||
| // https://opensource.org/licenses/MIT. | // https://opensource.org/licenses/MIT. | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| abstract class Constants { | abstract class Constants { | ||||||
|   /// The default chunk size in bytes used for encryption and decryption. |   /// The default chunk size in bytes used for encryption and decryption. | ||||||
|   ///  |   /// | ||||||
|   /// ~32MB |   /// ~32MB | ||||||
|   static const int defaultChunkSize = 33554432; |   static const int defaultChunkSize = 33554432; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,10 +8,13 @@ | |||||||
| enum Encoding { | enum Encoding { | ||||||
|   /// UTF-8 encoding, as defined by the Unicode standard. |   /// UTF-8 encoding, as defined by the Unicode standard. | ||||||
|   utf8, |   utf8, | ||||||
|  | 
 | ||||||
|   /// UTF-16 encoding, as defined by the Unicode standard. |   /// UTF-16 encoding, as defined by the Unicode standard. | ||||||
|   utf16, |   utf16, | ||||||
|  | 
 | ||||||
|   /// Base64 encoding, as defined by RFC 4648. |   /// Base64 encoding, as defined by RFC 4648. | ||||||
|   base64, |   base64, | ||||||
|  | 
 | ||||||
|   /// Hexadecimal encoding. |   /// Hexadecimal encoding. | ||||||
|   base16, |   base16, | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,8 +8,10 @@ | |||||||
| enum HashAlgorithm { | enum HashAlgorithm { | ||||||
|   /// The SHA-256 hash algorithm. |   /// The SHA-256 hash algorithm. | ||||||
|   sha256, |   sha256, | ||||||
|  | 
 | ||||||
|   /// The SHA-384 hash algorithm. |   /// The SHA-384 hash algorithm. | ||||||
|   sha384, |   sha384, | ||||||
|  | 
 | ||||||
|   /// The SHA-512 hash algorithm. |   /// The SHA-512 hash algorithm. | ||||||
|   sha512, |   sha512, | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // Copyright 2019-2023 Hugo Pointcheval | // Copyright 2019-2023 Hugo Pointcheval | ||||||
| //  | // | ||||||
| // Use of this source code is governed by an MIT-style | // Use of this source code is governed by an MIT-style | ||||||
| // license that can be found in the LICENSE file or at | // license that can be found in the LICENSE file or at | ||||||
| // https://opensource.org/licenses/MIT. | // https://opensource.org/licenses/MIT. | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // Copyright 2019-2023 Hugo Pointcheval | // Copyright 2019-2023 Hugo Pointcheval | ||||||
| //  | // | ||||||
| // Use of this source code is governed by an MIT-style | // Use of this source code is governed by an MIT-style | ||||||
| // license that can be found in the LICENSE file or at | // license that can be found in the LICENSE file or at | ||||||
| // https://opensource.org/licenses/MIT. | // https://opensource.org/licenses/MIT. | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ import 'package:native_crypto/src/domain/byte_array.dart'; | |||||||
| /// [BaseKey] is a [ByteArray] that can be used to store keys. | /// [BaseKey] is a [ByteArray] that can be used to store keys. | ||||||
| /// | /// | ||||||
| /// This interface is implemented by all the key classes. | /// This interface is implemented by all the key classes. | ||||||
| ///  | /// | ||||||
| /// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with | /// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with | ||||||
| /// the Key class from Flutter. | /// the Key class from Flutter. | ||||||
| /// {@endtemplate} | /// {@endtemplate} | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import 'package:native_crypto/src/domain/cipher_chunk.dart'; | |||||||
| abstract class Cipher<T extends CipherChunk> { | abstract class Cipher<T extends CipherChunk> { | ||||||
|   /// {@macro cipher} |   /// {@macro cipher} | ||||||
|   const Cipher(); |   const Cipher(); | ||||||
|  | 
 | ||||||
|   /// Encrypts a [Uint8List] and returns a [CipherText]. |   /// Encrypts a [Uint8List] and returns a [CipherText]. | ||||||
|   Future<CipherText<T>> encrypt(Uint8List plainText); |   Future<CipherText<T>> encrypt(Uint8List plainText); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // Copyright 2019-2023 Hugo Pointcheval | // Copyright 2019-2023 Hugo Pointcheval | ||||||
| //  | // | ||||||
| // Use of this source code is governed by an MIT-style | // Use of this source code is governed by an MIT-style | ||||||
| // license that can be found in the LICENSE file or at | // license that can be found in the LICENSE file or at | ||||||
| // https://opensource.org/licenses/MIT. | // https://opensource.org/licenses/MIT. | ||||||
|  | |||||||
| @ -215,13 +215,15 @@ class MockNativeCryptoAPI implements NativeCryptoAPI { | |||||||
|     HashAlgorithm argAlgorithm, |     HashAlgorithm argAlgorithm, | ||||||
|   ) { |   ) { | ||||||
|     if (pbkdf2Fn != null) { |     if (pbkdf2Fn != null) { | ||||||
|       return Future.value(pbkdf2Fn!( |       return Future.value( | ||||||
|         argPassword, |         pbkdf2Fn!( | ||||||
|         argSalt, |           argPassword, | ||||||
|         argIterations, |           argSalt, | ||||||
|         argLength, |           argIterations, | ||||||
|         argAlgorithm.toString(), |           argLength, | ||||||
|       ),); |           argAlgorithm.toString(), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|     } else { |     } else { | ||||||
|       return Future.value(Uint8List.fromList([1, 2, 3])); |       return Future.value(Uint8List.fromList([1, 2, 3])); | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user