xdvplatform-wallet

A wallet for documents and tokens

Usage no npm install needed!

<script type="module">
  import xdvplatformWallet from 'https://cdn.skypack.dev/xdvplatform-wallet';
</script>

README

XDV Platform Wallet

npm i xdvplatform-wallet

Features

crypto

  • Supports for JWT, DID, JWE, XmlDsig, CMS (X509)
  • Algorithms included: secp256k1, secp256r1, ed25519, rsa and upcoming bls
  • Keystore, HD Key, DER, JWK and PEM support out of the box
  • Support for output to LD Crypto Suite key pairs for ecdsa and ed25519
  • More features planned like bls and schnorr for multi sig scenarios

API

Wallet

A wallet instance is always required to start working with Wallet API. You configure RxJS subjects to handle password management between UI subscriber and Wallet API.

Creating a wallet with createWallet

    const wallet = new Wallet();
    this.wallet.onRequestPassphraseSubscriber.subscribe(async (i) => {
      this.canUnlock = true;
      this.loading = true;
      if (i.type === 'wallet') {
        this.passphraseSubject.subscribe((passphrase) =>
          this.wallet.onRequestPassphraseWallet.next({ type: 'ui', passphrase })
        );
      } else {
        this.canUnlock = false;
        this.loading = false;
      }
    });

    let { id } = await wallet.createWallet(this.password, null, mnemonic);

Unlocking using open

Now with that id, be sure to save it somewhere, you always query the Wallet API DB to open the wallet. It will ask you for your password if you configure the RxJS Subscribers as defined in the previous step.

 await this.wallet.open(id);

Wallet.generateMnemonic

Creates a new mnemonic

     const mnemonic = Wallet.generateMnemonic();
     const selectedWallet = await Wallet.createHDWallet({ mnemonic, password: '123password' });

deriveChild

Creates a new child HD Wallet

      const child1 = wallet.deriveChild(1, `m/44'/60'/0'/0`);
      const child2 = wallet.deriveChild(2, `m/44'/60'/0'/0`);
      const child3 = wallet.deriveChild(3, `m/44'/60'/0'/0`);

deriveFromPath

Creates a new child HD Wallet

      const child1 = wallet.deriveFromPath(`m/44'/60'/0'/0/1`);
      const child2 = wallet.deriveFromPath(`m/44'/60'/0'/0/2`);
      const child3 = wallet.deriveFromPath(`m/44'/60'/0'/0/3`);

getEd25519

Creates a new Ed25519 curve

    const kp = await wallet.getEd25519();

getP256

Creates a new P256/secp256r1 curve

    const kp = await wallet.getP256();

getES256k

Creates a new secp256k1 curve

    const kp = await wallet.getES256k();

getRSA256Standalone

Creates a new RSA key pair. This key pair is not generated from HD Wallet seed.

    const kp = await wallet.getRSA256Standalone();

getBlsMasterKey

Creates a new BLS key pair. Returns an object.

    const kp = wallet.getBlsMasterKey();
    const deriveValidatorKey1 = await kp.deriveValidatorKeys(1);

KeyConvert

Handles key pair convertion from / to PEM, DER or JWK. Some algorithm also returns JSON-LD compatible exports.

KeyConvert.getX509RSA

Converts a RSA key pair. Available exports: JWK, PEM and LD.

  const key = await Wallet.getRSA256Standalone();
        const issuer: X509Info = {
          stateOrProvinceName: 'PA',
          organizationName: 'RM',
          organizationalUnitName: 'Engineering',
          commonName: 'Rogelio Morrell',
          countryName: 'Panama',
          localityName: 'Panama'
        };
        const { jwk, pem } = await KeyConvert.getX509RSA(key);
        const jwt = await JWTService.sign(pem, {
          testing: 'testing'
        }, {
          iat: (new Date(2020, 10, 10)).getTime(),
          iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
          sub: 'document',
          aud: 'receptor',
          nbf: (new Date(2020, 10, 10)).getTime(),
        });

KeyConvert.getP256

Converts a P256 key pair. Available exports: JWK, DER, PEM and LD.


    const mnemonic = Wallet.generateMnemonic();
    const opts = { mnemonic, password: '123password' };
    const keystore = await Wallet.createHDWallet(opts);
    expect(JSON.parse(keystore).version).equal(3);

    const wallet = await Wallet.unlock(keystore, opts.password);
    const key = wallet.getP256();

    const pem = (await KeyConvert.getP256(key)).pem;
    const jwt = await JWTService.sign(pem, {
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });


KeyConvert.getES256K

Converts a ES256K key pair. Available exports: JWK, DER, PEM and LD.

    const wallet = await Wallet.unlock(keystore, opts.password);
    const pem = (await KeyConvert.getES256K(wallet.getES256K())).pem;

KeyConvert.getRSA

Converts a RSA key pair. Available exports: JWK and PEM as pemAsPrivate and pemAsPublic.

   const key = await Wallet.getRSA256Standalone();
    const pem = (await KeyConvert.getRSA(key)).pemAsPrivate;

