Unable to add vector to FlatBuffer table (Javascript) - javascript

I am following the FlatBuffers Javascript tutorial but I am having trouble adding a vector of non-scalar items to the following object:
namespace test;
enum Availability : byte {
Unavailable = 0,
Available = 1
}
table Channel {
channelNumber:uint;
myState:Availability = Unavailable;
}
table ControlChannel {
channels:[Channel];
}
root_type ControlChannel;
As you can see the root object contains a vector of Channel objects. I successfully generated my javascript code, but while trying to create some test data it seems that the channels aren't added correctly. This is what I tried:
const fs = require('fs');
const flatbuffers = require('./flatbuffers').flatbuffers;
const test = require('./control-channel_generated').test;
// Create builder. The '1' is the 'initial size', whatever that means.
const builder = new flatbuffers.Builder(1);
// Create the first channel
test.Channel.startChannel(builder);
test.Channel.addChannelNumber(builder, 1);
const channel1 = test.Channel.endChannel(builder);
// Create another channel
test.Channel.startChannel(builder);
test.Channel.addChannelNumber(builder, 2);
test.Channel.addMyState(builder, test.Availability.Available);
const channel2 = test.Channel.endChannel(builder);
// Create a vector of the channels
const chans = [channel1, channel2];
const channels = test.ControlChannel.createChannelsVector(builder, chans);
// Create control channel and add the channels
test.ControlChannel.startControlChannel(builder);
test.ControlChannel.addChannels(builder, channels); // The resulting cc-test.data file is the same whether I comment this line or not
const controlChannel = test.ControlChannel.endControlChannel(builder);
// Control channel is finished
builder.finish(controlChannel);
// Create a buffer (to send it, write it etc)
const buf = builder.asUint8Array();
fs.writeFileSync('cc-test.data', buf);
console.log(`Data written to 'cc-test.data'.`);
This results in a file called cc-test.data which contains the buffer and no matter how many channels I try to add, the buffer is always exactly the same. I also tried parsing the data back like this:
const fs = require('fs');
const flatbuffers = require('./flatbuffers').flatbuffers;
const test = require('./control-channel_generated').test;
// Parse the data as a new byte array
const data = new Uint8Array(fs.readFileSync('./cc-test.data'));
const buf = new flatbuffers.ByteBuffer(data);
// This is where all the magic happens
const controlChannel = test.ControlChannel.getRootAsControlChannel(buf);
// You can not iterate over the channels directly, you have to get it by index
console.log(`ControlChannel has ${controlChannel.channelsLength()} channels:`);
for (var i = 0; i < controlChannel.channelsLength(); i++) {
const channel = controlChannel.channels(i);
console.log(`Channel ${channel.channelNumber()} (Available: ${channel.myState()})`);
}
This just prints ControlChannel has 0 channels every time. What am I missing?

Related

Unable to remove data from json file on disk

