X25519 calculated shared key is different in two programs - dart

I have written two programs which will exchange X25519 public keys and then calculate shared secret. This is just a test, so public keys are exchanged trough text files (in PEM format), program is until you tell it to continue in order to generate keypairs and exchange them.
First one is in Dart:
import 'dart:convert';
import 'dart:io';
import 'package:cryptography/cryptography.dart';
import 'package:pem/pem.dart';
Future<void> main(List<String> arguments) async {
//https://pub.dev/documentation/cryptography/latest/cryptography/X25519-class.html
final algorithm = Cryptography.instance.x25519();
final keyPair = await algorithm.newKeyPair();
var file = File('/home/razj/dartPublic.txt');
file.writeAsStringSync(PemCodec(PemLabel.publicKey)
.encode(await keyPair.extractPublicKeyBytes()));
print('PRESS AFTER C# PROGRAM GENERATES KEYPAIR');
stdin.readLineSync();
String remotePem = File('/home/razj/sharpPublic.txt').readAsStringSync();
SimplePublicKey remotePublicKey = SimplePublicKey(
PemCodec(PemLabel.publicKey).decode(remotePem),
type: KeyPairType.x25519);
final sharedSecretKey = await algorithm.sharedSecretKey(
keyPair: keyPair,
remotePublicKey: remotePublicKey,
);
List<int> sharedKeyBytes = await sharedSecretKey.extractBytes();
print(base64.encode(sharedKeyBytes));
}
extension SimpleKeyPairExtension on SimpleKeyPair {
Future<List<int>> extractPublicKeyBytes() {
return extract().then((value) => value.bytes);
}
}
Second is in .NET CORE:
using System.Security.Cryptography;
using X25519;
internal class Program
{
private static void Main(string[] args)
{
//https://github.com/HirbodBehnam/X25519-CSharp
var keyPair = X25519KeyAgreement.GenerateKeyPair();
File.WriteAllText("/home/razj/sharpPublic.txt", new string(PemEncoding.Write("PUBLIC KEY", keyPair.PublicKey)));
Console.WriteLine("PRESS AFTER DART PROGRAM GENERATES KEYPAIR");
Console.ReadKey(true);
string remotePem = File.ReadAllText("/home/razj/dartPublic.txt").Replace("-----BEGIN PUBLIC KEY-----\n", "").Replace("\n-----END PUBLIC KEY-----\n", "");
byte[] sharedKeyBytes = X25519KeyAgreement.Agreement(keyPair.PrivateKey, Convert.FromBase64String(remotePem));
Console.WriteLine(Convert.ToBase64String(sharedKeyBytes));
}
}
In the end of each program I print base64 encoded byte array which represents the shared key. Unfortunately outputs doesn't match. Any idea how to fix this or what might be wrong? Thanks for answering.

As #Topaco said,
However, you simply apply the raw 32 bytes key instead of the DER
encoded key, so the PEM key is invalid. This has no consequence, since
the reverse direction returns the raw keys that both libraries use in
the end. It is unnecessary though, just apply the (Base64 encoded) raw
keys directly.
Just exchanged keys by encoding raw key bytes to Base64 string:
Dart:
import 'dart:io';
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
Future<void> main(List<String> arguments) async {
//https://pub.dev/documentation/cryptography/latest/cryptography/X25519-class.html
final algorithm = Cryptography.instance.x25519();
final keyPair = await algorithm.newKeyPair();
var file = File('/home/razj/dartPublic.txt');
SimplePublicKey publicKey = await keyPair.extractPublicKey();
//saving key bytes as base64 string
file.writeAsStringSync(base64.encode(publicKey.bytes));
print('PRESS AFTER C# PROGRAM GENERATES KEYPAIR');
stdin.readLineSync();
String remoteKeyBase64 =
File('/home/razj/sharpPublic.txt').readAsStringSync();
SimplePublicKey remotePublicKey =
SimplePublicKey(base64.decode(remoteKeyBase64), type: KeyPairType.x25519);
final sharedSecretKey = await algorithm.sharedSecretKey(
keyPair: keyPair,
remotePublicKey: remotePublicKey,
);
List<int> sharedKeyBytes = await sharedSecretKey.extractBytes();
print(base64.encode(sharedKeyBytes));
}
.NET CORE
using X25519;
internal class Program
{
private static void Main(string[] args)
{
//https://github.com/HirbodBehnam/X25519-CSharp
var keyPair = X25519KeyAgreement.GenerateKeyPair();
//saving key bytes as base64 string
File.WriteAllText("/home/razj/sharpPublic.txt", Convert.ToBase64String(keyPair.PublicKey));
Console.WriteLine("PRESS AFTER DART PROGRAM GENERATES KEYPAIR");
Console.ReadKey(true);
string remoteKeyBase64 = File.ReadAllText("/home/razj/dartPublic.txt");
byte[] sharedKeyBytes = X25519KeyAgreement.Agreement(keyPair.PrivateKey, Convert.FromBase64String(remoteKeyBase64));
Console.WriteLine(Convert.ToBase64String(sharedKeyBytes));
}
}
Dart output:
Hz2Rf2nUFbwmg4wgaXOl3qAi8ha5h61fHcMOXpNQ23o=
.NET-CORE output
Hz2Rf2nUFbwmg4wgaXOl3qAi8ha5h61fHcMOXpNQ23o=

