Nodejs crypto in typescript file - javascript

I have created my own hash.js file that requires crypto and exports two functions that use crypto. It works fine in my api.js file when I hash passwords. However Now I am trying to import this file in my user.service.ts file so that I can send the hashed version of the password as a query parameter instead of the password itself. When I try to do so I always get a TypeScript error telling me that the functions crypto uses are not functions. However I can still console log the object I import and it looks legit to me. I looked at other java script files in the node_modules folder and I cannot see anything that should be wrong with my file.
I have also found out that there seems to be some definition file that I need to create but I have also had many attempts at creating such a file but nothing seems to work
A few hours of googling and the lack of knowledge amongst lack of time on this project led me to this post, it is my first stackoverflow post and I hope that it is not to unclear and I am glad to provide any information needed to help me resolve this issue.
Error code from console
LoginComponent.html:18 ERROR TypeError: crypto.randomBytes is not a function
at Object.genRandomString (hash.js:12)
at UserService.loginUser (user.service.ts:82)
at LoginComponent.getUser (login.component.ts:54)
at LoginComponent.onSubmit (login.component.ts:44)
at Object.eval [as handleEvent] (LoginComponent.html:18)
at handleEvent (core.es5.js:12014)
at callWithDebugContext (core.es5.js:13475)
at Object.debugHandleEvent [as handleEvent] (core.es5.js:13063)
at dispatchEvent (core.es5.js:8607)
at core.es5.js:10775
LoginComponent.html:18 ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 31, nodeDef: {…}, elDef: {…}, elView: {…}}
the hash.js file
'use strict';
var crypto = require('crypto');
/**
* generates random string of characters i.e salt
* #function
* #param {number} length - Length of the random string.
*/
function genRandomString (length){
return crypto.randomBytes(Math.ceil(length/2))
.toString('hex') /** convert to hexadecimal format */
.slice(0,length); /** return required number of characters */
};
/**
* hash password with sha512.
* #function
* #param {string} password - List of required fields.
* #param {string} salt - Data to be validated.
*/
function sha512(password, salt){
var hash = crypto.createHmac('sha512', salt); /** Hashing algorithm sha512 */
hash.update(password);
var value = hash.digest('hex');
return {
salt:salt,
passwordHash:value
};
};
module.exports = {
genRandomString: genRandomString,
sha512: sha512
};

There seems to be some confusion mixing JavaScript and TypeScript, but since I came across this issue myself, here is how I solved it.
First, your hash.js file should be hash.ts. Then you can import crypto and use it normally. Related code below:
import * as crypto from "crypto";
public setEncryptionKeyDES(sKey: string) {
const desIV = Buffer.alloc(8);
this.encLobby.cipher = crypto.createCipheriv(
"des-cbc",
Buffer.from(sKey, "hex"),
desIV,
);
this.encLobby.cipher.setAutoPadding(false);
this.encLobby.decipher = crypto.createDecipheriv(
"des-cbc",
Buffer.from(sKey, "hex"),
desIV,
);
this.encLobby.decipher.setAutoPadding(false);
this.isSetupComplete = true;
}
Edit1: Adding #attdona's answer from below, make sure you install #types/node into your project as well, or you will receive many errors related to node modules not being found.

Just hitted this issue (node v13.12.0, tsc v3.8.3). In my case the import:
import * as crypto from "crypto";
gives the error:
error TS2307: Cannot find module 'crypto'
because I have to install types definition for node: it includes crypto ambient declarations.
npm install #types/node
Note: If you have a global install of #types/node then you have to explicitly declare the path where #types
are located with --typesRoot option.
See here for details.

When I try to do so I always get a TypeScript error telling me that the functions crypto uses are not functions.
This is happening because TypeScript statically analyzes your files and tries to help you with types of data that you're working with. If you're using a function written in JavaScript without any hint to TypeScript about what this function is using as arguments and what is the type of its return value, it simply cannot do its job, which is why you're getting an error.
You could create a decalration file, but you do not need to if you want a quick fix and if you won't be using these raw JS functions in other files. You just need to declare it anywhere in the file. You can read more about using declare keyword in TypeScript.
However I can still console log the object I import and it looks legit to me.
Judging by your screenshot, the function randomBytes is called properly (as you can see from the stack trace printed below the error). The problem is that the crypto object does not contain a method named randomBytes. This is because crypto is a global object in newer browsers.
Your crypto.js file is probably assuming existence of a different crypto global object than what browser has. Either provide this object in the proper scope so your function can see it, or inline those functions using a bundler such as Rollup.