KeyConvert.getEd25519

Converts a Ed25519 key pair. Available exports: DER and PEM.

    const mnemonic = Wallet.generateMnemonic();
    const opts = { mnemonic, password: '123password' };
    const keystore = await Wallet.createHDWallet(opts);

    const wallet = await Wallet.unlock(keystore, opts.password);
    const key = wallet.getEd25519();

    const pem = (await KeyConvert.getEd25519(key)).pem;
    const jwt = await JWTService.sign(pem, {
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });

KeyConvert.createLinkedDataJsonFormat

Creates a Linked Data suite json format from a LD Suite export.

    const mnemonic = Wallet.generateMnemonic();
    const opts = { mnemonic, password: '123password' };
    const keystore = await Wallet.createHDWallet(opts);

    const wallet = await Wallet.unlock(keystore, opts.password);

    const kp = wallet.getP256();
    const kpJwk = await KeyConvert.getP256(kp);

    // Create LD Crypto Suite - p256 / Sepc256r1
    const ldCrypto = await KeyConvert
        .createLinkedDataJsonFormat(LDCryptoTypes.Sepc256r1, kpJwk.ldSuite);

    // Create IPFS key storage lock
    const session = await xdvMethod.createIpldSession(kpJwk.pem);

    // Create DID document with an did-ipid based issuer
    const did = await DIDDocumentBuilder
        .createDID({
        issuer: session.key,
        verificationKeys: [ldCrypto.toPublicKey()],
        authenticationKeys: [ldCrypto.toAuthorizationKey()]
        });

    // Signing
    const signed = await  JWTService.sign(kpJwk.pem, {
        ...did,
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });
    

JWTService

Signs and verifies JWT.

JWTService.decodeWithSignature

Decodes a JWT

    // Sign as JWT
    let signed = await JWTService.sign(kpJwk.pem, {
        ...did,
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });

    // Decoded
    const decoded = await JWTService.decodeWithSignature(signed);
    expect(!!decoded.signature).equal(true)
    expect(!!decoded.data).equal(true)
    expect(!!decoded.header).equal(true)
    expect(!!decoded.payload).equal(true)

JWTService.sign

Signs a JWT

    // Unlock wallet
    const wallet = await Wallet.unlock(keystore, opts.password);

    // Create key
    const kp = wallet.getP256();
    const kpJwk = await KeyConvert.getP256(kp);

    // Create LD Crypto Suite - p256 / Sepc256r1
    const ldCrypto = await KeyConvert
        .createLinkedDataJsonFormat(LDCryptoTypes.Sepc256r1, kpJwk.ldSuite);

    // Create DID id from PEM keypair and store it variable  
    const session = await xdvMethod.createIpldSession(kpJwk.pem);

    // Set DID
    localStorage['recentlyStoreDID'] = session.key;

    // Create DID document with an did-ipid based issuer
    let did = await DIDDocumentBuilder
        .createDID({
        issuer: session.key,
        verificationKeys: [ldCrypto.toPublicKey()],
        authenticationKeys: [ldCrypto.toAuthorizationKey()]
        });

    // Sign as JWT
    let signed = await JWTService.sign(kpJwk.pem, {
        ...did,
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });

JWTService.verify

Verifies a JWT

    // get document with a CID and DID
    const resolver = await xdvMethod.getResolver(localStorage['recentlyStoreCID']);
    const doc = await resolver.xdv(localStorage['recentlyStoreDID']);

    // unlock wallet
    let wallet = await Wallet.unlock(localStorage['ks'], localStorage['ps']);

    // create new key pairs
    const kp = wallet.getP256();
    const kpJwk = await KeyConvert.getP256(kp);


    // decrypt
    const obj = await JOSEService.decrypt(kpJwk.jwk, doc.encrypted);
    const verified = await JWTService.verify(kpJwk.pem,(<Buffer> obj.plaintext).toString('utf8'), 'receptor');

JOSEService

Encrypts and decrypts JWT.

JOSEService.encrypt

Encrypts a string or buffer payload

    // Unlock wallet
    const wallet = await Wallet.unlock(keystore, opts.password);

    // Create key
    const kp = wallet.getP256();
    const kpJwk = await KeyConvert.getP256(kp);

    // Create LD Crypto Suite - p256 / Sepc256r1
    const ldCrypto = await KeyConvert
        .createLinkedDataJsonFormat(LDCryptoTypes.Sepc256r1, kpJwk.ldSuite);

    // Create DID id from PEM keypair and store it variable  
    const session = await xdvMethod.createIpldSession(kpJwk.pem);

    // Set DID
    localStorage['recentlyStoreDID'] = session.key;

    // Create DID document with an did-ipid based issuer
    let did = await DIDDocumentBuilder
        .createDID({
        issuer: session.key,
        verificationKeys: [ldCrypto.toPublicKey()],
        authenticationKeys: [ldCrypto.toAuthorizationKey()]
        });

    // Sign as JWT
    let signed = await JWTService.sign(kpJwk.pem, {
        ...did,
        testing: 'testing'
    }, {
        iat: (new Date(2020, 10, 10)).getTime(),
        iss: '0x4198258023eD0D6fae5DBCF3Af2aeDaaA363571F',
        sub: 'document',
        aud: 'receptor',
        nbf: (new Date(2020, 10, 10)).getTime(),
    });

    // Encrypt JWT
    const encrypted = await JOSEService.encrypt(kpJwk.jwk, signed);

