### In web
```html
-<script src="https://unpkg.com/nanocurrency-web@1.3.6" type="text/javascript"></script>
+<script src="https://unpkg.com/nanocurrency-web@1.4.0" type="text/javascript"></script>
<script type="text/javascript">
NanocurrencyWeb.wallet.generate(...);
</script>
```javascript
import { block } from 'nanocurrency-web'
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
+const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
const data = {
// Current balance from account info
walletBalanceRaw: '5618869000000000000000000000000',
```javascript
import { block } from 'nanocurrency-web'
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
+const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
const data = {
// Your current balance in RAW from account info
walletBalanceRaw: '18618869000000000000000000000000',
```javascript
import { block } from 'nanocurrency-web'
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
+const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
const data = {
// Your current balance, from account info
walletBalanceRaw: '3000000000000000000000000000000',
const valid = tools.validateMnemonic('edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur')
```
+#### Encrypting and decrypting strings
+You are able to encrypt and decrypt strings to implement end-to-end encryption using the Diffie-Hellman key exchange by using the Nano address and private key. The public and private keys are converted to Curve25519 keys which are suitable for encryption within the library.
+
+```javascript
+import { box } from 'nanocurrency-web'
+
+// Encrypt on device 1
+const encrypted = box.encrypt(message, recipientAddress, senderPrivateKey)
+
+// Send the encrypted message to the recipient and decrypt on device 2
+const decrypted = box.decrypt(encrypted, senderAddress, recipientPrivateKey)
+```
+
## Contributions
You are welcome to contribute to the module. To develop, use the following commands.
import AddressGenerator from './lib/address-generator'
import AddressImporter, { Account, Wallet } from './lib/address-importer'
import BlockSigner, { BlockData, ReceiveBlock, RepresentativeBlock, SendBlock, SignedBlock } from './lib/block-signer'
+import Box from './lib/box'
import NanoAddress from './lib/nano-address'
import NanoConverter from './lib/nano-converter'
import Signer from './lib/signer'
import Convert from './lib/util/convert'
-const nanoAddress = new NanoAddress()
-const generator = new AddressGenerator()
-const importer = new AddressImporter()
-const signer = new Signer()
-
const wallet = {
/**
* @returns {Wallet} The wallet
*/
generate: (entropy?: string, seedPassword?: string): Wallet => {
- return generator.generateWallet(entropy, seedPassword)
+ return AddressGenerator.generateWallet(entropy, seedPassword)
},
/**
* @returns {Wallet} The wallet
*/
generateLegacy: (seed?: string): Wallet => {
- return generator.generateLegacyWallet(seed)
+ return AddressGenerator.generateLegacyWallet(seed)
},
/**
* @returns {Wallet} The wallet
*/
fromMnemonic: (mnemonic: string, seedPassword?: string): Wallet => {
- return importer.fromMnemonic(mnemonic, seedPassword)
+ return AddressImporter.fromMnemonic(mnemonic, seedPassword)
},
/**
* @returns {Wallet} The wallet
*/
fromLegacyMnemonic: (mnemonic: string): Wallet => {
- return importer.fromLegacyMnemonic(mnemonic)
+ return AddressImporter.fromLegacyMnemonic(mnemonic)
},
/**
* @returns {Wallet} The wallet, without the mnemonic phrase because it's not possible to infer backwards
*/
fromSeed: (seed: string): Wallet => {
- return importer.fromSeed(seed)
+ return AddressImporter.fromSeed(seed)
},
/**
* @returns {Wallet} The wallet
*/
fromLegacySeed: (seed: string): Wallet => {
- return importer.fromLegacySeed(seed)
+ return AddressImporter.fromLegacySeed(seed)
},
/**
* @returns {Account[]} a list of accounts
*/
accounts: (seed: string, from: number, to: number): Account[] => {
- return importer.fromSeed(seed, from, to).accounts
+ return AddressImporter.fromSeed(seed, from, to).accounts
},
/**
* @returns {Account[]} a list of accounts
*/
legacyAccounts: (seed: string, from: number, to: number): Account[] => {
- return importer.fromLegacySeed(seed, from, to).accounts
+ return AddressImporter.fromLegacySeed(seed, from, to).accounts
},
}
-const blockSigner = new BlockSigner()
const block = {
/**
* @returns {SignedBlock} the signed block
*/
send: (data: SendBlock, privateKey: string): SignedBlock => {
- return blockSigner.send(data, privateKey)
+ return BlockSigner.send(data, privateKey)
},
* @returns {SignedBlock} the signed block
*/
receive: (data: ReceiveBlock, privateKey: string): SignedBlock => {
- return blockSigner.receive(data, privateKey)
+ return BlockSigner.receive(data, privateKey)
},
toAddress: 'nano_1111111111111111111111111111111111111111111111111111hifc8npp', // Burn address
}
- return blockSigner.send(block, privateKey)
+ return BlockSigner.send(block, privateKey)
},
}
*/
sign: (privateKey: string, ...input: string[]): string => {
const data = input.map(Convert.stringToHex)
- return signer.sign(privateKey, ...data)
+ return Signer.sign(privateKey, ...data)
},
/**
*/
verify: (publicKey: string, signature: string, ...input: string[]): boolean => {
const data = input.map(Convert.stringToHex)
- return signer.verify(publicKey, signature, ...data)
+ return Signer.verify(publicKey, signature, ...data)
},
/**
*/
verifyBlock: (publicKey: string, block: BlockData): boolean => {
const preamble = 0x6.toString().padStart(64, '0')
- return signer.verify(publicKey, block.signature,
+ return Signer.verify(publicKey, block.signature,
preamble,
- nanoAddress.nanoAddressToHexString(block.account),
+ NanoAddress.nanoAddressToHexString(block.account),
block.previous,
- nanoAddress.nanoAddressToHexString(block.representative),
+ NanoAddress.nanoAddressToHexString(block.representative),
Convert.dec2hex(block.balance, 16).toUpperCase(),
block.link)
},
* @returns {boolean} valid or not
*/
validateAddress: (input: string): boolean => {
- return nanoAddress.validateNanoAddress(input)
+ return NanoAddress.validateNanoAddress(input)
},
/**
* @returns {boolean} valid or not
*/
validateMnemonic: (input: string): boolean => {
- return importer.validateMnemonic(input)
+ return AddressImporter.validateMnemonic(input)
},
/**
* @returns {string} the public key
*/
addressToPublicKey: (input: string): string => {
- const cleaned = input
- .replace('nano_', '')
- .replace('xrb_', '')
- const publicKeyBytes = nanoAddress.decodeNanoBase32(cleaned)
- return Convert.ab2hex(publicKeyBytes).slice(0, 64)
+ return NanoAddress.addressToPublicKey(input)
},
/**
* @returns {string} the nano address
*/
publicKeyToAddress: (input: string): string => {
- return nanoAddress.deriveAddress(input)
+ return NanoAddress.deriveAddress(input)
},
/**
*/
blake2b: (input: string | string[]): string => {
if (Array.isArray(input)) {
- return Convert.ab2hex(signer.generateHash(input.map(Convert.stringToHex)))
+ return Convert.ab2hex(Signer.generateHash(input.map(Convert.stringToHex)))
} else {
- return Convert.ab2hex(signer.generateHash([Convert.stringToHex(input)]))
+ return Convert.ab2hex(Signer.generateHash([Convert.stringToHex(input)]))
}
},
}
+const box = {
+
+ /**
+ * Encrypt a message using a Nano address private key for
+ * end-to-end encrypted messaging.
+ *
+ * Encrypts the message using the recipient's public key,
+ * the sender's private key and a random nonce generated
+ * inside the library. The message can be opened with the
+ * recipient's private key and the sender's public key by
+ * using the decrypt method.
+ *
+ * @param {string} message string to encrypt
+ * @param {string} address nano address of the recipient
+ * @param {string} privateKey private key of the sender
+ * @returns {string} encrypted message encoded in Base64
+ */
+ encrypt: (message: string, address: string, privateKey: string): string => {
+ return Box.encrypt(message, address, privateKey)
+ },
+
+ /**
+ * Decrypt a message using a Nano address private key.
+ *
+ * Decrypts the message by using the sender's public key,
+ * the recipient's private key and the nonce which is included
+ * in the encrypted message.
+ *
+ * @param {string} encrypted string to decrypt
+ * @param {string} address nano address of the sender
+ * @param {string} privateKey private key of the recipient
+ * @returns {string} decrypted message encoded in UTF-8
+ */
+ decrypt: (encrypted: string, address: string, privateKey: string): string => {
+ return Box.decrypt(encrypted, address, privateKey)
+ }
+
+}
+
export {
wallet,
block,
tools,
+ box,
}
* @param {string} [entropy] - (Optional) Custom entropy if the caller doesn't want a default generated entropy
* @param {string} [seedPassword] - (Optional) Password for the seed
*/
- generateWallet(entropy = '', seedPassword: string = ''): Wallet {
- const bip39 = new Bip39Mnemonic(seedPassword)
- const mnemonicSeed = bip39.createWallet(entropy)
- const wallet = new AddressImporter().fromSeed(mnemonicSeed.seed, 0, 0)
-
+ static generateWallet = (entropy = '', seedPassword: string = ''): Wallet => {
+ const mnemonicSeed = Bip39Mnemonic.createWallet(entropy, seedPassword)
+ const wallet = AddressImporter.fromSeed(mnemonicSeed.seed, 0, 0)
return {
...wallet,
mnemonic: mnemonicSeed.mnemonic,
/**
* Generates a legacy Nano wallet
- *
*/
- generateLegacyWallet(seed?: string): Wallet {
- const bip39 = new Bip39Mnemonic()
- const mnemonicSeed = bip39.createLegacyWallet(seed)
- const wallet = new AddressImporter().fromLegacySeed(mnemonicSeed.seed, 0, 0, mnemonicSeed.mnemonic)
-
- return wallet
+ static generateLegacyWallet = (seed?: string): Wallet => {
+ const mnemonicSeed = Bip39Mnemonic.createLegacyWallet(seed)
+ return AddressImporter.fromLegacySeed(mnemonicSeed.seed, 0, 0, mnemonicSeed.mnemonic)
}
}
* @param {string} [seedPassword] - (Optional) The password to use to secure the mnemonic
* @returns {Wallet} - The wallet derived from the mnemonic phrase
*/
- fromMnemonic(mnemonic: string, seedPassword = ''): Wallet {
- const bip39 = new Bip39Mnemonic(seedPassword)
- if (!bip39.validateMnemonic(mnemonic)) {
+ static fromMnemonic = (mnemonic: string, seedPassword = ''): Wallet => {
+ if (!Bip39Mnemonic.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic phrase')
}
- const seed = bip39.mnemonicToSeed(mnemonic)
+ const seed = Bip39Mnemonic.mnemonicToSeed(mnemonic, seedPassword)
const accounts = this.accounts(seed, 0, 0)
return {
* @param {string} mnemonic - The mnemonic words to import the wallet from
* @returns {Wallet} - The wallet derived from the mnemonic phrase
*/
- fromLegacyMnemonic(mnemonic: string): Wallet {
- const bip39 = new Bip39Mnemonic()
- if (!bip39.validateMnemonic(mnemonic)) {
+ static fromLegacyMnemonic = (mnemonic: string): Wallet => {
+ if (!Bip39Mnemonic.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic phrase')
}
- const seed = bip39.mnemonicToLegacySeed(mnemonic)
+ const seed = Bip39Mnemonic.mnemonicToLegacySeed(mnemonic)
return this.fromLegacySeed(seed, 0, 0, mnemonic)
}
*
* @param mnemonic {string} mnemonic - The mnemonic words to validate
*/
- validateMnemonic(mnemonic: string): boolean {
- const bip39 = new Bip39Mnemonic()
- return bip39.validateMnemonic(mnemonic);
+ static validateMnemonic = (mnemonic: string): boolean => {
+ return Bip39Mnemonic.validateMnemonic(mnemonic)
}
/**
* @param {number} [to] - (Optional) The end index of the private keys to derive to
* @returns {Wallet} The wallet derived from the mnemonic phrase
*/
- fromSeed(seed: string, from = 0, to = 0): Wallet {
+ static fromSeed = (seed: string, from = 0, to = 0): Wallet => {
if (seed.length !== 128) {
throw new Error('Invalid seed length, must be a 128 byte hexadecimal string')
}
* @param {number} [to] - (Optional) The end index of the private keys to derive to
* @returns {Wallet} The wallet derived from the seed
*/
- fromLegacySeed(seed: string, from: number = 0, to: number = 0, mnemonic?: string): Wallet {
+ static fromLegacySeed = (seed: string, from: number = 0, to: number = 0, mnemonic?: string): Wallet => {
if (seed.length !== 64) {
throw new Error('Invalid seed length, must be a 64 byte hexadecimal string')
}
const accounts = this.legacyAccounts(seed, from, to)
return {
- mnemonic: mnemonic || new Bip39Mnemonic().deriveMnemonic(seed),
+ mnemonic: mnemonic || Bip39Mnemonic.deriveMnemonic(seed),
seed,
accounts,
}
* @param {number} from - The start index of private keys to derive from
* @param {number} to - The end index of private keys to derive to
*/
- private accounts(seed: string, from: number, to: number): Account[] {
+ private static accounts = (seed: string, from: number, to: number): Account[] => {
const accounts = []
for (let i = from; i <= to; i++) {
- const bip44 = new Bip32KeyDerivation(`44'/165'/${i}'`, seed)
- const privateKey = bip44.derivePath().key
-
- const ed25519 = new Ed25519()
- const keyPair = ed25519.generateKeys(privateKey)
-
- const nano = new NanoAddress()
- const address = nano.deriveAddress(keyPair.publicKey)
-
+ const privateKey = Bip32KeyDerivation.derivePath(`44'/165'/${i}'`, seed).key
+ const keyPair = new Ed25519().generateKeys(privateKey)
+ const address = NanoAddress.deriveAddress(keyPair.publicKey)
accounts.push({
accountIndex: i,
privateKey: keyPair.privateKey,
* @param {number} from - The start index of private keys to derive from
* @param {number} to - The end index of private keys to derive to
*/
- private legacyAccounts(seed: string, from: number, to: number): Account[] {
- const signer = new Signer()
+ private static legacyAccounts = (seed: string, from: number, to: number): Account[] => {
const accounts: Account[] = []
for (let i = from; i <= to; i++) {
- const privateKey = Convert.ab2hex(signer.generateHash([seed, Convert.dec2hex(i, 4)]))
-
- const ed25519 = new Ed25519()
- const keyPair = ed25519.generateKeys(privateKey)
-
- const nano = new NanoAddress()
- const address = nano.deriveAddress(keyPair.publicKey)
-
+ const privateKey = Convert.ab2hex(Signer.generateHash([ seed, Convert.dec2hex(i, 4) ]))
+ const keyPair = new Ed25519().generateKeys(privateKey)
+ const address = NanoAddress.deriveAddress(keyPair.publicKey)
accounts.push({
accountIndex: i,
privateKey: keyPair.privateKey,
export default class Bip32KeyDerivation {
- path: string
- seed: string
-
- constructor(path: string, seed: string) {
- this.path = path
- this.seed = seed
- }
-
- derivePath = (): Chain => {
- const { key, chainCode } = this.getKeyFromSeed()
- const segments = this.path
+ static derivePath = (path: string, seed: string): Chain => {
+ const { key, chainCode } = this.getKeyFromSeed(seed)
+ const segments = path
.split('/')
.map(v => v.replace('\'', ''))
.map(el => parseInt(el, 10))
)
}
- private getKeyFromSeed = (): Chain => {
+ private static getKeyFromSeed = (seed: string): Chain => {
return this.derive(
- enc.Hex.parse(this.seed),
+ enc.Hex.parse(seed),
enc.Utf8.parse(ED25519_CURVE))
}
- private CKDPriv = ({ key, chainCode }: Chain, index: number) => {
+ private static CKDPriv = ({ key, chainCode }: Chain, index: number) => {
const ib = []
ib.push((index >> 24) & 0xff)
ib.push((index >> 16) & 0xff)
enc.Hex.parse(chainCode))
}
- private derive = (data: string, base: string): Chain => {
+ private static derive = (data: string, base: string): Chain => {
const hmac = algo.HMAC.create(algo.SHA512, base)
const I = hmac.update(data).finalize().toString()
const IL = I.slice(0, I.length / 2)
export default class Bip39Mnemonic {
- password: string
-
- constructor(password?: string) {
- this.password = password
- }
-
/**
* Creates a BIP39 wallet
*
* @param {string} [entropy] - (Optional) the entropy to use instead of generating
* @returns {MnemonicSeed} The mnemonic phrase and a seed derived from the (generated) entropy
*/
- createWallet = (entropy: string): MnemonicSeed => {
+ static createWallet = (entropy: string, password: string): MnemonicSeed => {
if (entropy) {
if (entropy.length !== 64) {
throw new Error('Invalid entropy length, must be a 64 byte hexadecimal string')
}
const mnemonic = this.deriveMnemonic(entropy)
- const seed = this.mnemonicToSeed(mnemonic)
+ const seed = this.mnemonicToSeed(mnemonic, password)
return {
mnemonic,
* @param {string} seed - (Optional) the seed to be used for the wallet
* @returns {MnemonicSeed} The mnemonic phrase and a generated seed if none provided
*/
- createLegacyWallet = (seed?: string): MnemonicSeed => {
+ static createLegacyWallet = (seed?: string): MnemonicSeed => {
if (seed) {
if (seed.length !== 64) {
throw new Error('Invalid entropy length, must be a 64 byte hexadecimal string')
}
}
- deriveMnemonic = (entropy: string): string => {
+ static deriveMnemonic = (entropy: string): string => {
const entropyBinary = Convert.hexStringToBinary(entropy)
const entropySha256Binary = Convert.hexStringToBinary(this.calculateChecksum(entropy))
const entropyBinaryWithChecksum = entropyBinary + entropySha256Binary
* @param {string} mnemonic - The mnemonic phrase to validate
* @returns {boolean} Is the mnemonic phrase valid
*/
- validateMnemonic = (mnemonic: string): boolean => {
+ static validateMnemonic = (mnemonic: string): boolean => {
const wordArray = Util.normalizeUTF8(mnemonic).split(' ')
if (wordArray.length % 3 !== 0) {
return false
*
* @param {string} mnemonic Mnemonic phrase separated by spaces
*/
- mnemonicToLegacySeed = (mnemonic: string): string => {
+ static mnemonicToLegacySeed = (mnemonic: string): string => {
const wordArray = Util.normalizeUTF8(mnemonic).split(' ')
const bits = wordArray.map((w: string) => {
const wordIndex = words.indexOf(w)
*
* @param {string} mnemonic Mnemonic phrase separated by spaces
*/
- mnemonicToSeed = (mnemonic: string): string => {
+ static mnemonicToSeed = (mnemonic: string, password: string): string => {
const normalizedMnemonic = Util.normalizeUTF8(mnemonic)
- const normalizedPassword = 'mnemonic' + Util.normalizeUTF8(this.password)
+ const normalizedPassword = 'mnemonic' + Util.normalizeUTF8(password)
return PBKDF2(
normalizedMnemonic,
.toString(enc.Hex)
}
- private randomHex = (length: number): string => {
- return lib.WordArray.random(length / 2).toString()
+ private static randomHex = (length: number): string => {
+ return lib.WordArray.random(length).toString()
}
- private calculateChecksum = (entropyHex: string): string => {
+ private static calculateChecksum = (entropyHex: string): string => {
const entropySha256 = SHA256(enc.Hex.parse(entropyHex)).toString()
return entropySha256.substr(0, entropySha256.length / 32)
}
import BigNumber from 'bignumber.js'
-//@ts-ignore
-import { blake2b } from 'blakejs'
-import Ed25519 from './ed25519'
import NanoAddress from './nano-address'
import NanoConverter from './nano-converter'
import Signer from './signer'
export default class BlockSigner {
- nanoAddress = new NanoAddress()
- ed25519 = new Ed25519()
- signer = new Signer()
-
- preamble: string = 0x6.toString().padStart(64, '0')
+ static readonly preamble: string = 0x6.toString().padStart(64, '0')
/**
* Sign a receive block
* @param {string} privateKey Private key to sign the data with
* @returns {SignedBlock} the signed block to publish to the blockchain
*/
- receive(data: ReceiveBlock, privateKey: string): SignedBlock {
+ static receive = (data: ReceiveBlock, privateKey: string): SignedBlock => {
const validateInputRaw = (input: string) => !!input && !isNaN(+input)
if (!validateInputRaw(data.walletBalanceRaw)) {
throw new Error('Invalid format in wallet balance')
throw new Error('Invalid format in send amount')
}
- if (!this.nanoAddress.validateNanoAddress(data.toAddress)) {
+ if (!NanoAddress.validateNanoAddress(data.toAddress)) {
throw new Error('Invalid toAddress')
}
- if (!this.nanoAddress.validateNanoAddress(data.representativeAddress)) {
+ if (!NanoAddress.validateNanoAddress(data.representativeAddress)) {
throw new Error('Invalid representativeAddress')
}
const newBalanceNano = new BigNumber(balanceNano).plus(new BigNumber(amountNano))
const newBalanceRaw = NanoConverter.convert(newBalanceNano, 'NANO', 'RAW')
const newBalanceHex = Convert.dec2hex(newBalanceRaw, 16).toUpperCase()
- const account = this.nanoAddress.nanoAddressToHexString(data.toAddress)
+ const account = NanoAddress.nanoAddressToHexString(data.toAddress)
const link = data.transactionHash
- const representative = this.nanoAddress.nanoAddressToHexString(data.representativeAddress)
+ const representative = NanoAddress.nanoAddressToHexString(data.representativeAddress)
- const signature = this.signer.sign(
+ const signature = Signer.sign(
privateKey,
this.preamble,
account,
* @param {string} privateKey Private key to sign the data with
* @returns {SignedBlock} the signed block to publish to the blockchain
*/
- send(data: SendBlock, privateKey: string): SignedBlock {
+ static send = (data: SendBlock, privateKey: string): SignedBlock => {
const validateInputRaw = (input: string) => !!input && !isNaN(+input)
if (!validateInputRaw(data.walletBalanceRaw)) {
throw new Error('Invalid format in wallet balance')
throw new Error('Invalid format in send amount')
}
- if (!this.nanoAddress.validateNanoAddress(data.toAddress)) {
+ if (!NanoAddress.validateNanoAddress(data.toAddress)) {
throw new Error('Invalid toAddress')
}
- if (!this.nanoAddress.validateNanoAddress(data.fromAddress)) {
+ if (!NanoAddress.validateNanoAddress(data.fromAddress)) {
throw new Error('Invalid fromAddress')
}
- if (!this.nanoAddress.validateNanoAddress(data.representativeAddress)) {
+ if (!NanoAddress.validateNanoAddress(data.representativeAddress)) {
throw new Error('Invalid representativeAddress')
}
const newBalanceNano = new BigNumber(balanceNano).minus(new BigNumber(amountNano))
const newBalanceRaw = NanoConverter.convert(newBalanceNano, 'NANO', 'RAW')
const newBalanceHex = Convert.dec2hex(newBalanceRaw, 16).toUpperCase()
- const account = this.nanoAddress.nanoAddressToHexString(data.fromAddress)
- const link = this.nanoAddress.nanoAddressToHexString(data.toAddress)
- const representative = this.nanoAddress.nanoAddressToHexString(data.representativeAddress)
+ const account = NanoAddress.nanoAddressToHexString(data.fromAddress)
+ const link = NanoAddress.nanoAddressToHexString(data.toAddress)
+ const representative = NanoAddress.nanoAddressToHexString(data.representativeAddress)
- const signature = this.signer.sign(
+ const signature = Signer.sign(
privateKey,
this.preamble,
account,
--- /dev/null
+import * as base64 from 'byte-base64'
+//@ts-ignore
+import { lib } from 'crypto-js'
+
+import Ed25519 from './ed25519'
+import NanoAddress from './nano-address'
+import Convert from './util/convert'
+import Curve25519 from './util/curve25519'
+
+export default class Box {
+
+ static readonly NONCE_LENGTH = 24
+
+ static encrypt(message: string, address: string, privateKey: string) {
+ if (!message) {
+ throw new Error('No message to encrypt')
+ }
+
+ const publicKey = NanoAddress.addressToPublicKey(address)
+ const { privateKey: convertedPrivateKey, publicKey: convertedPublicKey } = new Ed25519().convertKeys({
+ privateKey,
+ publicKey,
+ })
+
+ const nonce = Convert.hex2ab(lib.WordArray.random(this.NONCE_LENGTH).toString())
+ const encrypted = new Curve25519().box(
+ Convert.str2bin(message),
+ nonce,
+ Convert.hex2ab(convertedPublicKey),
+ Convert.hex2ab(convertedPrivateKey),
+ )
+
+ const full = new Uint8Array(nonce.length + encrypted.length)
+ full.set(nonce)
+ full.set(encrypted, nonce.length)
+
+ return base64.bytesToBase64(full)
+ }
+
+ static decrypt(encrypted: string, address: string, privateKey: string) {
+ if (!encrypted) {
+ throw new Error('No message to decrypt')
+ }
+
+ const publicKey = NanoAddress.addressToPublicKey(address)
+ const { privateKey: convertedPrivateKey, publicKey: convertedPublicKey } = new Ed25519().convertKeys({
+ privateKey,
+ publicKey,
+ })
+
+ const decodedEncryptedMessageBytes = base64.base64ToBytes(encrypted)
+ const nonce = decodedEncryptedMessageBytes.slice(0, this.NONCE_LENGTH)
+ const encryptedMessage = decodedEncryptedMessageBytes.slice(this.NONCE_LENGTH, encrypted.length)
+
+ const decrypted = new Curve25519().boxOpen(
+ encryptedMessage,
+ nonce,
+ Convert.hex2ab(convertedPublicKey),
+ Convert.hex2ab(convertedPrivateKey),
+ )
+
+ if (!decrypted) {
+ throw new Error('Could not decrypt message')
+ }
+
+ return Convert.bin2str(decrypted)
+ }
+
+}
}
}
+ /**
+ * Convert ed25519 keypair to curve25519 keypair suitable for Diffie-Hellman key exchange
+ *
+ * @param {KeyPair} keyPair ed25519 keypair
+ * @returns {KeyPair} keyPair Curve25519 keypair
+ */
+ convertKeys(keyPair: KeyPair): KeyPair {
+ const publicKey = Convert.ab2hex(this.curve.convertEd25519PublicKeyToCurve25519(Convert.hex2ab(keyPair.publicKey)))
+ if (!publicKey) {
+ return null
+ }
+ const privateKey = Convert.ab2hex(this.curve.convertEd25519SecretKeyToCurve25519(Convert.hex2ab(keyPair.privateKey)))
+ return {
+ publicKey,
+ privateKey,
+ }
+ }
+
/**
* Generate a message signature
* @param {Uint8Array} msg Message to be signed as byte array
* @param {Uint8Array} Returns the signature as 64 byte typed array
*/
verify(msg: Uint8Array, publicKey: Uint8Array, signature: Uint8Array): boolean {
- const CURVE = this.curve;
+ const CURVE = this.curve
const p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
const q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
const pk = Convert.hex2ab(this.generateKeys(Convert.ab2hex(sk)).publicKey)
- this.cryptoHash(d, sk, 32)
+ this.curve.cryptoHash(d, sk, 32)
d[0] &= 248
d[31] &= 127
d[31] |= 64
sm[32 + i] = d[32 + i]
}
- this.cryptoHash(r, sm.subarray(32), n + 32)
+ this.curve.cryptoHash(r, sm.subarray(32), n + 32)
this.reduce(r)
this.scalarbase(p, r)
this.pack(sm, p)
sm[i] = pk[i - 32]
}
- this.cryptoHash(h, sm, n + 64)
+ this.curve.cryptoHash(h, sm, n + 64)
this.reduce(h)
for (i = 0; i < 64; i++) {
return smlen
}
- private cryptoHash(out: Uint8Array, m: Uint8Array, n: number): number {
- const input = new Uint8Array(n)
- for (let i = 0; i < n; ++i) {
- input[i] = m[i]
- }
-
- const hash = blake2b(input)
- for (let i = 0; i < 64; ++i) {
- out[i] = hash[i]
- }
-
- return 0
- }
-
}
export interface KeyPair {
export default class NanoAddress {
- readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
- readonly prefix = 'nano_'
+ static readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
+ static readonly prefix = 'nano_'
- deriveAddress = (publicKey: string): string => {
+ static deriveAddress = (publicKey: string): string => {
const publicKeyBytes = Convert.hex2ab(publicKey)
const checksum = blake2b(publicKeyBytes, undefined, 5).reverse()
const encoded = this.encodeNanoBase32(publicKeyBytes)
return this.prefix + encoded + encodedChecksum
}
- encodeNanoBase32 = (publicKey: Uint8Array): string => {
+ static encodeNanoBase32 = (publicKey: Uint8Array): string => {
const length = publicKey.length
const leftover = (length * 8) % 5
const offset = leftover === 0 ? 0 : 5 - leftover
return output
}
- decodeNanoBase32 = (input: string): Uint8Array => {
+ static addressToPublicKey = (input: string): string => {
+ const cleaned = input
+ .replace('nano_', '')
+ .replace('xrb_', '')
+ const publicKeyBytes = NanoAddress.decodeNanoBase32(cleaned)
+ return Convert.ab2hex(publicKeyBytes).slice(0, 64)
+ }
+
+ static decodeNanoBase32 = (input: string): Uint8Array => {
const length = input.length
const leftover = (length * 5) % 8
const offset = leftover === 0 ? 0 : 8 - leftover
*
* @param {string} address Nano address
*/
- validateNanoAddress = (address: string): boolean => {
+ static validateNanoAddress = (address: string): boolean => {
if (address === undefined) {
throw Error('Address must be defined.')
}
}
const expectedChecksum = address.slice(-8)
- const publicKey = address.slice(address.indexOf('_') + 1, -8)
+ const publicKey = this.stripAddress(address)
const publicKeyBuffer = this.decodeNanoBase32(publicKey)
const actualChecksumBuffer = blake2b(publicKeyBuffer, null, 5).reverse()
const actualChecksum = this.encodeNanoBase32(actualChecksumBuffer)
return expectedChecksum === actualChecksum
}
- nanoAddressToHexString = (addr: string): string => {
+ static nanoAddressToHexString = (addr: string): string => {
addr = addr.slice(-60)
const isValid = /^[13456789abcdefghijkmnopqrstuwxyz]+$/.test(addr)
if (isValid) {
}
}
- private readChar(char: string): number {
+ static stripAddress(address: string): string {
+ return address.slice(address.indexOf('_') + 1, -8)
+ }
+
+ private static readChar(char: string): number {
const idx = this.alphabet.indexOf(char)
if (idx === -1) {
* @param inputUnit {string} the unit to convert from
* @param outputUnit {string} the unit to convert to
*/
- static convert(input: string | BigNumber, inputUnit: string, outputUnit: string): string {
+ static convert = (input: string | BigNumber, inputUnit: string, outputUnit: string): string => {
let value = new BigNumber(input.toString())
switch (inputUnit) {
export default class Signer {
- ed25519 = new Ed25519()
-
/**
* Signs any data using the ed25519 signature system
*
* @param privateKey Private key to sign the data with
* @param data Data to sign
*/
- sign(privateKey: string, ...data: string[]): string {
- const signature = this.ed25519.sign(
+ static sign = (privateKey: string, ...data: string[]): string => {
+ const signature = new Ed25519().sign(
this.generateHash(data),
Convert.hex2ab(privateKey))
return Convert.ab2hex(signature)
* @param signature Signature to verify
* @param data Data to verify
*/
- verify(publicKey: string, signature: string, ...data: string[]): boolean {
- return this.ed25519.verify(
+ static verify = (publicKey: string, signature: string, ...data: string[]): boolean => {
+ return new Ed25519().verify(
this.generateHash(data),
Convert.hex2ab(publicKey),
- Convert.hex2ab(signature));
+ Convert.hex2ab(signature))
}
/**
*
* @param data Data to hash
*/
- generateHash(data: string[]): Uint8Array {
+ static generateHash = (data: string[]): Uint8Array => {
const ctx = blake2bInit(32, undefined)
data.forEach(str => blake2bUpdate(ctx, Convert.hex2ab(str)))
return blake2bFinal(ctx)
return bin.subarray(0, p)
}
+ /**
+ * Convert a byte array to a UTF-8 encoded string
+ *
+ * @param {Uint8Array} arr Byte array
+ * @return {String} UTF-8 encoded string
+ */
+ static bin2str = (arr: Uint8Array) => {
+ let i, s = []
+ for (i = 0; i < arr.length; i++) {
+ s.push(String.fromCharCode(arr[i]))
+ }
+
+ return decodeURIComponent(escape(s.join('')))
+ }
+
/**
* Convert Array of 8 bytes (int64) to hex string
*
+//@ts-ignore
+import { blake2b } from 'blakejs'
+
import Util from './util'
+/**
+ * Derived from:
+ * - mipher
+ * - tweetnacl
+ * - ed2curve-js
+ *
+ * With added types etc
+ */
export default class Curve25519 {
gf0: Int32Array
I: Int32Array
_9: Uint8Array
_121665: Int32Array
+ _0: Uint8Array
+ sigma: Uint8Array
+ minusp: Uint32Array
constructor() {
this.gf0 = this.gf()
this.D = this.gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])
this.D2 = this.gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])
this.I = this.gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])
+ this._0 = new Uint8Array(16)
+ this.sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107])
+ this.minusp = new Uint32Array([5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252])
}
gf(init?: number[]): Int32Array {
o[15] = t15
}
+ coreSalsa20(o: Uint8Array, p: Uint8Array, k: Uint8Array, c: Uint8Array) {
+ const j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24,
+ j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24,
+ j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24,
+ j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24,
+ j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24,
+ j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24,
+ j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24,
+ j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24,
+ j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24,
+ j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24,
+ j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24,
+ j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24,
+ j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24,
+ j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24,
+ j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24,
+ j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24
+
+ let x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
+ x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
+ x15 = j15, u
+
+ for (let i = 0; i < 20; i += 2) {
+ u = x0 + x12 | 0
+ x4 ^= u<<7 | u>>>(32-7)
+ u = x4 + x0 | 0
+ x8 ^= u<<9 | u>>>(32-9)
+ u = x8 + x4 | 0
+ x12 ^= u<<13 | u>>>(32-13)
+ u = x12 + x8 | 0
+ x0 ^= u<<18 | u>>>(32-18)
+
+ u = x5 + x1 | 0
+ x9 ^= u<<7 | u>>>(32-7)
+ u = x9 + x5 | 0
+ x13 ^= u<<9 | u>>>(32-9)
+ u = x13 + x9 | 0
+ x1 ^= u<<13 | u>>>(32-13)
+ u = x1 + x13 | 0
+ x5 ^= u<<18 | u>>>(32-18)
+
+ u = x10 + x6 | 0
+ x14 ^= u<<7 | u>>>(32-7)
+ u = x14 + x10 | 0
+ x2 ^= u<<9 | u>>>(32-9)
+ u = x2 + x14 | 0
+ x6 ^= u<<13 | u>>>(32-13)
+ u = x6 + x2 | 0
+ x10 ^= u<<18 | u>>>(32-18)
+
+ u = x15 + x11 | 0
+ x3 ^= u<<7 | u>>>(32-7)
+ u = x3 + x15 | 0
+ x7 ^= u<<9 | u>>>(32-9)
+ u = x7 + x3 | 0
+ x11 ^= u<<13 | u>>>(32-13)
+ u = x11 + x7 | 0
+ x15 ^= u<<18 | u>>>(32-18)
+
+ u = x0 + x3 | 0
+ x1 ^= u<<7 | u>>>(32-7)
+ u = x1 + x0 | 0
+ x2 ^= u<<9 | u>>>(32-9)
+ u = x2 + x1 | 0
+ x3 ^= u<<13 | u>>>(32-13)
+ u = x3 + x2 | 0
+ x0 ^= u<<18 | u>>>(32-18)
+
+ u = x5 + x4 | 0
+ x6 ^= u<<7 | u>>>(32-7)
+ u = x6 + x5 | 0
+ x7 ^= u<<9 | u>>>(32-9)
+ u = x7 + x6 | 0
+ x4 ^= u<<13 | u>>>(32-13)
+ u = x4 + x7 | 0
+ x5 ^= u<<18 | u>>>(32-18)
+
+ u = x10 + x9 | 0
+ x11 ^= u<<7 | u>>>(32-7)
+ u = x11 + x10 | 0
+ x8 ^= u<<9 | u>>>(32-9)
+ u = x8 + x11 | 0
+ x9 ^= u<<13 | u>>>(32-13)
+ u = x9 + x8 | 0
+ x10 ^= u<<18 | u>>>(32-18)
+
+ u = x15 + x14 | 0
+ x12 ^= u<<7 | u>>>(32-7)
+ u = x12 + x15 | 0
+ x13 ^= u<<9 | u>>>(32-9)
+ u = x13 + x12 | 0
+ x14 ^= u<<13 | u>>>(32-13)
+ u = x14 + x13 | 0
+ x15 ^= u<<18 | u>>>(32-18)
+ }
+ x0 = x0 + j0 | 0
+ x1 = x1 + j1 | 0
+ x2 = x2 + j2 | 0
+ x3 = x3 + j3 | 0
+ x4 = x4 + j4 | 0
+ x5 = x5 + j5 | 0
+ x6 = x6 + j6 | 0
+ x7 = x7 + j7 | 0
+ x8 = x8 + j8 | 0
+ x9 = x9 + j9 | 0
+ x10 = x10 + j10 | 0
+ x11 = x11 + j11 | 0
+ x12 = x12 + j12 | 0
+ x13 = x13 + j13 | 0
+ x14 = x14 + j14 | 0
+ x15 = x15 + j15 | 0
+
+ o[ 0] = x0 >>> 0 & 0xff
+ o[ 1] = x0 >>> 8 & 0xff
+ o[ 2] = x0 >>> 16 & 0xff
+ o[ 3] = x0 >>> 24 & 0xff
+
+ o[ 4] = x1 >>> 0 & 0xff
+ o[ 5] = x1 >>> 8 & 0xff
+ o[ 6] = x1 >>> 16 & 0xff
+ o[ 7] = x1 >>> 24 & 0xff
+
+ o[ 8] = x2 >>> 0 & 0xff
+ o[ 9] = x2 >>> 8 & 0xff
+ o[10] = x2 >>> 16 & 0xff
+ o[11] = x2 >>> 24 & 0xff
+
+ o[12] = x3 >>> 0 & 0xff
+ o[13] = x3 >>> 8 & 0xff
+ o[14] = x3 >>> 16 & 0xff
+ o[15] = x3 >>> 24 & 0xff
+
+ o[16] = x4 >>> 0 & 0xff
+ o[17] = x4 >>> 8 & 0xff
+ o[18] = x4 >>> 16 & 0xff
+ o[19] = x4 >>> 24 & 0xff
+
+ o[20] = x5 >>> 0 & 0xff
+ o[21] = x5 >>> 8 & 0xff
+ o[22] = x5 >>> 16 & 0xff
+ o[23] = x5 >>> 24 & 0xff
+
+ o[24] = x6 >>> 0 & 0xff
+ o[25] = x6 >>> 8 & 0xff
+ o[26] = x6 >>> 16 & 0xff
+ o[27] = x6 >>> 24 & 0xff
+
+ o[28] = x7 >>> 0 & 0xff
+ o[29] = x7 >>> 8 & 0xff
+ o[30] = x7 >>> 16 & 0xff
+ o[31] = x7 >>> 24 & 0xff
+
+ o[32] = x8 >>> 0 & 0xff
+ o[33] = x8 >>> 8 & 0xff
+ o[34] = x8 >>> 16 & 0xff
+ o[35] = x8 >>> 24 & 0xff
+
+ o[36] = x9 >>> 0 & 0xff
+ o[37] = x9 >>> 8 & 0xff
+ o[38] = x9 >>> 16 & 0xff
+ o[39] = x9 >>> 24 & 0xff
+
+ o[40] = x10 >>> 0 & 0xff
+ o[41] = x10 >>> 8 & 0xff
+ o[42] = x10 >>> 16 & 0xff
+ o[43] = x10 >>> 24 & 0xff
+
+ o[44] = x11 >>> 0 & 0xff
+ o[45] = x11 >>> 8 & 0xff
+ o[46] = x11 >>> 16 & 0xff
+ o[47] = x11 >>> 24 & 0xff
+
+ o[48] = x12 >>> 0 & 0xff
+ o[49] = x12 >>> 8 & 0xff
+ o[50] = x12 >>> 16 & 0xff
+ o[51] = x12 >>> 24 & 0xff
+
+ o[52] = x13 >>> 0 & 0xff
+ o[53] = x13 >>> 8 & 0xff
+ o[54] = x13 >>> 16 & 0xff
+ o[55] = x13 >>> 24 & 0xff
+
+ o[56] = x14 >>> 0 & 0xff
+ o[57] = x14 >>> 8 & 0xff
+ o[58] = x14 >>> 16 & 0xff
+ o[59] = x14 >>> 24 & 0xff
+
+ o[60] = x15 >>> 0 & 0xff
+ o[61] = x15 >>> 8 & 0xff
+ o[62] = x15 >>> 16 & 0xff
+ o[63] = x15 >>> 24 & 0xff
+ }
+
+ coreHsalsa20(o: Uint8Array, p: Uint8Array, k: Uint8Array, c: Uint8Array) {
+ const j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24,
+ j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24,
+ j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24,
+ j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24,
+ j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24,
+ j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24,
+ j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24,
+ j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24,
+ j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24,
+ j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24,
+ j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24,
+ j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24,
+ j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24,
+ j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24,
+ j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24,
+ j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24
+
+ let x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
+ x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
+ x15 = j15, u
+
+ for (let i = 0; i < 20; i += 2) {
+ u = x0 + x12 | 0
+ x4 ^= u<<7 | u>>>(32-7)
+ u = x4 + x0 | 0
+ x8 ^= u<<9 | u>>>(32-9)
+ u = x8 + x4 | 0
+ x12 ^= u<<13 | u>>>(32-13)
+ u = x12 + x8 | 0
+ x0 ^= u<<18 | u>>>(32-18)
+
+ u = x5 + x1 | 0
+ x9 ^= u<<7 | u>>>(32-7)
+ u = x9 + x5 | 0
+ x13 ^= u<<9 | u>>>(32-9)
+ u = x13 + x9 | 0
+ x1 ^= u<<13 | u>>>(32-13)
+ u = x1 + x13 | 0
+ x5 ^= u<<18 | u>>>(32-18)
+
+ u = x10 + x6 | 0
+ x14 ^= u<<7 | u>>>(32-7)
+ u = x14 + x10 | 0
+ x2 ^= u<<9 | u>>>(32-9)
+ u = x2 + x14 | 0
+ x6 ^= u<<13 | u>>>(32-13)
+ u = x6 + x2 | 0
+ x10 ^= u<<18 | u>>>(32-18)
+
+ u = x15 + x11 | 0
+ x3 ^= u<<7 | u>>>(32-7)
+ u = x3 + x15 | 0
+ x7 ^= u<<9 | u>>>(32-9)
+ u = x7 + x3 | 0
+ x11 ^= u<<13 | u>>>(32-13)
+ u = x11 + x7 | 0
+ x15 ^= u<<18 | u>>>(32-18)
+
+ u = x0 + x3 | 0
+ x1 ^= u<<7 | u>>>(32-7)
+ u = x1 + x0 | 0
+ x2 ^= u<<9 | u>>>(32-9)
+ u = x2 + x1 | 0
+ x3 ^= u<<13 | u>>>(32-13)
+ u = x3 + x2 | 0
+ x0 ^= u<<18 | u>>>(32-18)
+
+ u = x5 + x4 | 0
+ x6 ^= u<<7 | u>>>(32-7)
+ u = x6 + x5 | 0
+ x7 ^= u<<9 | u>>>(32-9)
+ u = x7 + x6 | 0
+ x4 ^= u<<13 | u>>>(32-13)
+ u = x4 + x7 | 0
+ x5 ^= u<<18 | u>>>(32-18)
+
+ u = x10 + x9 | 0
+ x11 ^= u<<7 | u>>>(32-7)
+ u = x11 + x10 | 0
+ x8 ^= u<<9 | u>>>(32-9)
+ u = x8 + x11 | 0
+ x9 ^= u<<13 | u>>>(32-13)
+ u = x9 + x8 | 0
+ x10 ^= u<<18 | u>>>(32-18)
+
+ u = x15 + x14 | 0
+ x12 ^= u<<7 | u>>>(32-7)
+ u = x12 + x15 | 0
+ x13 ^= u<<9 | u>>>(32-9)
+ u = x13 + x12 | 0
+ x14 ^= u<<13 | u>>>(32-13)
+ u = x14 + x13 | 0
+ x15 ^= u<<18 | u>>>(32-18)
+ }
+
+ o[ 0] = x0 >>> 0 & 0xff
+ o[ 1] = x0 >>> 8 & 0xff
+ o[ 2] = x0 >>> 16 & 0xff
+ o[ 3] = x0 >>> 24 & 0xff
+
+ o[ 4] = x5 >>> 0 & 0xff
+ o[ 5] = x5 >>> 8 & 0xff
+ o[ 6] = x5 >>> 16 & 0xff
+ o[ 7] = x5 >>> 24 & 0xff
+
+ o[ 8] = x10 >>> 0 & 0xff
+ o[ 9] = x10 >>> 8 & 0xff
+ o[10] = x10 >>> 16 & 0xff
+ o[11] = x10 >>> 24 & 0xff
+
+ o[12] = x15 >>> 0 & 0xff
+ o[13] = x15 >>> 8 & 0xff
+ o[14] = x15 >>> 16 & 0xff
+ o[15] = x15 >>> 24 & 0xff
+
+ o[16] = x6 >>> 0 & 0xff
+ o[17] = x6 >>> 8 & 0xff
+ o[18] = x6 >>> 16 & 0xff
+ o[19] = x6 >>> 24 & 0xff
+
+ o[20] = x7 >>> 0 & 0xff
+ o[21] = x7 >>> 8 & 0xff
+ o[22] = x7 >>> 16 & 0xff
+ o[23] = x7 >>> 24 & 0xff
+
+ o[24] = x8 >>> 0 & 0xff
+ o[25] = x8 >>> 8 & 0xff
+ o[26] = x8 >>> 16 & 0xff
+ o[27] = x8 >>> 24 & 0xff
+
+ o[28] = x9 >>> 0 & 0xff
+ o[29] = x9 >>> 8 & 0xff
+ o[30] = x9 >>> 16 & 0xff
+ o[31] = x9 >>> 24 & 0xff
+ }
+
S(o: Int32Array, a: Int32Array): void {
this.M(o, a, a)
}
return 0
}
+ vn(x: Uint8Array, xi: number, y: Uint8Array, yi: number, n: number) {
+ let i, d = 0
+ for (i = 0; i < n; i++) {
+ d |= x[xi+i]^y[yi+i]
+ }
+ return (1 & ((d - 1) >>> 8)) - 1
+ }
+
/**
* Internal scalar mult function
* @param {Uint8Array} q Result
this.pack25519(q, x16)
}
+ cryptoStreamSalsa20Xor(c: Uint8Array, cpos: number, m: Uint8Array, mpos: number, b: number, n: Uint8Array, k: Uint8Array) {
+ const z = new Uint8Array(16)
+ const x = new Uint8Array(64)
+ let u, i
+ for (i = 0; i < 16; i++) {
+ z[i] = 0
+ }
+ for (i = 0; i < 8; i++) {
+ z[i] = n[i]
+ }
+ while (b >= 64) {
+ this.coreSalsa20(x, z, k, this.sigma)
+ for (i = 0; i < 64; i++) c[cpos+i] = m[mpos+i] ^ x[i]
+ u = 1
+ for (i = 8; i < 16; i++) {
+ u = u + (z[i] & 0xff) | 0
+ z[i] = u & 0xff
+ u >>>= 8
+ }
+ b -= 64
+ cpos += 64
+ mpos += 64
+ }
+ if (b > 0) {
+ this.coreSalsa20(x, z, k, this.sigma)
+ for (i = 0; i < b; i++) {
+ c[cpos+i] = m[mpos+i] ^ x[i]
+ }
+ }
+ return 0
+ }
+
+ cryptoStreamSalsa20(c: Uint8Array, cpos: number, b: number, n: Uint8Array, k: Uint8Array) {
+ const z = new Uint8Array(16), x = new Uint8Array(64)
+ let u, i
+ for (i = 0; i < 16; i++) z[i] = 0
+ for (i = 0; i < 8; i++) z[i] = n[i]
+ while (b >= 64) {
+ this.coreSalsa20(x, z, k, this.sigma)
+ for (i = 0; i < 64; i++) {
+ c[cpos+i] = x[i]
+ }
+ u = 1
+ for (i = 8; i < 16; i++) {
+ u = u + (z[i] & 0xff) | 0
+ z[i] = u & 0xff
+ u >>>= 8
+ }
+ b -= 64
+ cpos += 64
+ }
+ if (b > 0) {
+ this.coreSalsa20(x, z, k, this.sigma)
+ for (i = 0; i < b; i++) {
+ c[cpos+i] = x[i]
+ }
+ }
+ return 0
+ }
+
+ add1305(h: Uint32Array, c: Uint32Array) {
+ let j, u = 0
+ for (j = 0; j < 17; j++) {
+ u = (u + ((h[j] + c[j]) | 0)) | 0
+ h[j] = u & 255
+ u >>>= 8
+ }
+ }
+
+ cryptoOnetimeauth(out: Uint8Array, outpos: number, m: Uint8Array, mpos: number, n: number, k: Uint8Array) {
+ let s, i, j, u
+ const x = new Uint32Array(17), r = new Uint32Array(17),
+ h = new Uint32Array(17), c = new Uint32Array(17),
+ g = new Uint32Array(17)
+ for (j = 0; j < 17; j++) {
+ r[j]=h[j]=0
+ }
+ for (j = 0; j < 16; j++) {
+ r[j]=k[j]
+ }
+
+ r[3]&=15
+ r[4]&=252
+ r[7]&=15
+ r[8]&=252
+ r[11]&=15
+ r[12]&=252
+ r[15]&=15
+
+ while (n > 0) {
+ for (j = 0; j < 17; j++) {
+ c[j] = 0
+ }
+ for (j = 0; (j < 16) && (j < n); ++j) {
+ c[j] = m[mpos+j]
+ }
+ c[j] = 1
+ mpos += j; n -= j
+ this.add1305(h, c)
+ for (i = 0; i < 17; i++) {
+ x[i] = 0
+ for (j = 0; j < 17; j++) {
+ x[i] = (x[i] + (h[j] * ((j <= i) ? r[i - j] : ((320 * r[i + 17 - j])|0))) | 0) | 0
+ }
+ }
+ for (i = 0; i < 17; i++) {
+ h[i] = x[i]
+ }
+ u = 0
+ for (j = 0; j < 16; j++) {
+ u = (u + h[j]) | 0
+ h[j] = u & 255
+ u >>>= 8
+ }
+ u = (u + h[16]) | 0; h[16] = u & 3
+ u = (5 * (u >>> 2)) | 0
+ for (j = 0; j < 16; j++) {
+ u = (u + h[j]) | 0
+ h[j] = u & 255
+ u >>>= 8
+ }
+ u = (u + h[16]) | 0; h[16] = u
+ }
+
+ for (j = 0; j < 17; j++) {
+ g[j] = h[j]
+ }
+ this.add1305(h, this.minusp)
+ s = (-(h[16] >>> 7) | 0)
+ for (j = 0; j < 17; j++) {
+ h[j] ^= s & (g[j] ^ h[j])
+ }
+
+ for (j = 0; j < 16; j++) {
+ c[j] = k[j + 16]
+ }
+ c[16] = 0
+ this.add1305(h, c)
+ for (j = 0; j < 16; j++) {
+ out[outpos+j] = h[j]
+ }
+ return 0
+ }
+
+ cryptoOnetimeauthVerify(h: Uint8Array, hpos: number, m: Uint8Array, mpos: number, n: number, k: Uint8Array) {
+ const x = new Uint8Array(16)
+ this.cryptoOnetimeauth(x, 0, m, mpos, n, k)
+ return this.cryptoVerify16(h, hpos, x, 0)
+ }
+
+ cryptoVerify16(x: Uint8Array, xi: number, y: Uint8Array, yi: number) {
+ return this.vn(x, xi, y, yi, 16)
+ }
+
+ cryptoBoxBeforenm(k: Uint8Array, y: Uint8Array, x: Uint8Array) {
+ const s = new Uint8Array(32)
+ this.cryptoScalarmult(s, x, y)
+ return this.coreHsalsa20(k, this._0, s, this.sigma)
+ }
+
+ cryptoSecretbox(c: Uint8Array, m: Uint8Array, d: number, n: Uint8Array, k: Uint8Array) {
+ let i
+ if (d < 32) {
+ return -1
+ }
+ this.cryptoStreamXor(c, 0, m, 0, d, n, k)
+ this.cryptoOnetimeauth(c, 16, c, 32, d - 32, c)
+ for (i = 0; i < 16; i++) {
+ c[i] = 0
+ }
+ return 0
+ }
+
+ cryptoSecretboxOpen(m: Uint8Array, c: Uint8Array, d: number, n: Uint8Array, k: Uint8Array) {
+ let i
+ const x = new Uint8Array(32)
+ if (d < 32) {
+ return -1
+ }
+ this.cryptoStream(x, 0, 32, n, k)
+ if (this.cryptoOnetimeauthVerify(c, 16, c, 32, d - 32, x) !== 0) {
+ return -1
+ }
+ this.cryptoStreamXor(m, 0, c, 0, d, n, k)
+ for (i = 0; i < 32; i++) {
+ m[i] = 0
+ }
+ return 0
+ }
+
+ cryptoStream(c: Uint8Array, cpos: number, d: number, n: Uint8Array, k: Uint8Array) {
+ const s = new Uint8Array(32)
+ this.coreHsalsa20(s, n, k, this.sigma)
+ const sn = new Uint8Array(8)
+ for (var i = 0; i < 8; i++) {
+ sn[i] = n[i+16]
+ }
+ return this.cryptoStreamSalsa20(c, cpos, d, sn, s)
+ }
+
+ cryptoStreamXor(c: Uint8Array, cpos: number, m: Uint8Array, mpos: number, d: number, n: Uint8Array, k: Uint8Array) {
+ const s = new Uint8Array(32)
+ this.coreHsalsa20(s, n, k, this.sigma)
+ const sn = new Uint8Array(8)
+ for (var i = 0; i < 8; i++) {
+ sn[i] = n[i+16]
+ }
+ return this.cryptoStreamSalsa20Xor(c, cpos, m, mpos, d, sn, s)
+ }
+
+ checkLengths(k: Uint8Array, n: Uint8Array) {
+ if (k.length !== 32) {
+ throw new Error('bad key size')
+ }
+ if (n.length !== 24) {
+ throw new Error('bad nonce size')
+ }
+ }
+
+ checkBoxLengths(pk: Uint8Array, sk: Uint8Array) {
+ if (pk.length !== 32) {
+ throw new Error('bad public key size')
+ }
+ if (sk.length !== 32) {
+ throw new Error('bad secret key size')
+ }
+ }
+
+ checkArrayTypes(...params: any) {
+ for (let i = 0; i < params.length; i++) {
+ if (!(params[i] instanceof Uint8Array)) {
+ throw new TypeError('unexpected type, use Uint8Array')
+ }
+ }
+ }
+
+ secretbox(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array) {
+ this.checkArrayTypes(msg, nonce, key)
+ this.checkLengths(key, nonce)
+ const m = new Uint8Array(32 + msg.length)
+ const c = new Uint8Array(m.length)
+ for (let i = 0; i < msg.length; i++) {
+ m[i + 32] = msg[i]
+ }
+ this.cryptoSecretbox(c, m, m.length, nonce, key)
+ return c.subarray(16)
+ }
+
+ secretboxOpen(box: Uint8Array, nonce: Uint8Array, key: Uint8Array) {
+ this.checkArrayTypes(box, nonce, key)
+ this.checkLengths(key, nonce)
+ const c = new Uint8Array(16 + box.length)
+ const m = new Uint8Array(c.length)
+ for (let i = 0; i < box.length; i++) {
+ c[i+16] = box[i]
+ }
+ if (c.length < 32) {
+ return null
+ }
+ if (this.cryptoSecretboxOpen(m, c, c.length, nonce, key) !== 0) {
+ return null
+ }
+ return m.subarray(32)
+ }
+
+ box(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array) {
+ const k = this.boxBefore(publicKey, secretKey)
+ return this.secretbox(msg, nonce, k)
+ }
+
+ boxOpen(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array) {
+ const k = this.boxBefore(publicKey, secretKey)
+ return this.secretboxOpen(msg, nonce, k)
+ }
+
+ boxBefore(publicKey: Uint8Array, secretKey: Uint8Array) {
+ this.checkArrayTypes(publicKey, secretKey)
+ this.checkBoxLengths(publicKey, secretKey)
+ const k = new Uint8Array(32)
+ this.cryptoBoxBeforenm(k, publicKey, secretKey)
+ return k
+ }
+
/**
* Generate the common key as the produkt of sk1 * pk2
* @param {Uint8Array} sk A 32 byte secret key of pair 1
}
}
+ /**
+ * Converts a ed25519 public key to Curve25519 to be used in
+ * Diffie-Hellman key exchange
+ */
+ convertEd25519PublicKeyToCurve25519(pk: Uint8Array) {
+ const z = new Uint8Array(32)
+ const q = [this.gf(), this.gf(), this.gf(), this.gf()]
+ const a = this.gf()
+ const b = this.gf()
+
+ if (this.unpackNeg(q, pk)) {
+ return null
+ }
+
+ const y = q[1]
+
+ this.A(a, this.gf1, y)
+ this.Z(b, this.gf1, y)
+ this.inv25519(b, b)
+ this.M(a, a, b)
+
+ this.pack25519(z, a)
+
+ return z
+ }
+
+ /**
+ * Converts a ed25519 secret key to Curve25519 to be used in
+ * Diffie-Hellman key exchange
+ */
+ convertEd25519SecretKeyToCurve25519(sk: Uint8Array) {
+ const d = new Uint8Array(64)
+ const o = new Uint8Array(32)
+ let i
+
+ this.cryptoHash(d, sk, 32)
+ d[0] &= 248
+ d[31] &= 127
+ d[31] |= 64
+
+ for (i = 0; i < 32; i++) {
+ o[i] = d[i]
+ }
+
+ for (i = 0; i < 64; i++) {
+ d[i] = 0
+ }
+
+ return o
+ }
+
+ cryptoHash(out: Uint8Array, m: Uint8Array, n: number): number {
+ const input = new Uint8Array(n)
+ for (let i = 0; i < n; ++i) {
+ input[i] = m[i]
+ }
+
+ const hash = blake2b(input)
+ for (let i = 0; i < 64; ++i) {
+ out[i] = hash[i]
+ }
+
+ return 0
+ }
+
}
* @param {Uint8Array} rh Second array of bytes
* @return {Boolean} True if the arrays are equal (length and content), false otherwise
*/
- static compare(lh: Uint8Array, rh: Uint8Array): boolean {
+ static compare = (lh: Uint8Array, rh: Uint8Array): boolean => {
if (lh.length !== rh.length) {
return false
}
{
"name": "nanocurrency-web",
- "version": "1.3.6",
+ "version": "1.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
}
},
"@types/estree": {
- "version": "0.0.50",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz",
- "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
+ "version": "0.0.51",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"dev": true
},
"@types/json-schema": {
- "version": "7.0.9",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
- "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"@types/node": {
- "version": "17.0.14",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz",
- "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==",
+ "version": "17.0.25",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz",
+ "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==",
"dev": true
},
"@ungap/promise-all-settled": {
"dev": true
},
"blakejs": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz",
- "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg=="
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
+ "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ=="
},
"brace-expansion": {
"version": "1.1.11",
"dev": true
},
"browserslist": {
- "version": "4.19.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
- "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
+ "version": "4.20.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
+ "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30001286",
- "electron-to-chromium": "^1.4.17",
+ "caniuse-lite": "^1.0.30001317",
+ "electron-to-chromium": "^1.4.84",
"escalade": "^3.1.1",
- "node-releases": "^2.0.1",
+ "node-releases": "^2.0.2",
"picocolors": "^1.0.0"
}
},
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "byte-base64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/byte-base64/-/byte-base64-1.1.0.tgz",
+ "integrity": "sha512-56cXelkJrVMdCY9V/3RfDxTh4VfMFCQ5km7B7GkIGfo4bcPL9aACyJLB0Ms3Ezu5rsHmLB2suis96z4fLM03DA=="
+ },
"camelcase": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30001307",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001307.tgz",
- "integrity": "sha512-+MXEMczJ4FuxJAUp0jvAl6Df0NI/OfW1RWEE61eSmzS7hw6lz4IKutbhbXendwq8BljfFuHtu26VWsg4afQ7Ng==",
+ "version": "1.0.30001332",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz",
+ "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==",
"dev": true
},
"chai": {
"dev": true
},
"electron-to-chromium": {
- "version": "1.4.64",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.64.tgz",
- "integrity": "sha512-8mec/99xgLUZCIZZq3wt61Tpxg55jnOSpxGYapE/1Ma9MpFEYYaz4QNYm0CM1rrnCo7i3FRHhbaWjeCLsveGjQ==",
+ "version": "1.4.118",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz",
+ "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==",
"dev": true
},
"emoji-regex": {
"dev": true
},
"enhanced-resolve": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
- "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
+ "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.4",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
+ },
+ "dependencies": {
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
}
},
"glob-parent": {
"dev": true
},
"graceful-fs": {
- "version": "4.2.9",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
- "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"growl": {
"dev": true
},
"jest-worker": {
- "version": "27.4.6",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz",
- "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==",
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"requires": {
"@types/node": "*",
"dev": true
},
"loader-runner": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
- "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
"dev": true
},
"locate-path": {
"dev": true
},
"micromatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
- "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": {
- "braces": "^3.0.1",
- "picomatch": "^2.2.3"
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
}
},
"mime-db": {
- "version": "1.51.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
- "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
- "version": "2.1.34",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
- "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
- "mime-db": "1.51.0"
+ "mime-db": "1.52.0"
}
},
"mimic-fn": {
"dev": true
},
"minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz",
+ "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"mocha": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz",
- "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==",
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz",
+ "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==",
"dev": true,
"requires": {
"@ungap/promise-all-settled": "1.1.2",
"he": "1.2.0",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
- "minimatch": "3.0.4",
+ "minimatch": "4.2.1",
"ms": "2.1.3",
- "nanoid": "3.2.0",
+ "nanoid": "3.3.1",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"dev": true
},
"nanoid": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
- "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"dev": true
},
"neo-async": {
"dev": true
},
"node-releases": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
- "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz",
+ "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==",
"dev": true
},
"normalize-path": {
}
},
"semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+ "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
"dev": true
},
"terser": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
- "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
+ "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
"dev": true,
"requires": {
+ "acorn": "^8.5.0",
"commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20"
}
},
"ts-loader": {
- "version": "9.2.6",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.6.tgz",
- "integrity": "sha512-QMTC4UFzHmu9wU2VHZEmWWE9cUajjfcdcws+Gh7FhiO+Dy0RnR1bNz0YCHqhI0yRowCE9arVnNxYHqELOy9Hjw==",
+ "version": "9.2.8",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.8.tgz",
+ "integrity": "sha512-gxSak7IHUuRtwKf3FIPSW1VpZcqF9+MBrHOvBp9cjHh+525SjtCIJKVGjRKIAfxBwDGDGCFF00rTfzB1quxdSw==",
"dev": true,
"requires": {
"chalk": "^4.1.0",
"dev": true
},
"typescript": {
- "version": "4.5.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
- "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
+ "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
"dev": true
},
"uri-js": {
}
},
"webpack": {
- "version": "5.68.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.68.0.tgz",
- "integrity": "sha512-zUcqaUO0772UuuW2bzaES2Zjlm/y3kRBQDVFVCge+s2Y8mwuUTdperGaAv65/NtRL/1zanpSJOq/MD8u61vo6g==",
+ "version": "5.72.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.0.tgz",
+ "integrity": "sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==",
"dev": true,
"requires": {
- "@types/eslint-scope": "^3.7.0",
- "@types/estree": "^0.0.50",
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.8.3",
+ "enhanced-resolve": "^5.9.2",
"es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
{
"name": "nanocurrency-web",
- "version": "1.3.6",
+ "version": "1.4.0",
"description": "Toolkit for Nano cryptocurrency client side offline integrations",
"author": "Miro Metsänheimo <miro@metsanheimo.fi>",
"license": "MIT",
"crypto",
"wallet",
"block",
- "sign"
+ "sign",
+ "encrypt",
+ "decrypt"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
},
"dependencies": {
"bignumber.js": "9.0.2",
- "blakejs": "1.1.1",
+ "blakejs": "1.2.1",
+ "byte-base64": "1.1.0",
"crypto-js": "3.1.9-1"
},
"devDependencies": {
"chai": "4.3.6",
- "mocha": "9.2.0",
- "ts-loader": "9.2.6",
- "typescript": "4.5.5",
- "webpack": "5.68.0",
+ "mocha": "9.2.2",
+ "ts-loader": "9.2.8",
+ "typescript": "4.6.3",
+ "webpack": "5.72.0",
"webpack-cli": "4.9.2"
}
}
'use strict'
const expect = require('chai').expect
-const { wallet, block, tools } = require('../dist/index')
+const { wallet, block, tools, box } = require('../dist/index')
// WARNING: Do not send any funds to the test vectors below
describe('generate wallet test', () => {
describe('Signer tests', () => {
- let testWallet;
-
before(() => {
- this.testWallet = wallet.generate();
+ this.testWallet = wallet.generate()
})
// Private key: 3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143
})
})
+
+describe('Box tests', () => {
+
+ before(() => {
+ this.message = 'The quick brown fox jumps over the lazy dog'
+ this.bob = wallet.generate()
+ this.alice = wallet.generate()
+ })
+
+ it('should encrypt and decrypt a message', () => {
+ const encrypted = box.encrypt(this.message, this.alice.accounts[0].address, this.bob.accounts[0].privateKey)
+ const encrypted2 = box.encrypt(this.message, this.alice.accounts[0].address, this.bob.accounts[0].privateKey)
+ const encrypted3 = box.encrypt(this.message + 'asd', this.alice.accounts[0].address, this.bob.accounts[0].privateKey)
+
+ // Just to be safe
+ expect(this.message).to.not.equal(encrypted)
+ expect(encrypted).to.not.equal(encrypted2)
+ expect(encrypted).to.not.equal(encrypted3)
+
+ const decrypted = box.decrypt(encrypted, this.bob.accounts[0].address, this.alice.accounts[0].privateKey)
+ expect(this.message).to.equal(decrypted)
+ })
+
+ it('should fail to decrypt with wrong public key in encryption', () => {
+ // Encrypt with wrong public key
+ const aliceAccounts = wallet.accounts(this.alice.seed, 1, 2)
+ const encrypted = box.encrypt(this.message, aliceAccounts[0].address, this.bob.accounts[0].privateKey)
+ expect(() => box.decrypt(encrypted, this.bob.accounts[0].address, this.alice.accounts[0].privateKey)).to.throw()
+ })
+
+ it('should fail to decrypt with wrong public key in decryption', () => {
+ // Decrypt with wrong public key
+ const bobAccounts = wallet.accounts(this.bob.seed, 1, 2)
+ const encrypted = box.encrypt(this.message, this.alice.accounts[0].address, this.bob.accounts[0].privateKey)
+ expect(() => box.decrypt(encrypted, bobAccounts[0].address, this.alice.accounts[0].privateKey)).to.throw()
+ })
+
+ it('should fail to decrypt with wrong private key in encryption', () => {
+ // Encrypt with wrong public key
+ const bobAccounts = wallet.accounts(this.bob.seed, 1, 2)
+ const encrypted = box.encrypt(this.message, this.alice.accounts[0].address, bobAccounts[0].privateKey)
+ expect(() => box.decrypt(encrypted, this.bob.accounts[0].address, this.alice.accounts[0].privateKey)).to.throw()
+ })
+
+ it('should fail to decrypt with wrong private key in decryption', () => {
+ // Encrypt with wrong public key
+ const aliceAccounts = wallet.accounts(this.alice.seed, 1, 2)
+ const encrypted = box.encrypt(this.message, this.alice.accounts[0].address, this.bob.accounts[0].privateKey)
+ expect(() => box.decrypt(encrypted, this.bob.accounts[0].address, aliceAccounts[0].privateKey)).to.throw()
+ })
+
+})
\ No newline at end of file