unicrypto
    TypeScript icon, indicating that this package has built-in type declarations

    1.11.0 • Public • Published

    unicrypto

    Minimalistic Javascript library required to perform basic operations with Universa smart contracts and other objects.

    Supports:

    Installation

    Node.js

    For usage in an existing Node.js project, add it to your dependencies:

    $ npm install unicrypto
    

    or with yarn:

    $ yarn add unicrypto
    

    And use it wherever you need it.

    import { encode64 } from 'unicrypto';

    Web

    In root folder of package run

    npm install
    npm run build

    In folder dist there will be crypto.js, crypto.wasm. Also there will be *.LICENSE files.

    Copy files to your scripts folder and set them in order. Also, wait for initalization:

    <script src="path/to/crypto.js"></script>
    
    <script>
      async function main() {
        // Example of key generation
        const options = { strength: 2048 };
        const priv = await Unicrypto.PrivateKey.generate(options);
        console.log(priv);
      }
    
      main();
    </script>

    Usage

    CryptoWorker

    You can run your code in a worker process, using a CryptoWorker:

    import { CryptoWorker } from 'unicrypto';
    
    const encryptedData; // Uint8Array
    const privateKey; // PrivateKey instance
    
    // Define code to run in a worker within a separate function. async/await is not supported, but you can use pure Promise interface
    function workerCode(resolve, reject) {
      // Here's Unicrypto instance
      const { PrivateKey } = this.Unicrypto;
    
      // All data you want to pass is stored in this.data. It should be serializable,
      // according to worker's data exchange requirements
      const { packedKey, encryptedData, decryptOptions } = this.data;
    
      // output data should be serializable
      PrivateKey.unpack(packedKey)
        .then(key => key.decrypt(encryptedData, decryptOptions))
        .then(resolve);
    }
    
    // Pass your data as second parameter, all data should be serializable
    const decryptedData = await CryptoWorker.run(
      workerCode,
      { data: { packedKey: await privateKey.pack(), encryptedData, decryptOptions } }
    );

    You can also pass context independent helper functions:

    import { CryptoWorker } from 'unicrypto';
    
    const encryptedData; // Uint8Array
    const privateKey; // PrivateKey instance
    
    // Helper function example. async/await is not supported, but you can use pure Promise interface
    function parseNumber(numberString) {
      return parseFloat(numberString);
    }
    
    // Define code to run in a worker within a separate function. async/await is not supported, but you can use pure Promise interface
    function workerCode(resolve, reject) {
      // Helper function is available in context
      const { parseNumber } = this;
    
      // All data you want to pass is stored in this.data. It should be serializable,
      // according to worker's data exchange requirements
      const { stringNumber } = this.data;
    
      const number = parseNumber(stringNumber);
    
      resolve(number);
    }
    
    // Pass helper functions as dictionary
    const decryptedData = await CryptoWorker.run(
      workerCode,
      {
        data: { stringNumber: '12323.12' },
        functions: {
          parseNumber
        }
      }
    );

    Signed record

    Pack data to signed record (Uint8Array) with key:

    import { SignedRecord, decode64, PrivateKey } from 'unicrypto';
    
    const payload = { ab: "cd" };
    const nonce = decode64("abc");
    const key = await PrivateKey.unpack(privateKeyPacked);
    
    const recordBinary = await SignedRecord.packWithKey(key, payload, nonce); // Uint8Array

    Unpack signed record:

    import { SignedRecord, decode64, PrivateKey } from 'unicrypto';
    
    const payload = { ab: "cd" };
    const nonce = decode64("abc");
    const key = await PrivateKey.unpack(privateKeyPacked);
    
    const recordBinary = await SignedRecord.packWithKey(key, payload, nonce); // Uint8Array
    
    const record = await SignedRecord.unpack(recordBinary);
    
    record.recordType === SignedRecord.RECORD_WITH_KEY; // true
    record.nonce // nonce
    record.payload // payload
    record.key // PublicKey

    Misc

    Random byte array for given length

    import { randomBytes } from 'unicrypto';
    
    const bytes16 = randomBytes(16); // Buffer

    HashId for binary data

    import { hashId } from 'unicrypto';
    
    const id = await hashId(decode64("abc")); // Uint8Array

    CRC32

    import { crc32 } from 'unicrypto';
    
    const digest = crc32(decode64("abc")); // Uint8Array

    Converters

    Convert byte array to hex string and back

        import { bytesToHex, hexToBytes } from 'unicrypto';
    
        const hexString = bytesToHex(uint8arr);  // String
        const bytesArray = hexToBytes(hexString); // Uint8Array

    Convert plain text to bytes and back

      import { textToBytes, bytesToText } from 'unicrypto';
    
      const bytes = textToBytes("one two three"); // Uint8Array
      const text = bytesToText(bytes); // "one two three"

    Convert bytes to base64 and back

    import { encode64, encode64Short, decode64 } from 'unicrypto';
    
    const bytes = decode64("abc"); // Uint8Array
    const base64str = encode64(bytes); // String
    
    // short representation of base64 string
    const base64ShortString = encode64Short(bytes);

    Convert bytes to safe base58 and back

    import { encode58, decode58 } from 'unicrypto';
    
    const bytes = decode58("abc"); // Uint8Array
    const base58str = encode58(bytes); // String

    SHA

    Supports SHA256, SHA512, SHA1, SHA3(256, 384, 512)

    Get instant hash value for given byte array

    import { SHA } from 'unicrypto';
    
    const resultBytes1 = await SHA.getDigest('sha256', textToBytes('somevalue')); // Uint8Array

    Get hash value for large data

    import { SHA } from 'unicrypto';
    
    const sha512 = new SHA(512);
    
    await sha512.put(dataPart1); // dataPart1 is Uint8Array
    await sha512.put(dataPart2);
    // .....
    await sha512.put(dataPartFinal);
    
    const resultBytes = await sha512.get(); // Uint8Array

    Get hash value in HEX

    import { SHA } from 'unicrypto';
    
    const sha256 = new SHA(256);
    const hexResult = await sha256.get(textToBytes("one two three"), 'hex'); // String

    Sync usage

    import { SHA, unicryptoReady } from 'unicrypto';
    
    await unicryptoReady;
    
    const resultBytes1 = SHA.getDigestSync('sha256', textToBytes('somevalue')); // Uint8Array
    
    const sha512 = new SHA(512);
    
    sha512.putSync(dataPart1); // dataPart1 is Uint8Array
    sha512.putSync(dataPart2);
    // .....
    sha512.putSync(dataPartFinal);
    
    const resultBytes = sha512.getSync();

    HMAC

    import { HMAC  } from 'unicrypto';
    
    const data = textToBytes('a quick brown for his done something disgusting');
    const key = textToBytes('1234567890abcdef1234567890abcdef');
    
    const hmac = new HMAC('sha256', key);
    const result = await hmac.get(data) // Uint8Array

    Sync usage

    import { HMAC, unicryptoReady  } from 'unicrypto';
    
    await unicryptoReady;
    
    const data = textToBytes('a quick brown for his done something disgusting');
    const key = textToBytes('1234567890abcdef1234567890abcdef');
    
    const hmac = new HMAC('sha256', key);
    const result = hmac.getSync(data) // Uint8Array

    PBKDF2

    import { hexToBytes, pbkdf2, SHA } from 'unicrypto';
    
    const derivedKey = await pbkdf2('sha256', {
      rounds: 1, // number of iterations
      keyLength: 20,  // bytes length
      password: 'password',
      salt: hexToBytes('abc123')
    }); // Uint8Array

    RSA Pair, keys helpers

    Private key unpack

    import { PrivateKey, decode64, BigInteger } from 'unicrypto';
    
    const bossEncodedKey = decode64(keyPacked64);
    
    const privateKey2 = await PrivateKey.unpack(bossEncodedKey);
    
    // Read password-protected key
    const privateKey4 = await PrivateKey.unpack({
      bin: bossEncodedKey,
      password: "qwerty"
    });

    Public key unpack

    import { PublicKey, PrivateKey, decode64, BigInteger } from 'unicrypto';
    
    const bossEncodedKey = decode64(keyPacked64);
    const privateKey1 = await PrivateKey.unpack(bossEncodedKey);
    const publicKey1 = privateKey1.publicKey;
    
    const publicKey2 = await PublicKey.unpack(bossEncodedPublicKey);

    Public key fingerprint

    publicKey.fingerprint; // fingerprint (Uint8Array)

    Public key bit strength

    publicKey.getBitStrength(); // number

    Public key address

    publicKey.shortAddress.bytes;   // short address (Uint8Array)
    publicKey.shortAddress.string;   // short address (Uint8Array)
    publicKey.longAddress.bytes;    // long address (Uint8Array)
    publicKey.longAddress.string;    // long address (Uint8Array)

    Check if given address is valid

    import { PublicKey } from 'unicrypto';
    
    PublicKey.isValidAddress(publicKey.shortAddress) // true
    
    // accepts bytes representation of KeyAddress
    PublicKey.isValidAddress(publicKey.shortAddress.bytes) // true
    
    // accepts string representation of address too
    PublicKey.isValidAddress(publicKey.shortAddress.string) // true
    
    // check address by public key
    publicKey.shortAddress.isMatchingKey(publicKey) // true
    
    // check address by private key
    publicKey.longAddress.isMatchingKey(privateKey) // true

    Generate private key

    import { PrivateKey } from 'unicrypto';
    
    const options = { strength: 2048 };
    const priv = await PrivateKey.generate(options); // instance of PrivateKey

    Private(public) key - export

    import { PrivateKey } from 'unicrypto';
    
    const bossEncodedKey = decode64(keyPacked64);
    
    const key = await PrivateKey.unpack(bossEncodedKey);
    const keyPacked = await key.pack(); // Uint8Array
    const keyPackedProtected = await key.pack("somepassword"); // Uint8Array
    const keyPackedProtected1000 = await key.pack({ password: "qwerty", rounds: 1000 });
    
    const bossEncodedPublic = key.publicKey.packed;

    Get type of key package. There are 4 types of what key binary package may contain.

    AbstractKey.TYPE_PRIVATE - binary package of private key without password AbstractKey.TYPE_PUBLIC - binary package of public key without password AbstractKey.TYPE_PRIVATE_PASSWORD_V2 - binary package of private key with password (actual version) AbstractKey.TYPE_PRIVATE_PASSWORD_V1 - binary package of private key with password (deprecated version)

    import { AbstractKey } from 'unicrypto';
    
    const bossEncoded = await privateKey.pack("somepassword");
    
    AbstractKey.typeOf(bossEncoded) === AbstractKey.TYPE_PRIVATE_PASSWORD_V2 // true

    Key Address

    import { PublicKey, KeyAddress } from 'unicrypto';
    
    const pub; // PublicKey instance
    const shortAddress = pub.shortAddress;
    const longAddress = pub.longAddress;
    
    const shortAddressBytes = shortAddress.asBinary;
    const shortAddressString = shortAddress.asString;
    
    const address2 = new KeyAddress(shortAddressBytes);
    const address3 = new KeyAddress(shortAddressString);
    
    // is adress valid?
    const isValid = address2.isValid;
    const isValid2 = KeyAddress.checkAddress(shortAddressBytes);
    const isValid3 = KeyAddress.checkAddress(shortAddressString);
    
    // is address long?
    const isLong = address2.isLong;

    KEY INFO

    Contains information about Key and helper to match keys compatibility

    Supported algorithms: RSAPublic, RSAPrivate, AES256

    Supported PRF: HMAC_SHA1, HMAC_SHA256, HMAC_SHA512

    import { KeyInfo } from 'unicrypto';
    
    const keyInfo = new KeyInfo({
      algorithm: KeyInfo.Algorithm.AES256,
      tag: decode64("abc"), // Uint8Array
      keyLength: 32,        // Int
      prf: KeyInfo.PRF.HMAC_SHA256,
      rounds: 16000,        // number of iterations
      salt: decode64("bcd") // Uint8Array
    });

    Pack to BOSS

    const packed = keyInfo.pack(); // Uint8Array

    Read from BOSS

    // bossEncoded is Uint8Array
    const keyInfo = KeyInfo.unpack(bossEncoded); // KeyInfo

    Check that this key can decrypt other key

    const canDecrypt = keyInfo.matchType(otherKeyInfo); // boolean

    Derived key from password

    const derivedKey = await keyInfo.derivePassword("somepassword"); // Uint8Array

    SYMMETRIC KEY

    Symmetric key: main interface to the symmetric cipher. This implementation uses AES256 in CTR mode with IV to encrypt / decrypt.

    import { SymmetricKey } from 'unicrypto';
    
    // Creates random key (AES256, CTR)
    const symmetricKey = new SymmetricKey();
    
    // Creates key by derived key (Uint8Array) and it's info (KeyInfo)
    const symmetricKey2 = new SymmetricKey({
      keyBytes: derivedKey,
      keyInfo: keyInfo
    });
    
    // Creates key by derived key (Uint8Array)
    const symmetricKey2 = new SymmetricKey({
      keyBytes: derivedKey
    });
    
    // Creates key by password (String) and number of rounds (Int). Salt is optional
    // Uint8Array, null by default
    const symmetricKey3 = await SymmetricKey.fromPassword(password, rounds, salt);

    Pack symmetric key (get derived key bytes)

    import { SymmetricKey } from 'unicrypto';
    
    // Creates random key (AES256, CTR)
    const symmetricKey = new SymmetricKey();
    
    const derivedKey = symmetricKey.pack(); // Uint8Array

    Encrypt / decrypt data with AES256 in CRT mode with IV

    // data is Uint8Array
    const encrypted = symmetricKey.encrypt(data); // Uint8Array
    const decrypted = symmetricKey.decrypt(encrypted); // Uint8Array

    Encrypt / decrypt data with EtA using Sha256-based HMAC

    // data is Uint8Array
    const encrypted = await symmetricKey.etaEncrypt(data); // Uint8Array
    const decrypted = await symmetricKey.etaDecrypt(encrypted); // Uint8Array

    Sync usage

    import { SymmetricKey, unicryptoReady } from 'unicrypto';
    
    await unicryptoReady;
    // Creates random key (AES256, CTR)
    const symmetricKey = new SymmetricKey();
    
    // data is Uint8Array
    const encrypted = symmetricKey.etaEncryptSync(data); // Uint8Array
    const decrypted = symmetricKey.etaDecryptSync(encrypted); // Uint8Array

    RSA OAEP/PSS

    OAEP encrypt/decrypt

    You can pass hash types with instances or with string types. Supported types for SHA: sha1 sha256 sha384 sha512 sha512/256 sha3_256 sha3_384 sha3_512

    const privateKey; // some PrivateKey instance
    const publicKey = privateKey.publicKey;
    
    // encrypt data
    const data = decode64("abc123");
    const options = {
      seed: decode64("abcabc"), // optional, default none
      mgf1Hash: 'sha512', // optional, default SHA(256)
      oaepHash: 'sha512' // optional, default SHA(256)
    };
    const encrypted = await publicKey.encrypt(data, options);
    const decrypted = await privateKey.decrypt(encrypted, options);
    
    encode64(data) === encode64(decrypted); // true

    OAEP max encryption message length

    const privateKey; // some PrivateKey instance
    const publicKey = privateKey.publicKey;
    
    // encrypt data
    const options = {
      seed: decode64("abcabc"), // optional, default none
      mgf1Hash: 'SHA512', // optional, default SHA(256)
      oaepHash: 'SHA512' // optional, default SHA(256)
    };
    
    const maxLength = publicKey.encryptionMaxLength(options);

    OAEP default hash

    PublicKey.DEFAULT_OAEP_HASH // SHA1

    MGF1 default hash

    PublicKey.DEFAULT_MGF1_HASH // SHA1

    PSS sign/verify

    You can pass hash types with instances or with string types. Supported types for SHA: sha1 sha256 sha384 sha512 sha512/256 sha3_256 sha3_384 sha3_512

    const privateKey; // some PrivateKey instance
    const publicKey = privateKey.publicKey;
    
    const options = {
      salt: decode64("abcabc"), // optional
      saltLength: null, // optional, numeric
      mgf1Hash: 'sha512', // optional, default SHA(256)
      pssHash: 'sha512' // optional, default SHA(256)
    };
    
    const message = 'abc123';
    
    const signature = await privateKey.sign(message, options);
    const isCorrect = await publicKey.verify(message, signature, options);
    console.log(isCorrect); // true

    Extended signature

    Sign/verify

    import { ExtendedSignature } from 'unicrypto';
    
    const data = decode64("abcde12345");
    const privateKey; // some PrivateKey instance
    const publicKey = privateKey.publicKey;
    
    const signature = await privateKey.signExtended(data);
    const es = await publicKey.verifyExtended(signature, data);
    
    const isCorrect = !!es;
    console.log(es.created_at); // Date - signature created at
    console.log(es.key); // Uint8Array - PublicKey fingerprint
    console.log(ExtendedSignature.extractPublicKey(signature)); // PublicKey instance

    BOSS

    Encode/decode

    import { Boss } from 'unicrypto';
    
    const data = {
      a: decode64("abc")
      b: new Date(),
      c: [1, 2, 'test'],
      d: { a: 1 }
    };
    
    const encoded = Boss.dump(data); // Uint8Array
    const decoded = Boss.load(encoded); // original data

    Encode stream

    const writer = new Boss.Writer();
    
    writer.write(0);
    writer.write(1);
    writer.write(2);
    writer.write(3);
    
    const dump = writer.get(); // Uint8Array

    Decode stream

    const reader = new Boss.Reader(hexToBytes('00081018'));
    
    const arg1 = reader.read(); // 0
    const arg2 = reader.read(); // 1
    const arg3 = reader.read(); // 2
    const arg4 = reader.read(); // 3
    const arg5 = reader.read(); // undefined

    Typesript class registration

    You can pack and load instance of your class by implementing BossSerializable and BossDeserializable interfaces:

    import { BossSerializable, Boss } from 'unicrypto';
    
    interface MyClassSerialized {
      someValue: string;
    }
    
    class MyClass implements BossSerializable {
      someValue: string;
    
      constructor(someValue: string) { this.someValue = someValue; }
    
      serializeToBOSS() {
        return { someValue };
      }
    
      // You need to implement method according to BossDeserializable interface
      static deserializeFromBOSS(serialized: MyClassSerialized) {
        return new MyClass(serialized.someValue);
      }
    }
    
    Boss.register("MyClassType", MyClass);

    And you can pack/load instances like this:

    import { Boss } from 'unicrypto';
    
    const myInstance: MyClass;
    
    const packed = Boss.dump(myInstance); // Uint8Array
    const loaded = Boss.load(packed) as MyClass;

    AES

    Encrypt/decrypt

    import { AES } from 'unicrypto';
    
    const key = decode64("abc"); // 16 bytes for aes128, 32 bytes for aes256
    const message = textToBytes('some text');
    
    const aes256 = new AES(key);
    const encrypted = aes256.encrypt(message);   // Uint8Array
    const decrypted = aes256.decrypt(encrypted); // Uint8Array

    Diffie-Hellman

    import { DiffieHellman } from 'unicrypto';
    
    const primeLength = 1024;
    const generator = 2; // you can omit generator in options, it's 2 by default
    
    const alice = DiffieHellman.generate(primeLength, generator);
    const alicePrime = alice.prime; // base64 string
    const aliceGenerator = alice.generator; // base64 string
    const alicePublic = alice.generateKeys(); // base64 string
    
    // transfer prime, generator and publicKey to bob
    
    const bob = new DiffieHellman(alicePrime, aliceGenerator);
    const bobPublic = bob.generateKeys(); // base64 string
    const bobSecret = bob.computeSecret(alicePublic); // base64 string
    
    // transfer bobPublic back to alice
    
    const aliceSecret = alice.computeSecret(bobPublic); // aliceSecret equals bobSecret

    Create bundle

    Run in package root folder

    npm install
    npm run build

    In folder dist there will be universa.min.js, crypto.js, crypto.wasm. Also there will be *.LICENSE files.

    Running tests

    npm test

    Install

    npm i unicrypto

    DownloadsWeekly Downloads

    91

    Version

    1.11.0

    License

    (BSD-3-Clause OR GPL-2.0)

    Unpacked Size

    10.8 MB

    Total Files

    79

    Last publish

    Collaborators

    • anzhu