Related

Signing and Verification failures for Dart Ethereum Library

I am coding the logic to sign & verify a string in Dart.
I am importing the following libraries
import 'package:web3dart/web3dart.dart';
import 'package:eth_sig_util/eth_sig_util.dart';
import 'package:collection/collection.dart';
import 'package:web3dart/crypto.dart';
I generate a KeyPair using the following code. I leverage the 'package:web3dart/web3dart.dart'; library for it.
var random_number = Random.secure();
EthPrivateKey keyPair = EthPrivateKey.createRandom(random_number);
I ran the signing and verifying multiple times in a loop.
When I run over 100 times, I get 1-2 verifications which fail. And this error rate is proportional when I run it over 1000 times too. Roughly 1-2% verification failures. This is my signing logic
Future<String> sign (final String message) async
{
List<int> message_list = message.codeUnits;
final Uint8List bytes_message = Uint8List
.fromList(message_list);
String signature = EthSigUtil.signPersonalMessage
(
privateKey: bytesToHex(keyPair.privateKey),
message: bytes_message
);
return signature;
}
And the code to verify is given below :
Future<bool> verify (final String message, final String signature, String public_key) async
{
String recovered_address = '';
List<int> message_list = message.codeUnits;
final Uint8List bytes_message = Uint8List
.fromList(message_list);
try {
recovered_address = EthSigUtil.recoverPersonalSignature
(
signature: signature,
message: bytes_message
);
}
catch (e)
{
return false;
}
var given_address = publicKeyToAddress(hexToBytes(public_key));
bool is_verified = const ListEquality()
.equals(
hexToBytes(recovered_address),
given_address
);
return is_verified;
}
Am I doing something wrong?

How to get ByteData from a File

I want to convert a File to a ByteData object in flutter.
Something like this:
import 'dart:io';
File file = getSomeCorrectFile(); //This file is correct
ByteData bytes = ByteData(file.readAsBytesSync()); //Doesnt compile
return bytes;
I understood that ByteData constructor receives the length of the amount of bytes and initialize them with 0, so I could do something like ByteData(file.readAsBytesStync().length); but then how do I fill them?
What am I missing?
In Dart 2.5.0 or later, I believe that the following should work:
import 'dart:io';
import 'dart:typed_data';
...
File file = getSomeCorrectFile();
Uint8List bytes = file.readAsBytesSync();
return ByteData.view(bytes.buffer);
(Prior to Dart 2.5.0, the file.readAsBytesSync() line should be:
Uint8List bytes = file.readAsBytesSync() as Uint8List;
File.readAsBytes/File.readAsBytesSync used to be declared to return a List<int>, but the returned object was actually a Uint8List subtype.)
Once you have the bytes as a Uint8List, you can extract its ByteBuffer and construct a ByteData from that.
in Dart 2.9:
import 'dart:io';
import 'dart:typed_data';
final file = getSomeCorrectFile(); // File
final bytes = await file.readAsBytes(); // Uint8List
final byteData = bytes.buffer.asByteData(); // ByteData
return byteData;
this is worked for me ...
Uint8List uint8list = Uint8List.fromList(File(path).readAsBytesSync())
Try this:
File file = getSomeCorrectFile();
ByteData bytes = await file.readAsBytes().then((data) => ByteData.view(data as ByteBuffer));
return bytes;
here is my class to handle this:
import 'dart:io';
import 'dart:typed_data';
class FileHandler {
final String _filePath;
FileHandler(this._filePath);
Future<Uint8List> _readToBytes() async {
var file = File.fromUri(Uri.parse(_filePath));
return await file.readAsBytes();
}
Future<Map<String, dynamic>> get data async {
var byte = await _readToBytes();
var ext = _filePath.split('.').last;
return {'byte': byte, 'extension': ext};
}
}
here is my class to handle this:
import 'dart:io';
import 'dart:typed_data';
class FileHandler {
final String _filePath;
FileHandler(this._filePath);
Future<Uint8List> _readToBytes() async {
var file = File.fromUri(Uri.parse(_filePath));
return await file.readAsBytes();
}
Future<Map<String, dynamic>> get data async {
var byte = await _readToBytes();
var ext = _filePath.split('.').last;
return {'byte': byte, 'extension': ext};
}
}

