Public Key of ECC P256K1 in AWS-KMS is 88 bytes? - amazon-kms

I am using AWS KMS with ECC_SECG_P256K1 key. When I retreive the
public key via aws-sdk the key is 88 bytes, which suppoed to be 64 bytes (as shown in the code)
Even more the size of signature is varying between 70,71,72, which
means we can't calculate the (r,s) values based on r=[0:32],s=[32,64]
var kms = new AWS.KMS();
var pubKeyParam = {
KeyId: 'xxxxxxxx', /* required */
};
kms.getPublicKey(pubKeyParam, function(err, data) {
if (err) console.log(err, err.stack);
else
publicKey = data.PublicKey
console.log(publicKey.length) <-- 88 bytes not 64 bytes
});
Thanks in advance for help

KMS Public Key Parsing
KMS is returning the public key in ASN.1 format.
If you convert the key using publicKeyFromAsn1 here, it returns 64 bytes:
import * as asn1js from 'asn1js';
function toArrayBuffer(buffer: Buffer): ArrayBuffer {
const ab = new ArrayBuffer(buffer.length);
const view = new Uint8Array(ab);
for (let i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return ab;
}
// call this with your KMS public key
function publicKeyFromAsn1(buf: Buffer): Buffer {
const { result } = asn1js.fromBER(toArrayBuffer(buf));
const values = (result as asn1js.Sequence).valueBlock.value;
const value = values[1] as asn1js.BitString;
return Buffer.from(value.valueBlock.valueHex.slice(1));
}
KMS Signature Parsing
The KMS Signature is in DER format (which is valid BER). It ends up looking like this: 30440220{r}0220{s} Here is some parsing logic to help you extract r & s.
import * as asn1js from 'asn1js';
function toArrayBuffer(buffer: Buffer): ArrayBuffer {
const ab = new ArrayBuffer(buffer.length);
const view = new Uint8Array(ab);
for (let i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return ab;
}
//call this with your signature buffer
function parseBERSignature(sig: Buffer): { r: Buffer; s: Buffer } {
const { result } = asn1js.fromBER(toArrayBuffer(sig));
const part1 = (result as asn1js.Sequence).valueBlock.value[0] as asn1js.BitString;
const part2 = (result as asn1js.Sequence).valueBlock.value[1] as asn1js.BitString;
return {
r: Buffer.from(part1.valueBlock.valueHex),
s: Buffer.from(part2.valueBlock.valueHex),
};
}

Related

How to Decrypt encrypted text in Flutter/Dart

We have this code on encrypting/decrypting a particular message and I'd like to decrypt the value in Flutter (dart).
/* Encrypt text
* #param text
*/
export const encrypt = (text: string): string => {
const encJson = CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encJson));
};
/* Decrypt text
* #param ciphertext
*/
export const decrypt = (ciphertext: string): string => {
const decData = CryptoJS.enc.Base64.parse(ciphertext).toString(CryptoJS.enc.Utf8);
const bytes = CryptoJS.AES.decrypt(decData, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
};
I have tried the example mentioned in this article but could not make it work.
https://medium.com/#chingsuehok/cryptojs-aes-encryption-decryption-for-flutter-dart-7ca123bd7464
I really appreciate if anyone can help or point me out on what to change on my code.
Current code:
String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
Uint8List encryptedBytes =
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
final salt = encryptedBytesWithSalt.sublist(8, 16);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final decrypted =
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
return decrypted;
} catch (error) {
throw error;
}
}
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
var password = createUint8ListFromString(passphrase);
Uint8List concatenatedHashes = Uint8List(0);
List<int> currentHash = Uint8List(0);
bool enoughBytesForKey = false;
Uint8List preHash = Uint8List(0);
while (!enoughBytesForKey) {
int preHashLength = currentHash.length + password.length + salt.length;
if (currentHash.length > 0)
preHash = Uint8List.fromList(currentHash + password + salt);
else
preHash = Uint8List.fromList(password + salt);
currentHash = md5.convert(preHash).bytes;
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
}
var keyBtyes = concatenatedHashes.sublist(0, 32);
var ivBtyes = concatenatedHashes.sublist(32, 48);
return new Tuple2(keyBtyes, ivBtyes);
}
Uint8List createUint8ListFromString(String s) {
var ret = new Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
Uint8List genRandomWithNonZero(int seedLength) {
final random = Random.secure();
const int randomMax = 245;
final Uint8List uint8list = Uint8List(seedLength);
for (int i = 0; i < seedLength; i++) {
uint8list[i] = random.nextInt(randomMax) + 1;
}
return uint8list;
}
The CryptoJS code unnecessarily Base64 encodes the ciphertext twice during encryption. So the most reasonable solution would be to fix the CryptoJS code.
In encrypt(), encJson is already the Base64 encoded ciphertext, i.e. the body of the encrypt() method should actually be:
return CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
and analogously the body of decrypt():
const bytes = CryptoJS.AES.decrypt(ciphertext, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
With this fix, successful decryption with the unmodified Dart code is possible.
If for some reason the CryptoJS code must not be changed, the Dart code in decryptAESCryptoJS() must also Base64 decode twice:
Uint8List encryptedBytesWithSalt = base64.decode(utf8.decode(base64.decode(encrypted)));
With this fix, the ciphertext of the unmodified CryptoJS code can be successfully decrypted.

ethereum solidity: from concatenated hexadecimal strings to uint64[]

I want my API to return an array of uint64 to my on-chain contract.
I tried 2 response formats for my API:
The array of uint64 itself (BN string here, but I need it in true uint64 not strings in my contract):
{"data":["629343835796877311","629343835797458943","629343835797471231"]}
concatenated hexadecimal strings (a new value every 16 chars):
{"data":"08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff"}
I discarded using the first approach because having ["629343835796877311","629343835797458943","629343835797471231"] as bytes is actually difficult to extract. I might be wrong! Maybe there is a base64 approach to encode and decode the data back into solidity data types, maybe?
I will use the second approach bellow.
Chainlink will pass the response as bytes memory _data:
function fulfill(bytes32 _requestId, bytes memory _data)
public
recordChainlinkFulfillment(_requestId)
{
data = string(_data);
}
Those bytes memory _data are successfully received and converted to a string (in storage data). The string value looks like this
08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff ...
In this example each 16 chars represent a uint64 number.
The first one: 08bbe0e25e412fff is 629343835796877311 for instance.
In solidity, I need to split the string each 16 chars and then convert it into their uint64 value.
I could use the bytes memory _data instead of the string(_data) if the code would be simpler or consume less gas. I am not sure
Please I need help with this I have been struggling.
Thanks
I got this contract working
COMMENTS:
the method hexBytesToInt is going to get a string representing hexa value like ffa0 for instance and return it's decimal value.
the method getSlice is just going to slice a string. In my case I have a new hexa value every 16 chars so I need to slice (0,16) than (16,32) etc...
the method hexStringToIntArray is managing the increments to slice every 16 chars and call the hexBytesToInt to transform the hex string in uint.
If you really want to dig into this solution, you are better off starting by understanding the test cases.
pragma solidity >=0.4.22 <0.8.11;
contract Serializer {
function hexStringToIntArray(string memory s) public pure returns (uint64[] memory) {
uint size = bytes(s).length / 16;
uint64[] memory result = new uint64[](size);
for (uint i = 0; i< size; i++) {
string memory strSlice = getSlice(i*16, (i+1)*16, s);
result[i] = hexStringToInt(strSlice);
}
return result;
}
function getSlice(uint startIndex, uint endIndex, string memory str) public pure returns (string memory) {
bytes memory strBytes = bytes(str);
bytes memory result = new bytes(endIndex-startIndex);
for(uint i = startIndex; i < endIndex; i++) {
result[i-startIndex] = strBytes[i];
}
return string(result);
}
function hexBytesToInt(bytes memory ss) public pure returns (uint64){
uint64 val = 0;
uint8 a = uint8(97); // a
uint8 zero = uint8(48); //0
uint8 nine = uint8(57); //9
uint8 A = uint8(65); //A
uint8 F = uint8(70); //F
uint8 f = uint8(102); //f
for (uint i=0; i<ss.length; ++i) {
uint8 byt = uint8(ss[i]);
if (byt >= zero && byt <= nine) byt = byt - zero;
else if (byt >= a && byt <= f) byt = byt - a + 10;
else if (byt >= A && byt <= F) byt = byt - A + 10;
val = (val << 4) | (byt & 0xF);
}
return val;
}
function hexStringToInt(string memory s) public pure returns (uint64) {
bytes memory ss = bytes(s);
uint64 val = hexBytesToInt(ss);
return val;
}
}
the tests:
const Serializer = artifacts.require("Serializer");
const truffleAssert = require("truffle-assertions");
const fs = require("fs");
const { readLines } = require("./utils.js");
const BN = web3.utils.BN;
contract("Serializer", (accounts) => {
const [deployerAddress, tokenHolderOneAddress, tokenHolderTwoAddress] = accounts;
it("hexStringToInt", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToInt.call("08bbe0e25e412fff");
let expected = new BN("629343835796877311");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a0fff");
expected = new BN("629343835797458943");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a3fff");
expected = new BN("629343835797471231");
assert.equal(result.toString(10), expected.toString(10));
});
it("getSlice1", async () => {
let s = await Serializer.deployed();
let result = await s.getSlice.call(0, 16, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
let expected = "08bbe0e25e412fff";
assert.equal(result, expected);
});
it("getSlice2", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(16, 32, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a0fff";
assert.equal(result, expected);
});
it("getSlice3", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(32, 48, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a3fff";
assert.equal(result, expected);
});
it("hexStringToIntArray", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToIntArray.call("08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
console.log(result);
let expected = [
new BN("629343835796877311").toString(),
new BN("629343835797458943").toString(),
new BN("629343835797471231").toString(),
];
const resultS = result.map((x) => x.toString());
assert.deepEqual(resultS, expected);
});
});

Using RSA to encrypt a message in JS and decrypt in Python

I want to encrypt a message using RSA with a provided PEM public key in Javascript, using SubtleCrypto window.crypto.subtle and then decode it with Python (PyCryptodome) in the back-end. However, I get a ValueError: Incorrect decryption.. I'm not sure if the data is being correctly handled though. Here is my code:
JavaScript:
var publicKey;
var pemPublicKey = `public.pem key with stripped header and footer and newlines (just the base64 data)`;
function base64ToArrayBuffer(b64) {
var byteString = window.atob(b64);
var byteArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) { byteArray[i] = byteString.charCodeAt(i); }
return byteArray;
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); }
return window.btoa(binary);
}
window.crypto.subtle.importKey(
"spki",
base64ToArrayBuffer(pemPublicKey),
{ name: "RSA-OAEP", hash: { name: "SHA-256" } },
false,
["encrypt"])
.then(function (key) {
publicKey = key
})
console.log(publicKey)
var enc = new TextEncoder()
var encmessage = enc.encode("test14")
var encryptedData;
window.crypto.subtle.encrypt({
name: "RSA-OAEP"
}, publicKey, encmessage).then(function (encrypted) { encryptedData = encrypted })
var encodedData = arrayBufferToBase64(encryptedData);
console.log(encodedData)
What the code above does is convert the public PEM key, generate a CryptoKey object out of it (using crypto.subtle.importKey) and then encrypts a simple message "test14".
Python backend:
import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import AES, PKCS1_OAEP
with open('private.pem', 'r') as f: keypair = RSA.import_key(f.read())
decryptor = PKCS1_OAEP.new(keypair)
decrypted = decryptor.decrypt(base64.b64decode(encrypted)) # encrypted is the data that is returned by JavaScript code
print(decrypted)
Directly from the documentation of Crypto.Cipher.PKCS1_OAEP.new(key, hashAlgo=None, mgfunc=None, label='', randfunc=None):
...
hashAlgo (hash object) - The hash function to use. This can be a module under Crypto.Hash or an existing hash object created from any of such modules. If not specified, Crypto.Hash.SHA1 is used.
...

