Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@samouraiwallet/bip47
Advanced tools
A set of utilities for working with BIP47 and bitcoinjs-lib.
This library uses ES Modules. Node.js v16 or later is required. This library does not use any Node.js built-ins and thus is browser compatible.
Source code was written in Typescript. Type definitions are included in the published bundle.
npm install @samouraiwallet/bip47
or
pnpm add @samouraiwallet/bip47
or
yarn add @samouraiwallet/bip47
Generated API documentation is available in the git repository. To view latest API docs locally, you can run these commands.
# create a temp folder
mkdir bip47-docs
cd bip47-docs
# download and extract docs directory
curl -fsSL https://code.samourai.io/dojo/bip47-js/-/archive/master/bip47-js-master.tar.gz\?path\=docs | tar -xzv --strip-components=2
# run simple HTTP file server
npx serve .
You need to provide an implementation of secp256k1
elliptic curve.
Supported libraries:
import BIP47Factory from "@samouraiwallet/bip47";
import * as ecc from "tiny-secp256k1";
const bip47 = BIP47Factory(ecc);
on mainnet
import type {PaymentCodePrivate} from "@samouraiwallet/bip47";
let walletSeed: Uint8Array;
const alice: PaymentCodePrivate = bip47.fromSeed(walletSeed);
// with segwit support
const alice2: PaymentCodePrivate = bip47.fromSeed(walletSeed, true);
on testnet
import type {PaymentCodePrivate} from "@samouraiwallet/bip47";
import {networks} from "@samouraiwallet/bip47/utils";
let walletSeed: Uint8Array;
// pass in a desired network object (bitcoin | testnet | regtest) from utils or directly from bitcoinjs-lib
const alice: PaymentCodePrivate = bip47.fromSeed(walletSeed, false, networks['testnet']);
// with segwit support
const alice2: PaymentCodePrivate = bip47.fromSeed(walletSeed, true, networks['testnet']);
on mainnet
import type {PaymentCodePublic} from "@samouraiwallet/bip47";
const pcode = "PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97";
const bob: PaymentCodePublic = bip47.fromBase58(pcode);
on testnet
import type {PaymentCodePublic} from "@samouraiwallet/bip47";
import {networks} from "@samouraiwallet/bip47/utils";
const pcode = "PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97";
// pass in a desired network object (bitcoin | testnet | regtest) from utils or directly from bitcoinjs-lib
const bob: PaymentCodePublic = bip47.fromBase58(pcode, networks['testnet']);
const alicePcode: string = alice.toBase58(); // PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA
const aliceNotificationAddress: string = alice.getNotificationAddress(); // 1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW
const aliceNotifPubKey: Uint8Array = alice.getNotificationPublicKey(); // 0353883a146a23f988e0f381a9507cbdb3e3130cd81b3ce26daf2af088724ce683
const aliceNotifPrivKey: Uint8Array = alice.getNotificationPrivateKey(); // 8d6a8ecd8ee5e0042ad0cb56e3a971c760b5145c3917a8e7beaf0ed92d7a520c
Alice's side
// Bob's P2PKH address at index 0
const bobAddress: string = bob.getPaymentAddress(alice, 0, 'p2pkh'); // 141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK
// check if Bob's payment code supports receiving to segwit addresses
if (bob.segwit) {
// Bob's P2WPKH address at index 1
const bobSegwitAddress = bob.getPaymentAddress(alice, 1, 'p2wpkh'); // bc1qzn8a8drxv6ln7rztjsw660gzf3hnrfwupzmsfh
}
Bob's side
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47";
let bobSeed: Uint8Array;
let alicePcode: string; // base58 encoded payment code
const bob: PaymentCodePrivate = bip47.fromSeed(bobSeed);
const alice: PaymentCodePublic = bip47.fromBase58(alicePcode);
const bobAddress: string = bob.getPaymentAddress(alice, 0, 'p2pkh'); // 141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK
Alice's side
// Bob's payment pubkey at index 0
const bobPubKey: Uint8Array = bob.derivePaymentPublicKey(alice, 0); // 0344b4795e48df097bd87e6cf87a70e4f0c30b2d847b6e34cddde64af10296952d
Bob's side
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47";
let bobSeed: Uint8Array;
let alicePcode: string; // base58 encoded payment code
const bob: PaymentCodePrivate = bip47.fromSeed(bobSeed);
const alice: PaymentCodePublic = bip47.fromBase58(alicePcode);
// Bob's payment keys at index 0
const bobPubKey: Uint8Array = bob.derivePaymentPublicKey(alice, 0);
const bobPrivKey: Uint8Array = bob.derivePaymentPrivateKey(alice, 0);
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47";
let bobSeed: Uint8Array;
const bob: PaymentCodePrivate = bip47.fromSeed(bob.seed);
let scriptPubKey: Uint8Array; // scriptPubKey of notification transaction OP_RETURN output
let outpoint: Uint8Array; // outpoint of first input of notification transaction
let pubKey: Uint8Array; // public key of first input of notification transaction
const alice: PaymentCodePublic = bob.getPaymentCodeFromNotificationTransactionData(scriptPubKey, outpoint, pubKey);
const alicePcode: string = alice.toBase58(); // PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA
In order to extract payment code from a notification transaction, the scriptPubKey, outpoint and pubKey must be provided.
You can use bitcoinjs-lib
to extract these values from a transaction.
import * as bitcoin from 'bitcoinjs-lib';
let notificationTxHex: string;
const tx: bitcoin.Transaction = bitcoin.Transaction.fromHex(notificationTxHex);
const opReturnOutput = tx.outs.find((o) =>
o.script[0] === 0x6a && o.script[1] === 0x4c && o.script[2] === 0x50
);
if (!opReturnOutput) throw new error("Transaction doesn't contain OP_RETURN output");
const scriptPubKey: Uint8Array = opReturnOutput.script;
const input = tx.ins[0];
const outpoint: Uint8Array = new Uint8Array(input.hash.length + 4);
outpoint.set(input.hash);
outpoint.set(new Uint32Array([input.index]), input.hash.length)
let pubKey: Uint8Array;
if (input.witness.length) {
pubKey = input.witness[1];
} else if (bitcoin.script.toASM(input.script).split(' ').length === 2) {
pubKey = Buffer.from(bitcoin.script.toASM(input.script).split(' ')[1], 'hex',);
} else throw new Error('Unknown Transaction type');
let outpoint: Uint8Array; // outpoint of the first input of the notification transaction
let privKey: Uint8Array; // private key of a first input of the notification transaction
const blindedAlicePcode: string = alicePcode.getBlindedPaymentCode(bob, outpoint, privKey);
export declare const BIP47Factory: (ecc: TinySecp256k1Interface) => {
fromSeed: (bSeed: Uint8Array, segwit?: boolean, network?: Network) => PaymentCodePrivate;
fromBase58: (inString: string, network?: Network) => PaymentCodePublic;
fromBuffer: (buf: Uint8Array, network?: Network) => PaymentCodePublic;
};
export declare class PaymentCodePublic {
protected readonly ecc: TinySecp256k1Interface;
protected readonly bip32: BIP32API;
protected readonly buf: Uint8Array;
protected readonly network: Network;
root: BIP32Interface;
hasPrivKeys: boolean;
segwit: boolean;
constructor(ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network);
get features(): Uint8Array;
get pubKey(): Uint8Array;
get chainCode(): Uint8Array;
get paymentCode(): Uint8Array;
clone(): PaymentCodePublic;
toBase58(): string;
derive(index: number): BIP32Interface;
getNotificationPublicKey(): Uint8Array;
getNotificationAddress(): string;
protected derivePublicKeyFromSharedSecret(B: Uint8Array, S: Uint8Array | null): Uint8Array;
derivePaymentPublicKey(paymentCode: PaymentCodePrivate, idx: number): Uint8Array;
protected getAddressFromPubkey(pubKey: Uint8Array, type: AddressType): string;
getPaymentAddress(paymentCode: PaymentCodePrivate, idx: number, type?: AddressType): string;
getBlindedPaymentCode(destinationPaymentCode: PaymentCodePublic, outpoint: Uint8Array, privateKey: Uint8Array): string;
}
export declare class PaymentCodePrivate extends PaymentCodePublic {
constructor(root: BIP32Interface, ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network);
toPaymentCodePublic(): PaymentCodePublic;
clone(): PaymentCodePrivate;
deriveHardened(index: number): BIP32Interface;
derivePaymentPublicKey(paymentCode: PaymentCodePublic, idx: number): Uint8Array;
getPaymentAddress(paymentCode: PaymentCodePublic, idx: number, type?: AddressType): string;
derivePaymentPrivateKey(paymentCodePublic: PaymentCodePublic, idx: number): Uint8Array;
getNotificationPrivateKey(): Uint8Array;
getPaymentCodeFromNotificationTransactionData(scriptPubKey: Uint8Array, outpoint: Uint8Array, pubKey: Uint8Array): PaymentCodePublic;
}
FAQs
A set of utilities for working with BIP47 and bitcoinjs-lib
The npm package @samouraiwallet/bip47 receives a total of 38 weekly downloads. As such, @samouraiwallet/bip47 popularity was classified as not popular.
We found that @samouraiwallet/bip47 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.