I'm unable to find a way to remove whole line of JSON data(line) after it's used.
For some reason delete is not working or rather said not doing anything.
.JSON
[
{"id":"1","code":"super-S","expires_in":"","gives_currencies":"1.5","gives_items":"","number_of_attempts":"1","attempts_used":""},
{"id":"2","code":"wow!","expires_in":"","gives_currencies":"3","gives_items":"","number_of_attempts":"1","attempts_used":""},
{"id":"3","code":"slashme","expires_in":"","gives_currencies":"4","gives_items":"","number_of_attempts":"1","attempts_used":""},
{"id":"4","code":"randombla","expires_in":"","gives_currencies":"5","gives_items":"","number_of_attempts":"1","attempts_used":""}
]
code
//fs configuration
const fs = require('fs');
let rawdata = fs.readFileSync('test.json');
let mycodes = JSON.parse(rawdata);
//something above
const randomcode = mycodes[Math.floor(Math.random() * mycodes.length)];
console.log('Your code is:', randomcode['code']); //logs me a random code value
delete mycodes[randomcode];
The goal here is to select random code, which is done but then I need to remove it from .JSON file so it won't repeat. I tried several things but it's not working, delete.randomcode etc... the line never removed from the .JSON file.
Use Array.prototype.splice(index, deleteCount) instead of delete.
delete, on an Array, will just null the key, without removing it.
Save back your modified data using JSON.stringify(mycodes) to that same file.
const fs = require('fs');
const mycodes = JSON.parse(fs.readFileSync('./test.json'));
const randomIndex = Math.floor(Math.random() * mycodes.length);
const randomObject = mycodes[randomIndex];
console.log('Your code is:', randomObject.code); // Log a random code value
mycodes.splice(randomIndex, 1); // Remove one key at randomIndex
// Write back to file
fs.writeFileSync('test.json', JSON.stringify(mycodes, 0, 4), 'utf8');
If you already have that Object out of your Array, and since Objects are passed by reference (like pointer in memory), make use of the Array.prototype.indexOf(someObject) like:
const fs = require('fs');
const mycodes = JSON.parse(fs.readFileSync('./test.json'));
const randomIndex = Math.floor(Math.random() * mycodes.length);
const randomObject = mycodes[randomIndex];
// later in your code....
const objIndex = mycodes.indexOf(randomObject); // Get Object index in Array
mycodes.splice(objIndex, 1); // Remove it from array at that index
// Write back to file
fs.writeFileSync('test.json', JSON.stringify(mycodes, 0, 4), 'utf8');
You need to persist your data by writing it back to your JSON file after using JSON.stringify().
While you're at it, you can move your code into functions, which will make it easier to read and work with.
You might also want to read about editing arrays using Array.prototype.splice().
The delete operator is for deleting properties from objects. While you can use it to delete elements from an array, it will leave the index empty rather than closing the gap in the array after deletion.
const fs = require('fs');
// get a random element from any array
function getRandomElement (array) {
const randomElement = array[Math.floor(Math.random() * array.length)];
return randomElement;
}
function deleteElementFromArray (array, element) {
const index = array.indexOf(element);
if (index < 0) return false;
array.splice(index, 1);
return true;
}
// move the reading work inside a function
function readJson (filePath) {
const json = fs.readFileSync(filePath, {encoding: 'utf8'});
const data = JSON.parse(json);
return data;
}
// move the reading work inside a function
function writeJson (filePath, data) {
const json = JSON.stringify(data);
fs.writeFileSync(filePath, json);
}
const jsonFilePath = 'test.json';
const mycodes = readJson(jsonFilePath);
const randomcode = getRandomElement(mycodes);
console.log('Your code is:', randomcode['code']);
deleteElementFromArray(mycodes, randomcode);
writeJson(jsonFilePath, mycodes);

Sign PDF in a modern browser natively?

What I'm trying to achieve
Sign a PDF in the browser using cliets certificate store or Smart Card
What I did so far
For accessing the local cert store I use FortifyApp.
Pdf is pre-signed on the server using iText(Sharp), then sent to the client via Ajax.
Relevant code:
using (var fileStream = new MemoryStream())
{
using (var stamper = PdfStamper.CreateSignature(reader, fileStream, '0', null, true))
{
var signatureAppearance = stamper.SignatureAppearance;
signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle(15,15,15,15), 1, "A");
IExternalSignatureContainer external =
new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
signatureAppearance.Reason = "AsdAsd";
signatureAppearance.Layer2Text = "Asd";
signatureAppearance.SignatureRenderingMode =
iTextSharp.text.pdf.PdfSignatureAppearance.RenderingMode.DESCRIPTION;
MakeSignature.SignExternalContainer(signatureAppearance, external, 512);
return fileStream.ToArray();
}
}
Following this, I managed to manipulate the pdf, extract byteRange, insert signature, etc. Relevant code:
let pdfBuffer = Buffer.from(new Uint8Array(pdf));
const byteRangeString = `/ByteRange `;
const byteRangePos = pdfBuffer.indexOf(byteRangeString);
if (byteRangePos === -1)
throw new Error('asd');
let len = pdfBuffer.slice(byteRangePos).indexOf(`]`) + 1;
// Calculate the actual ByteRange that needs to replace the placeholder.
const byteRangeEnd = byteRangePos + len;
const contentsTagPos = pdfBuffer.indexOf('/Contents ', byteRangeEnd);
const placeholderPos = pdfBuffer.indexOf('<', contentsTagPos);
const placeholderEnd = pdfBuffer.indexOf('>', placeholderPos);
const placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos;
const placeholderLength = placeholderLengthWithBrackets - 2;
const byteRange = [0, 0, 0, 0];
byteRange[1] = placeholderPos;
byteRange[2] = byteRange[1] + placeholderLengthWithBrackets;
byteRange[3] = pdfBuffer.length - byteRange[2];
let actualByteRange = `/ByteRange [${byteRange.join(' ')}]`;
actualByteRange += ' '.repeat(len - actualByteRange.length);
// Replace the /ByteRange placeholder with the actual ByteRange
pdfBuffer = Buffer.concat([pdfBuffer.slice(0, byteRangePos) as any, Buffer.from(actualByteRange), pdfBuffer.slice(byteRangeEnd)]);
// Remove the placeholder signature
pdfBuffer = Buffer.concat([pdfBuffer.slice(0, byteRange[1]) as any, pdfBuffer.slice(byteRange[2], byteRange[2] + byteRange[3])]);
and
//stringSignature comes from the signature creations below, and is 'hex' encoded
// Pad the signature with zeroes so the it is the same length as the placeholder
stringSignature += Buffer
.from(String.fromCharCode(0).repeat((placeholderLength / 2) - len))
.toString('hex');
// Place it in the document.
pdfBuffer = Buffer.concat([
pdfBuffer.slice(0, byteRange[1]) as any,
Buffer.from(`<${stringSignature}>`),
pdfBuffer.slice(byteRange[1])
]);
The problem
This uses forge, and an uploaded p12 file. - This would probably work, if I could translate the imported(?) privateKey from Fortify (which is === typeof CryptoKey, and forge throws an error: TypeError: signer.key.sign is not a function).
p7.addCertificate(certificate); //certificate is the Certificate from Fortify CertificateStore.getItem(certId)
p7.addSigner({
key: privateKey, //this is the CryptoKey from Fortify
certificate: null/*certificate*/, //also tried certificate from Fortify
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [
{
type: forge.pki.oids.contentType,
value: forge.pki.oids.data,
}, {
type: forge.pki.oids.messageDigest,
// value will be auto-populated at signing time
}, {
type: forge.pki.oids.signingTime,
// value can also be auto-populated at signing time
// We may also support passing this as an option to sign().
// Would be useful to match the creation time of the document for example.
value: new Date(),
},
],
});
// Sign in detached mode.
p7.sign({detached: true});
I also tried pkijs for creating the signature (throws a similar error: Signing error: TypeError: Failed to execute 'sign' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'.)
let cmsSigned = new pki.SignedData({
encapContentInfo: new pki.EncapsulatedContentInfo({
eContentType: "1.2.840.113549.1.7.1", // "data" content type
eContent: new asn.OctetString({ valueHex: pdfBuffer })
}),
signerInfos: [
new pki.SignerInfo({
sid: new pki.IssuerAndSerialNumber({
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
})
})
],
certificates: [certificate]
});
let signature = await cmsSigned.sign(privateKey, 0, 'SHA-256');
What "works" is, if I create the signature using the code below:
let signature = await provider.subtle.sign(alg, privateKey, new Uint8Array(pdfBuffer).buffer);
"works", because it creates an invalid signature:
Error during signature verification.
ASN.1 parsing error:
Error encountered while BER decoding:
I tried multiple certificates, no luck.
Questions
Can I achieve my goal without having to manually upload a p12/pfx file, is it even possible?
Is the server-side implementation of the deferred signature correct, do I need something else?
Is the pdf manipulation in javascript correct?
Can I transform the native CrytpoKey to forge or pkijs?
What is wrong with the last signature? At first glance it seems right (at least the format):
<>>>/ContactInfo()/M(D:20200619143454+02'00')/Filter/Adobe.PPKLite/SubFilter/adbe.pkcs7.detached/ByteRange [0 180165 181191 1492] /Contents <72eb2731c9de4a5ccc94f1e1f2d9b07be0c6eed8144cb73f3dfe2764595dcc8f58b8a55f5026618fd9c79146ea93afdafc00b617c6e70de553600e4520f290bef70c499ea91862bb3acc651b6a7b162c984987f05ec59db5b032af0127a1224cad82e3be38ae74dd110ef5f870f0a0a92a8fba295009f267508c372db680b3d89d3157d3b218f33e7bf30c500d599b977c956e6a6e4b02a0bbd4a86737378b421ae2af0a4a3c03584eaf076c1cdb56d372617da06729ef364605ecd98b6b32d3bb792b4541887b59b686b41db3fc32eb4c651060bb02e2babeb30e6545834b2935993f6ee9edcc8f99fee8ad6edd2958c780177df6071fdc75208f76bbbcc21a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>>>
Thanks:
F
Original answer:
So I figured it out.
Can I achieve my goal without having to manually upload a p12/pfx
file, is it even possible?
Yes, it is. (See below on what needs to be changed.)
Is the server-side implementation of the deferred signature correct, do I need something else?
Yes, the code above is fine.
Is the pdf manipulation in javascript correct?
Also fine.
Can I transform the native CrytpoKey to forge or pkijs?
Yes, see below.
What is wrong with the last signature?
#mkl answered it in a comment, thank you.
FortifyApp has a CMS demo now. Although it didn't work with the version I was using, it works with version 1.3.4.
So I went with the pki.js implementation. The code changes need for the signing to be successful are the following:
Export the certificate:
const cryptoCert = await provider.certStorage.getItem(selectedCertificateId);
const certRawData = await provider.certStorage.exportCert('raw', cryptoCert);
const pkiCert = new pki.Certificate({
schema: asn.fromBER(certRawData).result,
});
return pkiCert;
Sign in detached mode
let cmsSigned = new pki.SignedData({
version: 1,
encapContentInfo: new pki.EncapsulatedContentInfo({
eContentType: '1.2.840.113549.1.7.1',
}),
signerInfos: [
new pki.SignerInfo({
version: 1,
sid: new pki.IssuerAndSerialNumber({
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
})
})
],
certificates: [certificate]
});
let signature = await cmsSigned.sign(privateKey, 0, 'SHA-256', pdfBuffer);
const cms = new pki.ContentInfo({
contentType: '1.2.840.113549.1.7.2',
content: cmsSigned.toSchema(true),
});
const result = cms.toSchema().toBER(false);
return result;
Convert signature to 'HEX' string
let stringSignature = Array.prototype.map.call(new Uint8Array(signature), x => (`00${x.toString(16)}`).slice(-2)).join('');
let len = signature.byteLength;
Update (summary on the js side of things):
Download the pre-signed pdf (+ byteRange - this can be extracted with iText, so you can apply multiple signatures)
Prepare the signature (see first part of point 3. in the question)
Get private key:
const provider = await this.ws.getCrypto(selectedProviderId); // this.ws is a WebcryptoSocket
provider.sign = provider.subtle.sign.bind(provider.subtle);
setEngine(
'newEngine',
provider,
new CryptoEngine({
name: '',
crypto: provider,
subtle: provider.subtle,
})
);
const key = await this.getCertificateKey('private', provider, selectedCertificateId); //can be null
See Original answer points 1. and 2. Between theese I also have a hack:
let logout = await provider.logout();
let loggedIn = await provider.isLoggedIn();
if (!loggedIn) {
let login = await provider.login();
}
Add the signature on the pdf. Use original answer point 3., then the second part of point 3 in the question.

Node js reuse existing buff call

I use the following code which works as expected however I wonder if there is a way to improve is as Im doing New Buffer twice.
I need to encode two values now but more latter... , user and password.
I mean reduce boilerplate code
let secret = await getSecret("mt", "fe");
let adminBuff = new Buffer(secret.admin, 'base64');
let admin = adminBuff.toString('ascii');
let adminPass = new Buffer(secret.pass, 'base64');
let pass = adminPass.toString('ascii');
Use Buffer.from() since new Buffer is deprecated.
As for the reusability part, creating a function would be helpful. Something like the following:
function toAsciiString (value) {
const valueBuff = Buffer.from(value, 'base64');
return valueBuff.toString('ascii');
}
let secret = await getSecret("mt", "fe");
let admin = toAsciiString(secret.admin);
let pass = toAsciiString(secret.pass);
Hope this helps :)
You can iterate over the keys of the object "secret", process their values and create a new object containing the processing results.
For example:
let secret = await getSecret("mt", "fe");
const resultObj = {};
Object.keys(secret).forEach((key) => {
const newBuffer = new Buffer(secret[key], 'base64');
resultObj[key] = newBuffer.toString('ascii');
});

BCH transaction failed: Missing inputs 25

I am using Bitcoin Cash JS to create a transaction, and my code is as follows:
let BITBOXCli = require('bitbox-cli/lib/bitbox-cli').default;
const explorers = require('bitcore-explorers')
const insight = new explorers.Insight('https://test-bch-insight.bitpay.com')
let BITBOX = new BITBOXCli();
let txb = new BITBOX.TransactionBuilder('testnet');
var To = 'mkiuwbSQQVxMvvbBcYEKUdZgJfURhu3hrW'
var from = 'mvStb7hPtDCL8dmyifPGcYTuToVzf7ajTb';
var bch = require('bitcoincashjs')
var bchLib = require('#owstack/bch-lib')
const buf = new Buffer('b27ab45d3e3d157e8b95f800347974f9991cf13ceb814e1992f40c5e4e6d5253', 'hex')
const privateKey = new bch.PrivateKey(buf, bch.Networks.testnet)
const address = privateKey.toAddress('testnet')
insight.getUnspentUtxos(address.toString(), function (error, utxos) {
if (error) {
console.error(error)
return
}
console.log(utxos)
const utxo = {
txid: utxos[0].txid,
outputIndex: utxos[0].vout,
script: utxos[0].scriptPubKey,
satoshis: utxos[0].satoshis
}
const transaction = new bch.Transaction()
.from(utxo)
.to(To, 50000)
.sign(0, privateKey)
console.log(transaction.toString())
});
Now when I am running this code, I am able to get the raw transaction hash but I am not able to broadcast transaction and message is as follows:
Missing Inputs Error:-25
Any idea about this error?
Or is there any other way to create BCH transaction?
It looks like you're trying to create a simple transaction to send BCH from one address to another. There is now an example for this exact use case in the BITBOX SDK repository:
https://github.com/Bitcoin-com/bitbox-javascript-sdk/blob/master/examples/applications/wallet/send-bch/send-bch.js
There are also other examples there like creating a wallet checking a balance:
https://github.com/Bitcoin-com/bitbox-javascript-sdk/tree/master/examples/applications/wallet
There are even more examples in the Wormhole SDK. (Wormhole SDK is a superset of BITBOX, so it can do anything BITBOX can do):
https://github.com/Bitcoin-com/wormhole-sdk/tree/master/examples

Creating a new JSON import instance

so I have an object in JSON format: object.json.
And on every http call it's used in the method getAlbum(), but if I make multiple requests, the JSON gets cached, because it imports at the upper of the page.
How can I create a new instance to clear the JSON every time?
It has a lot of fields and depth, so I just can't create a new Object();
const albumReportJSON = require('./album.report.json');
const getAlbum = async(ctx) => {
const value = albumReportJSON;
/.. processing
}
Require the json inside your function
const getAlbum = async(ctx) => {
const albumReportJSON = require('./album.report.json');
// Or just do const value = require('./album.report.json');
const value = albumReportJSON;
/.. processing
}

Categories