Well this turned out to be a rather horrific rabbit hole to go down. I followed a bunch of recommended suggestions but ultimately the solution that worked for me was (as always...) super-simple, ONCE you know how.
The answer was to use the browser built-in crypto functionality. I only wanted to check a one-way digest of a password as being sufficient for a very lightweight app. However most guides were only partial suggestions, or didn't work with my app's setup of using Angular 7 and babel. So here's the final code I ended up with, with the following notes:
Need to encode into an array buffer to perform the digest
The digest function is returning a promise, so had to unwrap its value using .then
Did not require adding any NPM modules or imports
Anyway here's the code, hope it helps other Stack Overflowers.
I also found Daniel Roesler's Github page very useful for implementations of various web cryptography examples.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-user-create',
templateUrl: './user-create.component.html',
styleUrls: ['./user-create.component.css']
})
export class UserCreateComponent implements OnInit {
constructor() { }
ngOnInit() {
sha256("hello").then(digestValue => {
console.log(digestValue);
});
}
}
async function sha256(str) {
const encoder = new TextEncoder();
const encdata = encoder.encode(str);
const buf = await crypto.subtle.digest("SHA-256", encdata);
return Array.prototype.map.call(new Uint8Array(buf), x=>(('00'+x.toString(16)).slice(-2))).join('');
}

Related

How can I ensure browser compatibility for the webcrypto API in Typescript?

So, what I'm trying to achieve is to use the Webcrypto API in Typescript and compile it to Javascript code that runs in the browser.
In the Browser, accessing the Webcrypto API is simple. Just window.crypto or crypto for short.
In node.js, you have to import it from the built-in "crypto" library first:
import { webcrypto as crypto } from "crypto"
Since I want to run it in the browser, I don't have to and also can not import it.
And here is the problem: For Typescript, I have to import the node library to get the necessary types. If I don't, this is the error:
src/main.ts:15:43 - error TS2503: Cannot find namespace 'crypto'.
So, for Typescript to compile, I have to import "crypto", but for the browser to run it I must not.
Possible work-arounds I can think of are:
tell Typescript that "crypto" (window.crypto) exists and is equivalent to crypto.webcrypto from the node library
tell Typescript to not include the line with the import in the output Javascript file
tell the browser to ignore that import
None of those I could find in the internet.
What I also tried is to use a dynamic import so I can conditionally import it, something like:
let _crypto: any = window?.crypto
if (!_crypto) {
_crypto = await import("crypto")
}
const crypto: any = _crypto
In theory, this should make the browser just ignore that "crypto" import since window.crypto exists.
But Typescript doesn't import the necessary types for dynamic imports for some reason.
In case this is needed, this is part of my code:
export function sign(privkey: crypto.CryptoKey, data: any) {
data = YSON.stringify(data)
data = new TextEncoder().encode(data).buffer
return crypto.subtle.sign({name: "ECDSA", hash: {name: "SHA-256"}}, privkey, data)
}
This function takes some data, stringifies it, and signs it using a private key.
The Y in YSON is intended.
The error is in the first line of that code snippet, I'm using the crypto.CryptoKey type.

how to correctly use Google Protobuf, when using imports in Javascript

Lets suppose there are two .proto files, file hello.proto and file core.proto.
File core.proto is imported with import statement like
import "core.proto";
content of files:
hello.proto
syntax = "proto3";
import "core.proto";
message GetBalance {
string address = 1;
}
core.proto
syntax = "proto3";
message Block {
string chain = 1;
}
I run protoc with
protoc --proto_path=./ --js_out=import_style=commonjs,binary:./ *.proto
Javascript files are generated, but because of "import" statement there is something like
goog.object.extend(proto, core_pb);
and thats a problem because when I try to import this javascript file into my code,
import * as hello_pb from './hello_pb'
it does not work, it says "proto is not defined" it is a reference error.
What am I doing wrong?
Thanks!
While this answer will not directly solve your import problem, it may give you a workaround.
An alternative to "compiling" protobuf into JS is to use protobufjs which implements protobuf and can handle loading arbitrary .proto files. It has its disadvantages (not as lightweight as your solution could be) but it works well.
Assuming you distribute your .proto in your environment (service, app, whatever it is) importing protobufjs and deserializing a message would be something like:
import { loadSync } from 'protobufjs';
...
const protoRoot = loadSync('path/to/hello.proto'));
const Hello = protoRoot.lookupType('Hello'); // can be some_namespace.Hello
...
let message = Hello.decode(buffer);
Most (if not all) datatypes and encoding/decoding is supported. Check out protobuf project page.

process.env.FOO transformed into {}.FOO and throwing error "expected expression, got "."