JOSEService.decrypt

Decrypts a cipher

    // get document with a CID and DID
    const resolver = await xdvMethod.getResolver(localStorage['recentlyStoreCID']);
    const doc = await resolver.xdv(localStorage['recentlyStoreDID']);

    // unlock wallet
    let wallet = await Wallet.unlock(localStorage['ks'], localStorage['ps']);

    // create new key pairs
    const kp = wallet.getP256();
    const kpJwk = await KeyConvert.getP256(kp);


    // decrypt
    const obj = await JOSEService.decrypt(kpJwk.jwk, doc.encrypted);
    const verified = await JWTService.verify(kpJwk.pem,(<Buffer> obj.plaintext).toString('utf8'), 'receptor');


X509

X509.createSelfSignedCertificateFromRSA

Creates a self sign certificate from a RSA key pair

    const issuer: X509Info = {
        stateOrProvinceName: 'PA',
        organizationName: 'RM',
        organizationalUnitName: 'Engineering',
        commonName: 'Rogelio Morrell',
        countryName: 'Panama',
        localityName: 'Panama'
    };
    const rsaKey = await Wallet.getRSA256Standalone();

    const rsaKeyExports = await KeyConvert.getX509RSA(rsaKey);
    const selfSignedCert = X509.createSelfSignedCertificateFromRSA(
      rsaKeyExports.pemAsPrivate, rsaKeyExports.pemAsPublic, issuer);
    const signedDocuments = await XmlDsig.signFEDocument(rsaKeyExports.pem, selfSignedCert, latestFEDocument);

XmlDsig (FE specific)

Signs FE XML Documents

XmlDsig.signFEDocument

Signs a Factura Electronica de Panama document

    const issuer: X509Info = {
        stateOrProvinceName: 'PA',
        organizationName: 'RM',
        organizationalUnitName: 'Engineering',
        commonName: 'Rogelio Morrell',
        countryName: 'Panama',
        localityName: 'Panama'
    };
    const rsaKey = await Wallet.getRSA256Standalone();

    const rsaKeyExports = await KeyConvert.getX509RSA(rsaKey);
    const selfSignedCert = X509.createSelfSignedCertificateFromRSA(
      rsaKeyExports.pemAsPrivate, rsaKeyExports.pemAsPublic, issuer);
    const signedDocuments = await XmlDsig.signFEDocument(rsaKeyExports.pemAsPrivate, selfSignedCert, latestFEDocument);

CMSSigner

Signs CMS Documents

CMSSigner.sign

Signs a CMS document

    const issuer: X509Info = {
        stateOrProvinceName: 'PA',
        organizationName: 'RM',
        organizationalUnitName: 'Engineering',
        commonName: 'Rogelio Morrell',
        countryName: 'Panama',
        localityName: 'Panama'
    };
    const rsaKey = await Wallet.getRSA256Standalone();

    const rsaKeyExports = await KeyConvert.getX509RSA(rsaKey);
    const selfSignedCert = X509.createSelfSignedCertificateFromRSA(
      rsaKeyExports.pemAsPrivate, rsaKeyExports.pemAsPublic, issuer);
    const res = CMSSigner.sign(selfSignedCert,
        rsaKeyExports.pemAsPrivate,
        fs.readFileSync(__dirname + '/fixtures/cms.pdf'));

did

  • Supports for basic DID JSON schemas
  • DID JWT signing and validation using xdvplatform-tools/crypto API
  • Includes did-method-xdv with resolver

API

Wallet.createHDWallet

Creates a new random HD Wallet

  const keystore = await Wallet.createHDWallet({ password: 'password123' });

fe

  • Includes most of the API specifications for Factura Electronica DGI Panama up to September 2018 specification
  • Planned features: CUFE generation and QR generation

dv

  • Digito Verificador for RUC Natural Person based on algorithm from Registro Publico de Panama.
  • Client that calls a read only function in a Solidity smart contract
Usage
        const nodeUrl = '...node url';
        const isMainnet = true;
        const account = '...your ethereum accpunt';

        const dv = new DV(account, nodeUrl, isMainnet);
        
        // initialize contracts
        await dv.initialize();
        
        // call calculate
        // input is 
        // { type, segments 1 to  4}
        // For more information, visit https://dv.auth2factor.com
        const resp = await dv.calculate(CedulaInputTypes.N,
            [0, 8],
            [0, 0],
            [7, 1, 3],
            [2, 2, 3]);

        expect(resp).equals('11');

@molekilla, Rogelio Morrell C. Copyright 2020-2021

MIT License