How can I handle raw bytes on dart?

I'm totally new at app developing.
I trying to communicate with C-written end device via raw UDP packet. (such a modbus-like protocol)
And I'm suffering pain with serial/deserializing class(struct).
Here is a simple class, Packet which contain uint32, uint16, uint8.
Pack() is working, but are there any better way to achieve that?
I don't know how can I implement Unpack() method. I mean, how can I convert Uint8List to int?
import 'dart:typed_data';
void main() {
var pkt = new Packet();
pkt.TID = 0x01234567;
pkt.Src = 0x89;
pkt.Des = 0xab;
pkt.Data = 0xcdef;
var buf = pkt.Pack();
print('Packed: ${buf}');
var pkt_2 = new Packet();
if (pkt_2.Unpack(buf) != null) {
print("panic!");
return;
}
print('UnPacked: ${pkt_2.toString()}');
}
class Packet {
int TID; // uint32
int Src; // uint8
int Des; // uint8
int Data; // uint16
static const SIZE = 8;
String toString() {
return 'TID: ${TID.toRadixString(16)}, Src:${Src.toRadixString(16)}, Des:${Des.toRadixString(16)}, Data:${Data.toRadixString(16)}';
}
Uint8List Pack() {
var buf = Byteconv.itou32(TID) +
Byteconv.itou8(Src) +
Byteconv.itou8(Des) +
Byteconv.itou16(Data);
return Uint8List.fromList(buf);
}
Error Unpack(Uint8List buf) {
if (buf.length != SIZE) {
return Error();
}
// What can I do?
// TID =
// Src =
// Des =
// Data =
return null;
}
}
class Byteconv {
static Uint8List itou64(int val) {
return Uint8List(8)..buffer.asByteData().setUint64(0, val, Endian.big);
}
static Uint8List itou32(int val) {
return Uint8List(4)..buffer.asByteData().setUint32(0, val, Endian.big);
}
static Uint8List itou16(int val) {
return Uint8List(2)..buffer.asByteData().setUint16(0, val, Endian.big);
}
static Uint8List itou8(int u8) {
return Uint8List(1)..buffer.asUint8List()[0] = u8;
}
}
SOLVED
var buf_view = ByteData.sublistView(buf);
TID = buf_view.getUint32(0);
Src = buf_view.getUint8(4);
Des = buf_view.getUint8(5);
Data = buf_view.getUint16(6);