I wrote a TypeScript module that interacts with a specific RESTful API. The module never refers to process.env anywhere (target is browser, possibly Node down the line as well).
I use Rollup to transpile to a single JS file. From there, going into Node and require('./build/index') is successful, and I can even run the functions and get expected results. So Rollup itself appears to work.
But the file contains many references to things like process.env.DEBUG. (I suspect Rollup is doing something to create loggers that can work in both Node and browser.)
Now I import this into a Gatsby UI project that will need to connect to the API using this module:
import { api } from 'my-api'
api.someApiCall()
Problem is that when Gatsby compiles all this (using Webpack?) into commons.js (some big JS file with a lot of combined code from different libraries, including my API module), it appears to transform the module's process.env.DEBUG (for example) into {}.DEBUG. Then the browser complains that "expected expression, got '.'". Which makes sense. You cannot access {}.DEBUG. It would have to be ({}).DEBUG or const o = {}; o.DEBUG.
Now I have been off in the world of other languages for a while. Rollup is fairly new to me. Gatsby is very new to me. What is the way forward? Do I tell Rollup via a config to replace process.env with ({}) so that way ? But then that precludes the library from ever being used in Node.js and taking advantage of process.env.
Do I need to change something about Gatsby to have it replace process.env with ({})?
Edit For example, here is some output showing up in my browserin commons.js:
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete {}.DEBUG;
} else {
{}.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* #return {String} returns the previously persisted debug modes
* #api private
*/
function load() {
return {}.DEBUG;
}
In my module, those are process.env.DEBUG.
Edit 2 I've also tried putting a gatsby-node.js containing this:
exports.onCreateWebpackConfig = ({
plugins,
actions,
}) => {
const { setWebpackConfig } = actions;
setWebpackConfig({
plugins: [
plugins.define({
'process.env': "({})",
'{}.DEBUG': '({}).DEBUG',
})
]
})
}
No effect.

Document function that returns object property by parameter

I'm making an Api repository for my Vue.js application, following this article.
Thing is, I like to document my functions so I have better code completion on VSCode. I typically use jsDoc for this.
I'm stuck here:
import DiarioEscolarRepository from './diarioEscolarRepository';
import PeriodoAvaliativoRepository from './periodoAvaliativoRepository';
import AtividadeAvaliativaRepository from './atividadeAvaliativaRepository';
import CorteEtarioRepository from './corteEtarioRepository';
const repositories = {
diarioEscolar: DiarioEscolarRepository,
periodoAvaliativo: PeriodoAvaliativoRepository,
atividadeAvaliativa: AtividadeAvaliativaRepository,
corteEtario: CorteEtarioRepository,
};
export default const RepositoryFactory = {
get(name){
return repositories[name];
}
};
I need to make it so the editor understands that the get function is a simple acessor to the repositories object.
I tried using #typedef and #type, but none of them worked properly.
I tried something like #returns {repositories.name}, but is also does not work.
Is there a way to document this?
I also thought about using a typescript definition file but I never did it, so I don't know where to begin.

Load “Vanilla” Javascript Libraries into Node.js = 'Unexpected token export'

Building on related issue: Load "Vanilla" Javascript Libraries into Node.js
I'm trying to load a 'Vanilla' Javascript library 'rsa.js' into NodeJS, using the method described by Chris W. in the question above. In essence I've created a custom node_modules directory like this:
./node_modules/rsa/rsa-lib/rsa.js
./node_modules/rsa/rsa-lib/README
./node_modules/rsa/index.js
Only problem is, compiling the module fails on the last line of rsa.js:
undefined:836
export default rsa;
SyntaxError: Unexpected token export
rsa.js
var rsa = {};
(function(rsa) {
...
})(rsa);
console.log("rsa",rsa);
export default rsa;
index.js
var fs = require('fs');
// Read and eval library
filedata = fs.readFileSync('./node_modules/rsa/rsa-lib/rsa.js','utf8');
eval(filedata);
/* The rsa.js file defines a class 'rsa' which is all we want to export */
exports.rsa = rsa
Any suggestions how to fix this problem?
The error 'Unexpected token export' is caused because the library is using
export default thingToExport;
instead of
module.exports = thingToExport
This is an ES6 feature not supported by Node (yet), so when Node tries to run the code, it throws an error.
How to solve: try modifying the last line of the library so it says module.exports = rsa;.
I should add, though, that eval is not a good way to load a library into your own code. That is what require is for. If you have installed this library with npm i and it is in your node_modules, you should be able to load it into your code with var rsa = require('rsa');.
Again though, if you're not transpiling the code, it may have problems with export default, so you will probably want to change that to module.exports either way.

Categories