Skip to content

Commit

Permalink
WebAPI-compatible, async rsa function
Browse files Browse the repository at this point in the history
  • Loading branch information
marshallswain committed Feb 3, 2024
1 parent f4ae35a commit 2c2a639
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 32 deletions.
2 changes: 1 addition & 1 deletion lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module.exports = async (data) => {
options.headers = headers

if (options.oauth) {
Object.assign(options, oauthOptions(url, options))
Object.assign(options, await oauthOptions(url, options))
}

if (options.auth) {
Expand Down
31 changes: 15 additions & 16 deletions lib/client/oauth.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
var crypto = require('crypto')
var qs = require('qs')
var _oauth = require('../sign/oauth.js')
const crypto = require('crypto')
const qs = require('qs')
const _oauth = require('../sign/oauth.js')

function buildOauth(url, options) {
async function buildOauth(url, options) {
const { oauth, method, form, body } = options
const uri = new URL(url)
return build(options.oauth, uri, options.method, uri.search, options.form, options.body)
}

function build(oauth, uri, method, query, form, body) {
var transport = oauth.transport_method || 'header'
const query = uri.search

const transport = oauth.transport_method || 'header'

if (transport === 'body' && (method !== 'POST' || form === undefined)) {
return new Error(
Expand All @@ -17,7 +16,7 @@ function build(oauth, uri, method, query, form, body) {
}

if (oauth.body_hash && form === undefined) {
var sig = oauth.signature_method || 'HMAC-SHA1'
const sig = oauth.signature_method || 'HMAC-SHA1'
if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(sig) < 0) {
return new Error(
'oauth: ' + oauth.signature_method + ' signature_method' + ' not supported with body_hash signing'
Expand All @@ -28,9 +27,9 @@ function build(oauth, uri, method, query, form, body) {
}
}

var oa = buildParams(oauth, uri, method, query, form)
const oa = await buildParams(oauth, uri, method, query, form)

var prefix, params
let prefix, params
if (transport === 'header') {
prefix = 'OAuth '
params = concatParams(oa, ',', '"')
Expand All @@ -55,7 +54,7 @@ function buildBodyHash(oauth, body) {
return Buffer.from(sha1, 'hex').toString('base64')
}

function buildParams(oauth, uri, method, query, form) {
async function buildParams(oauth, uri, method, query, form) {
var oa = {}
for (var key in oauth) {
var param = !/^oauth_/.test(key) ? 'oauth_' + key : key
Expand Down Expand Up @@ -88,7 +87,7 @@ function buildParams(oauth, uri, method, query, form) {
var baseurl = uri.protocol + '//' + uri.host + uri.pathname
var params = qs.parse([].concat(query, form, qs.stringify(oa)).join('&'))

oa.oauth_signature = _oauth.sign(
oa.oauth_signature = await _oauth.sign(
oa.oauth_signature_method,
method,
baseurl,
Expand Down Expand Up @@ -125,8 +124,8 @@ function concatParams(oauth, sep, wrap) {
.join(sep)
}

module.exports = (url, options) => {
var result = buildOauth(url, options)
module.exports = async (url, options) => {
const result = await buildOauth(url, options)
let { body } = options

if (result instanceof Error) {
Expand Down
61 changes: 46 additions & 15 deletions lib/sign/oauth.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
var crypto = require('crypto')
const crypto = require('crypto')

function sha (key, body, algorithm) {
return crypto.createHmac(algorithm, key).update(body).digest('base64')
}

function rsa (key, body) {
return crypto.createSign('RSA-SHA1').update(body).sign(key, 'base64')
}

async function rsaSignWithWebCrypto(privateKey, data) {
async function rsa(key, data) {
const privateKey = await pemToCryptoKey(key);
// Ensure data is in the form of an ArrayBuffer. If data is a string, encode it
const encoder = new TextEncoder();
const encodedData = encoder.encode(data);

// Sign the data with the RSA private key
const signature = await crypto.subtle.sign(
{
name: "RSASSA-PKCS1-v1_5",
hash: {name: "SHA-1"}, // Specify hash as SHA-1
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-1' }, // Specify hash as SHA-1
},
privateKey, // The RSA private key (CryptoKey) to sign with
encodedData // The data to sign
Expand All @@ -36,7 +33,41 @@ function arrayBufferToBase64(buffer) {
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
return btoa(binary);
}

async function pemToCryptoKey(pem) {
// Step 1: Remove PEM headers and footers
let b64 = pem.replace(/-----\w+ PRIVATE KEY-----/g, '');
b64 = b64.replace(/\n/g, '');

// Step 2: Base64 decode
const binaryDerString = atob(b64);

// Step 3: Convert to Uint8Array
const binaryDer = str2ab(binaryDerString);

// Step 4: Import as CryptoKey
return crypto.subtle.importKey(
'pkcs8',
binaryDer,
{
name: 'RSASSA-PKCS1-v1_5',
hash: 'SHA-1', // Ensure this matches the hash used in your RSA operations
},
true,
['sign']
);
}

// Utility function to convert from a binary string to an ArrayBuffer
function str2ab(str) {
const buffer = new ArrayBuffer(str.length);
const bufferView = new Uint8Array(buffer);
for (let i = 0; i < str.length; i++) {
bufferView[i] = str.charCodeAt(i);
}
return buffer;
}

function rfc3986 (str) {
Expand Down Expand Up @@ -107,7 +138,7 @@ function generateBase (httpMethod, base_uri, params) {
return base
}

function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) {
async function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) {
var base = generateBase(httpMethod, base_uri, params)
var key = [
consumer_secret || '',
Expand All @@ -117,7 +148,7 @@ function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret)
return sha(key, base, 'sha1')
}

function hmacsign256 (httpMethod, base_uri, params, consumer_secret, token_secret) {
async function hmacsign256 (httpMethod, base_uri, params, consumer_secret, token_secret) {
var base = generateBase(httpMethod, base_uri, params)
var key = [
consumer_secret || '',
Expand All @@ -127,14 +158,14 @@ function hmacsign256 (httpMethod, base_uri, params, consumer_secret, token_secre
return sha(key, base, 'sha256')
}

function rsasign (httpMethod, base_uri, params, private_key, token_secret) {
async function rsasign (httpMethod, base_uri, params, private_key, token_secret) {
var base = generateBase(httpMethod, base_uri, params)
var key = private_key || ''

return rsa(key, base)
}

function plaintext (consumer_secret, token_secret) {
async function plaintext (consumer_secret, token_secret) {
var key = [
consumer_secret || '',
token_secret || ''
Expand All @@ -143,7 +174,7 @@ function plaintext (consumer_secret, token_secret) {
return key
}

function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_secret) {
async function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_secret) {
var method
var skipArgs = 1

Expand All @@ -165,7 +196,7 @@ function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_
throw new Error('Signature method not supported: ' + signMethod)
}

return method.apply(null, [].slice.call(arguments, skipArgs))
return await method.apply(null, [].slice.call(arguments, skipArgs))
}

exports.hmacsign = hmacsign
Expand Down

0 comments on commit 2c2a639

Please sign in to comment.