OWIN password hash manual comparison from JavaScript

I need to use an existing ASP.NET OWIN database with Auth0. A problem that I've encountered is that I need to write a function in JavaScript (for the Auth0 Action Scripts) that takes a plaintext password, hashes it and compares that hash to an existing hash from the OWIN database. The hashes are created using ASP.NET Identity UserManager class.
How can I replicate the algorithm that UserManager uses in JavaScript?
Thanks in advance,
Arthur.
Found the answer. You can use this C# code snippet that from node using edge to has the password like UserManager does it in ASP.NET Identity.
var edge = require('edge');
var verifyHash = edge.func(function() {/*
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Security.Principal;
internal static class Crypto
{
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
private const int PBKDF2SubkeyLength = 256/8; // 256 bits
private const int SaltSize = 128/8; // 128 bits
public static string HashPassword(string password)
{
if (password == null)
{
throw new ArgumentNullException("password");
}
// Produce a version 0 (see comment above) text hash.
byte[] salt;
byte[] subkey;
using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
{
salt = deriveBytes.Salt;
subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}
var outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
return Convert.ToBase64String(outputBytes);
}
// hashedPassword must be of the format of HashWithPassword (salt + Hash(salt+input)
public static bool VerifyHashedPassword(string hashedPassword, string password)
{
if (hashedPassword == null)
{
return false;
}
if (password == null)
{
throw new ArgumentNullException("password");
}
var hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
// Verify a version 0 (see comment above) text hash.
if (hashedPasswordBytes.Length != (1 + SaltSize + PBKDF2SubkeyLength) || hashedPasswordBytes[0] != 0x00)
{
// Wrong length or version header.
return false;
}
var salt = new byte[SaltSize];
Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, SaltSize);
var storedSubkey = new byte[PBKDF2SubkeyLength];
Buffer.BlockCopy(hashedPasswordBytes, 1 + SaltSize, storedSubkey, 0, PBKDF2SubkeyLength);
byte[] generatedSubkey;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, PBKDF2IterCount))
{
generatedSubkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}
return ByteArraysEqual(storedSubkey, generatedSubkey);
}
// Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
var areSame = true;
for (var i = 0; i < a.Length; i++)
{
areSame &= (a[i] == b[i]);
}
return areSame;
}
}
class Startup
{
public async Task<object> Invoke(dynamic input)
{
return await Task<object>.Run(() => {
return Task.FromResult<object>(Crypto.VerifyHashedPassword (input.hashedPassword, input.providedPassword));
});
}
}
*/});
verifyHash({ hashedPassword: 'AONmKUXiPKhN1J+t8DwCp2uTq3TBcJNabcc4HuQkx+uwO+5+yRJZreJ9jZyxyGzhzg==', providedPassword: 'abc' }, function (error, result) {
if (error) {
console.log(error);
}
else {
if (result) {
// Password matches!
}
}
});