Make a http request in dart whith dart:io

Hey I'm a beginner and I want to interact with an API with dart:io for fetch JSON files I can fetch the data with this code :
final HttpClient client = HttpClient();
client.getUrl(Uri.parse("https://api.themoviedb.org/3/movie/76341?api_key=fbe54362add6e62e0e959f0e7662d64e&language=fr"))
.then((HttpClientRequest request) {
return request.close();
})
.then((HttpClientResponse response) {
Map a;
print(a);
But I want to have a Map whith the JSON but I can't do it. If I could get a String that contains the JSON I could do it with
json.decode();
also know that the answer is stored in an int list that represents the utf8 values of the characters so with utf8.decode(responce.toList()) I can get the utf8 value but responce.toList() return a Future but even if it may be easy I don't know how to get the list.
import 'dart:convert';
import 'dart:io';
void main() async {
final client = HttpClient();
final request = await client.getUrl(Uri.parse(
'https://api.themoviedb.org/3/movie/76341?api_key=fbe54362add6e62e0e959f0e7662d64e&language=fr'));
final response = await request.close();
final contentAsString = await utf8.decodeStream(response);
final map = json.decode(contentAsString);
print(map);
}

How to write a `ByteData` instance to a File in Dart?

I am using Flutter to load an "asset" into a File so that a native application can access it.
This is how I load the asset:
final dbBytes = await rootBundle.load('assets/file');
This returns an instance of ByteData.
How can I write this to a dart.io.File instance?
ByteData is an abstraction for:
A fixed-length, random-access sequence of bytes that also provides
random and unaligned access to the fixed-width integers and floating
point numbers represented by those bytes.
As Gunter mentioned in the comments, you can use File.writeAsBytes. It does require a bit of API work to get from ByteData to a List<int>, however.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
I've also filed an issue to make the docs on Flutter more clear for this use case.
you need to have path_provider package installed, then
This should work :
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
final dbBytes = await rootBundle.load('assets/file'); // <= your ByteData
//=======================
Future<File> writeToFile(ByteData data) async {
final buffer = data.buffer;
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var filePath = tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything
return new File(filePath).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
//======================
to get your file :
var file;
try {
file = await writeToFile(dbBytes); // <= returns File
} catch(e) {
// catch errors here
}
Hope this helps,
Thank you.
to search flutter ByteData to List<int> then found here, but not fully answer my question:
how to convert ByteData to List<int> ?
after self investigate, solution is:
use .cast<int>()
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.cast<int>();
or 2. use .map
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.map((eachUint8) => eachUint8.toInt()).toList();
For those looking to write bytes (aka Uint8List) instead of ByteData please note that ByteData is a wrapper for Uint8List.
From /runtime/lib/typed_data.patch:
#patch
class ByteData implements TypedData {
#patch
#pragma("vm:entry-point")
factory ByteData(int length) {
final list = new Uint8List(length) as _TypedList;
_rangeCheck(list.lengthInBytes, 0, length);
return new _ByteDataView(list, 0, length);
}
#patch
class Uint8List {
#patch
#pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
}
If you are using the latter type you can use the answer provided by Rami and modify the return as follow:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
Future<File> writeToFile(Uint8List data) async {
(...)
return new File(filePath).writeAsBytes(data);
}

Signing a message with hmac and sha256 in dart

I try to generate a sha256 HMAC using a base64-decoded secret key on a message. I would like to use the dart language. In python, I could do it with the following code:
# PYTHON CODE
import hmac, hashlib, base64
...
message = 'blabla'
secret = 'DfeRt[...]=='
secret_b64 = base64.b64decode(secret)
signature = hmac.new(secret_b64, message, hashlib.sha256)
signature_b64 = signature.digest().encode('base64').rstrip('\n')
Here is what I tried with dart:
// DART CODE
import 'package:crypto/crypto.dart';
import 'dart:convert';
...
String message = 'blabla';
String secret = 'DfeRt[...]=='
var secret_b64 = BASE64.decode(secret);
var hmac = new Hmac(sha256, secret_b64);
// what now?
But then I don't know how to go on. I found some old example code which looks like the following
var message_byte = UTF8.encode(message);
hmac.add(message_byte);
However, the method "add" does not exist any more in the Hmac class. I also tried this, but I am not sure if this is correct
var message_byte = UTF8.encode(message);
var signature = hmac.convert(message_byte);
var signature_b64 = BASE64.encode(signature.bytes);
Can someone help me out?
If you have the whole 'message' available then just call convert(). If the message is large or in pieces then deal with it in chunks.
Your example is simple, when spelled out step by step.
String base64Key = 'DfeRt...';
String message = 'blabla';
List<int> messageBytes = utf8.encode(message);
List<int> key = base64.decode(base64Key);
Hmac hmac = new Hmac(sha256, key);
Digest digest = hmac.convert(messageBytes);
String base64Mac = base64.encode(digest.bytes);
Please read the Effective Dart guide. Note how constants are now lower case, variables in Dart use camel case, etc
I had to sign a request with hmac for calling an API in my recent project. Here is what I had done. Hope this helps you too.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:crypto/crypto.dart';
String getHmacAuthHeader({
#required final String inputUrl,
#required final dynamic inputJsonContent,
#required final String appId,
#required final String appSecrets,
final String method = "POST",
}) {
final url = _encodeUrl(inputUrl);
final seconds =
(DateTime.now().millisecondsSinceEpoch / 1000).round().toString();
final nonce = "N${DateTime.now().millisecondsSinceEpoch}";
final contentHash = _getMd5HashInBase64FromJson(inputJsonContent);
final signature = "$appId$method$url$seconds$nonce$contentHash";
final signatureHmacHashBase64 = _getHmacHashInBase64FromString(appSecrets, signature);
final token = "$appId:$signatureHmacHashBase64:$nonce:$seconds";
return "hmacauth $token";
}
String _encodeUrl(String url) {
if (!url.startsWith("/")) {
url = "/$url";
}
return Uri.encodeComponent(url).toLowerCase();
}
String _getMd5HashInBase64FromJson(dynamic json) {
final jsonString = jsonEncode(json);
final jsonStringBytes = Utf8Encoder().convert(jsonString);
final hashBytes = md5.convert(jsonStringBytes).bytes;
final hashBase64 = base64Encode(hashBytes);
return hashBase64;
}
String _getHmacHashInBase64FromString(String key, String data){
final keyBytes = Utf8Encoder().convert(key);
final dataBytes = Utf8Encoder().convert(data);
final hmacBytes = Hmac(sha256, keyBytes)
.convert(dataBytes)
.bytes;
final hmacBase64 = base64Encode(hmacBytes);
return hmacBase64;
}
Hey I'm late to Answer this question. But I think anyone can use this Answer.
I use https://pub.dev/packages/crypto package
For that you can use
import 'dart:convert';
import 'package:crypto/crypto.dart';
message = 'blabla'
secret = 'DfeRt[...]=='
void main() {
var key = utf8.encode(secret);
var bytes = utf8.encode(message);
var hmacSha256 = Hmac(sha256, key); // HMAC-SHA256
var digest = hmacSha256.convert(bytes);
print("HMAC digest as bytes: ${digest.bytes}");
print("HMAC digest as hex string: $digest");
}
I think this will save codes and clean.

Resources