commit
6fefc4821b
18 changed files with 1138 additions and 265 deletions
21
README.md
21
README.md
|
|
@ -33,7 +33,7 @@ npm install nanocurrency-web
|
|||
### 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>
|
||||
|
|
@ -114,7 +114,7 @@ If the account hasn't been opened yet (this is the first block), you will need t
|
|||
```javascript
|
||||
import { block } from 'nanocurrency-web'
|
||||
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
|
||||
const data = {
|
||||
// Current balance from account info
|
||||
walletBalanceRaw: '5618869000000000000000000000000',
|
||||
|
|
@ -147,7 +147,7 @@ const signedBlock = block.send(data, privateKey)
|
|||
```javascript
|
||||
import { block } from 'nanocurrency-web'
|
||||
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
|
||||
const data = {
|
||||
// Your current balance in RAW from account info
|
||||
walletBalanceRaw: '18618869000000000000000000000000',
|
||||
|
|
@ -180,7 +180,7 @@ const signedBlock = block.receive(data, privateKey)
|
|||
```javascript
|
||||
import { block } from 'nanocurrency-web'
|
||||
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
|
||||
const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
|
||||
const data = {
|
||||
// Your current balance, from account info
|
||||
walletBalanceRaw: '3000000000000000000000000000000',
|
||||
|
|
@ -270,6 +270,19 @@ const valid = tools.validateAddress('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94
|
|||
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.
|
||||
|
|
|
|||
95
index.ts
95
index.ts
|
|
@ -3,16 +3,12 @@ import BigNumber from 'bignumber.js'
|
|||
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 = {
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +34,7 @@ const wallet = {
|
|||
* @returns {Wallet} The wallet
|
||||
*/
|
||||
generate: (entropy?: string, seedPassword?: string): Wallet => {
|
||||
return generator.generateWallet(entropy, seedPassword)
|
||||
return AddressGenerator.generateWallet(entropy, seedPassword)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -57,7 +53,7 @@ const wallet = {
|
|||
* @returns {Wallet} The wallet
|
||||
*/
|
||||
generateLegacy: (seed?: string): Wallet => {
|
||||
return generator.generateLegacyWallet(seed)
|
||||
return AddressGenerator.generateLegacyWallet(seed)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -76,7 +72,7 @@ const wallet = {
|
|||
* @returns {Wallet} The wallet
|
||||
*/
|
||||
fromMnemonic: (mnemonic: string, seedPassword?: string): Wallet => {
|
||||
return importer.fromMnemonic(mnemonic, seedPassword)
|
||||
return AddressImporter.fromMnemonic(mnemonic, seedPassword)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +89,7 @@ const wallet = {
|
|||
* @returns {Wallet} The wallet
|
||||
*/
|
||||
fromLegacyMnemonic: (mnemonic: string): Wallet => {
|
||||
return importer.fromLegacyMnemonic(mnemonic)
|
||||
return AddressImporter.fromLegacyMnemonic(mnemonic)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -110,7 +106,7 @@ const wallet = {
|
|||
* @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)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -127,7 +123,7 @@ const wallet = {
|
|||
* @returns {Wallet} The wallet
|
||||
*/
|
||||
fromLegacySeed: (seed: string): Wallet => {
|
||||
return importer.fromLegacySeed(seed)
|
||||
return AddressImporter.fromLegacySeed(seed)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +139,7 @@ const wallet = {
|
|||
* @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
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -158,12 +154,11 @@ const wallet = {
|
|||
* @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 = {
|
||||
|
||||
/**
|
||||
|
|
@ -185,7 +180,7 @@ const block = {
|
|||
* @returns {SignedBlock} the signed block
|
||||
*/
|
||||
send: (data: SendBlock, privateKey: string): SignedBlock => {
|
||||
return blockSigner.send(data, privateKey)
|
||||
return BlockSigner.send(data, privateKey)
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -208,7 +203,7 @@ const block = {
|
|||
* @returns {SignedBlock} the signed block
|
||||
*/
|
||||
receive: (data: ReceiveBlock, privateKey: string): SignedBlock => {
|
||||
return blockSigner.receive(data, privateKey)
|
||||
return BlockSigner.receive(data, privateKey)
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -236,7 +231,7 @@ const block = {
|
|||
toAddress: 'nano_1111111111111111111111111111111111111111111111111111hifc8npp', // Burn address
|
||||
}
|
||||
|
||||
return blockSigner.send(block, privateKey)
|
||||
return BlockSigner.send(block, privateKey)
|
||||
},
|
||||
|
||||
}
|
||||
|
|
@ -266,7 +261,7 @@ const tools = {
|
|||
*/
|
||||
sign: (privateKey: string, ...input: string[]): string => {
|
||||
const data = input.map(Convert.stringToHex)
|
||||
return signer.sign(privateKey, ...data)
|
||||
return Signer.sign(privateKey, ...data)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -279,7 +274,7 @@ const tools = {
|
|||
*/
|
||||
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)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -291,11 +286,11 @@ const tools = {
|
|||
*/
|
||||
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)
|
||||
},
|
||||
|
|
@ -307,7 +302,7 @@ const tools = {
|
|||
* @returns {boolean} valid or not
|
||||
*/
|
||||
validateAddress: (input: string): boolean => {
|
||||
return nanoAddress.validateNanoAddress(input)
|
||||
return NanoAddress.validateNanoAddress(input)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -317,7 +312,7 @@ const tools = {
|
|||
* @returns {boolean} valid or not
|
||||
*/
|
||||
validateMnemonic: (input: string): boolean => {
|
||||
return importer.validateMnemonic(input)
|
||||
return AddressImporter.validateMnemonic(input)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -327,11 +322,7 @@ const tools = {
|
|||
* @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)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -341,7 +332,7 @@ const tools = {
|
|||
* @returns {string} the nano address
|
||||
*/
|
||||
publicKeyToAddress: (input: string): string => {
|
||||
return nanoAddress.deriveAddress(input)
|
||||
return NanoAddress.deriveAddress(input)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -352,16 +343,56 @@ const tools = {
|
|||
*/
|
||||
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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ export default class AddressGenerator {
|
|||
* @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,
|
||||
|
|
@ -22,14 +20,10 @@ export default class AddressGenerator {
|
|||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,12 @@ export default class AddressImporter {
|
|||
* @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 {
|
||||
|
|
@ -36,13 +35,12 @@ export default class AddressImporter {
|
|||
* @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)
|
||||
}
|
||||
|
||||
|
|
@ -51,9 +49,8 @@ export default class AddressImporter {
|
|||
*
|
||||
* @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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,7 +61,7 @@ export default class AddressImporter {
|
|||
* @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')
|
||||
}
|
||||
|
|
@ -90,7 +87,7 @@ export default class AddressImporter {
|
|||
* @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')
|
||||
}
|
||||
|
|
@ -100,7 +97,7 @@ export default class AddressImporter {
|
|||
|
||||
const accounts = this.legacyAccounts(seed, from, to)
|
||||
return {
|
||||
mnemonic: mnemonic || new Bip39Mnemonic().deriveMnemonic(seed),
|
||||
mnemonic: mnemonic || Bip39Mnemonic.deriveMnemonic(seed),
|
||||
seed,
|
||||
accounts,
|
||||
}
|
||||
|
|
@ -113,19 +110,13 @@ export default class AddressImporter {
|
|||
* @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,
|
||||
|
|
@ -144,18 +135,12 @@ export default class AddressImporter {
|
|||
* @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,
|
||||
|
|
|
|||
|
|
@ -8,17 +8,9 @@ const HARDENED_OFFSET = 0x80000000
|
|||
|
||||
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))
|
||||
|
|
@ -29,13 +21,13 @@ export default class Bip32KeyDerivation {
|
|||
)
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -48,7 +40,7 @@ export default class Bip32KeyDerivation {
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -7,19 +7,13 @@ import words from './words'
|
|||
|
||||
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')
|
||||
|
|
@ -34,7 +28,7 @@ export default class Bip39Mnemonic {
|
|||
}
|
||||
|
||||
const mnemonic = this.deriveMnemonic(entropy)
|
||||
const seed = this.mnemonicToSeed(mnemonic)
|
||||
const seed = this.mnemonicToSeed(mnemonic, password)
|
||||
|
||||
return {
|
||||
mnemonic,
|
||||
|
|
@ -48,7 +42,7 @@ export default class Bip39Mnemonic {
|
|||
* @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')
|
||||
|
|
@ -70,7 +64,7 @@ export default class Bip39Mnemonic {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -89,7 +83,7 @@ export default class Bip39Mnemonic {
|
|||
* @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
|
||||
|
|
@ -136,7 +130,7 @@ export default class Bip39Mnemonic {
|
|||
*
|
||||
* @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)
|
||||
|
|
@ -159,9 +153,9 @@ export default class Bip39Mnemonic {
|
|||
*
|
||||
* @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,
|
||||
|
|
@ -174,11 +168,11 @@ export default class Bip39Mnemonic {
|
|||
.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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
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'
|
||||
|
|
@ -10,11 +7,7 @@ import Convert from './util/convert'
|
|||
|
||||
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
|
||||
|
|
@ -23,7 +16,7 @@ export default class BlockSigner {
|
|||
* @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')
|
||||
|
|
@ -33,11 +26,11 @@ export default class BlockSigner {
|
|||
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')
|
||||
}
|
||||
|
||||
|
|
@ -58,11 +51,11 @@ export default class BlockSigner {
|
|||
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,
|
||||
|
|
@ -90,7 +83,7 @@ export default class BlockSigner {
|
|||
* @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')
|
||||
|
|
@ -100,15 +93,15 @@ export default class BlockSigner {
|
|||
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')
|
||||
}
|
||||
|
||||
|
|
@ -125,11 +118,11 @@ export default class BlockSigner {
|
|||
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,
|
||||
|
|
|
|||
69
lib/box.ts
Normal file
69
lib/box.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -118,6 +118,24 @@ export default class Ed25519 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -143,7 +161,7 @@ export default class Ed25519 {
|
|||
* @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()]
|
||||
|
||||
|
|
@ -197,7 +215,7 @@ export default class Ed25519 {
|
|||
|
||||
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
|
||||
|
|
@ -211,7 +229,7 @@ export default class Ed25519 {
|
|||
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)
|
||||
|
|
@ -220,7 +238,7 @@ export default class Ed25519 {
|
|||
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++) {
|
||||
|
|
@ -242,20 +260,6 @@ export default class Ed25519 {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import Convert from './util/convert'
|
|||
|
||||
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)
|
||||
|
|
@ -17,7 +17,7 @@ export default class NanoAddress {
|
|||
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
|
||||
|
|
@ -43,7 +43,15 @@ export default class NanoAddress {
|
|||
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
|
||||
|
|
@ -81,7 +89,7 @@ export default class NanoAddress {
|
|||
*
|
||||
* @param {string} address Nano address
|
||||
*/
|
||||
validateNanoAddress = (address: string): boolean => {
|
||||
static validateNanoAddress = (address: string): boolean => {
|
||||
if (address === undefined) {
|
||||
throw Error('Address must be defined.')
|
||||
}
|
||||
|
|
@ -100,7 +108,7 @@ export default class NanoAddress {
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -108,7 +116,7 @@ export default class NanoAddress {
|
|||
return expectedChecksum === actualChecksum
|
||||
}
|
||||
|
||||
nanoAddressToHexString = (addr: string): string => {
|
||||
static nanoAddressToHexString = (addr: string): string => {
|
||||
addr = addr.slice(-60)
|
||||
const isValid = /^[13456789abcdefghijkmnopqrstuwxyz]+$/.test(addr)
|
||||
if (isValid) {
|
||||
|
|
@ -125,7 +133,11 @@ export default class NanoAddress {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default class NanoConverter {
|
|||
* @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) {
|
||||
|
|
|
|||
|
|
@ -7,16 +7,14 @@ import Convert from './util/convert'
|
|||
|
||||
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)
|
||||
|
|
@ -29,11 +27,11 @@ export default class Signer {
|
|||
* @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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -41,7 +39,7 @@ export default class Signer {
|
|||
*
|
||||
* @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)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,21 @@ export default class Convert {
|
|||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
//@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
|
||||
|
|
@ -9,6 +20,9 @@ export default class Curve25519 {
|
|||
I: Int32Array
|
||||
_9: Uint8Array
|
||||
_121665: Int32Array
|
||||
_0: Uint8Array
|
||||
sigma: Uint8Array
|
||||
minusp: Uint32Array
|
||||
|
||||
constructor() {
|
||||
this.gf0 = this.gf()
|
||||
|
|
@ -19,6 +33,9 @@ export default class Curve25519 {
|
|||
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 {
|
||||
|
|
@ -413,6 +430,336 @@ export default class Curve25519 {
|
|||
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)
|
||||
}
|
||||
|
|
@ -612,6 +959,14 @@ export default class Curve25519 {
|
|||
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
|
||||
|
|
@ -671,6 +1026,289 @@ export default class Curve25519 {
|
|||
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
|
||||
|
|
@ -708,4 +1346,69 @@ export default class Curve25519 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default class Util {
|
|||
* @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
|
||||
}
|
||||
|
|
|
|||
179
package-lock.json
generated
179
package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "nanocurrency-web",
|
||||
"version": "1.3.6",
|
||||
"version": "1.4.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
@ -31,21 +31,21 @@
|
|||
}
|
||||
},
|
||||
"@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": {
|
||||
|
|
@ -324,9 +324,9 @@
|
|||
"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",
|
||||
|
|
@ -354,15 +354,15 @@
|
|||
"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"
|
||||
}
|
||||
},
|
||||
|
|
@ -372,6 +372,11 @@
|
|||
"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",
|
||||
|
|
@ -379,9 +384,9 @@
|
|||
"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": {
|
||||
|
|
@ -558,9 +563,9 @@
|
|||
"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": {
|
||||
|
|
@ -570,9 +575,9 @@
|
|||
"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",
|
||||
|
|
@ -751,6 +756,17 @@
|
|||
"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": {
|
||||
|
|
@ -769,9 +785,9 @@
|
|||
"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": {
|
||||
|
|
@ -924,9 +940,9 @@
|
|||
"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": "*",
|
||||
|
|
@ -962,9 +978,9 @@
|
|||
"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": {
|
||||
|
|
@ -1011,28 +1027,28 @@
|
|||
"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": {
|
||||
|
|
@ -1042,18 +1058,18 @@
|
|||
"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",
|
||||
|
|
@ -1069,9 +1085,9 @@
|
|||
"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",
|
||||
|
|
@ -1089,9 +1105,9 @@
|
|||
"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": {
|
||||
|
|
@ -1101,9 +1117,9 @@
|
|||
"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": {
|
||||
|
|
@ -1336,9 +1352,9 @@
|
|||
}
|
||||
},
|
||||
"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"
|
||||
|
|
@ -1453,11 +1469,12 @@
|
|||
"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"
|
||||
|
|
@ -1494,9 +1511,9 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
|
|
@ -1512,9 +1529,9 @@
|
|||
"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": {
|
||||
|
|
@ -1537,13 +1554,13 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
|
|
@ -1551,7 +1568,7 @@
|
|||
"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",
|
||||
|
|
|
|||
17
package.json
17
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"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",
|
||||
|
|
@ -19,7 +19,9 @@
|
|||
"crypto",
|
||||
"wallet",
|
||||
"block",
|
||||
"sign"
|
||||
"sign",
|
||||
"encrypt",
|
||||
"decrypt"
|
||||
],
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
@ -31,15 +33,16 @@
|
|||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
test/test.js
58
test/test.js
|
|
@ -1,7 +1,7 @@
|
|||
'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', () => {
|
||||
|
|
@ -261,10 +261,8 @@ describe('unit conversion tests', () => {
|
|||
|
||||
describe('Signer tests', () => {
|
||||
|
||||
let testWallet;
|
||||
|
||||
before(() => {
|
||||
this.testWallet = wallet.generate();
|
||||
this.testWallet = wallet.generate()
|
||||
})
|
||||
|
||||
// Private key: 3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143
|
||||
|
|
@ -330,3 +328,55 @@ describe('Signer tests', () => {
|
|||
})
|
||||
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue