# Cryptographic algorithm crypto

# Interface Declaration

{ "name": "system.crypto" }

# Import Module

import crypto from '@system.crypto' 
// or 
const crypto = require('@system.crypto')

# Interface Definition

# crypto.hashDigest(OBJECT)

Create a hash digest of the data.

# Parameters:

Parameter Type Required Description
data String | Uint8Array No Content to be computed. Either data or uri must be provided.
uri String No File address to be computed. Either data or uri must be provided.
algo String No Algorithm. Default: SHA256
Options: MD5, SHA1, SHA256, SHA512

# Return Value:

Type Description
String Computed digest content

# Example:

const digest = crypto.hashDigest({
  data: 'hello',
  algo: 'MD5'
})

# crypto.hmacDigest(OBJECT)

Create an encrypted HMAC digest.

# Parameters:

Parameter Type Required Description
data String Yes Data to be computed.
algo String No Algorithm. Default: SHA256
Options: MD5, SHA1, SHA256, SHA512
key String Yes Key
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Return Value Object:

Parameter Type Description
data String Digest

# Example:

crypto.hmacDigest({
  data: 'hello',
  algo: 'SHA256',
  key: 'a secret',
  success: function(res) {
    console.log(`### crypto.hmacDigest success:`, res.data)
  },
  fail: function(data, code) {
    console.log(`### crypto.hmacDigest fail ### ${code}: ${data}`)
  }
})

# crypto.sign(OBJECT)

Used to generate a signature.

# Parameters:

Parameter Type Required Description
data String | Uint8Array No Text to be signed. Either data or uri must be provided.
uri String No File address to be signed. Either data or uri must be provided.
algo String No Signature algorithm. Default: 'RSA-SHA256'
Options: RSA-MD5, RSA-SHA1, RSA-SHA256, RSA-SHA512
privateKey String Yes Private key
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Return Value Object:

Parameter Type Description
data String | Uint8Array If input is a string, returns a base64-encoded string; otherwise, returns Uint8Array; if only uri is passed, returns string by default.

# Example:

crypto.sign({
  data: 'hello',
  algo: 'RSA-SHA256',
  privateKey: 'a secret',
  success: function(res) {
    console.log(`### crypto.sign success:`, res.data)
  },
  fail: function(data, code) {
    console.log(`### crypto.sign fail ### ${code}: ${data}`)
  }
})

# crypto.verify(OBJECT)

Used to verify a signature.

# Parameters:

Parameter Type Required Description
data String | Uint8Array No Text to be signed. Either data or uri must be provided.
uri String No File address to be signed. Either data or uri must be provided.
algo String No Signature algorithm. Default: 'RSA-SHA256'
Options: RSA-MD5, RSA-SHA1, RSA-SHA256, RSA-SHA512
signature String | Uint8Array Yes Signature
publicKey String Yes Public key
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Return Value Boolean:

Type Description
Boolean Verification result. True if passed, false if not.

# Example:

crypto.verify({
  data: 'hello',
  algo: 'RSA-SHA256',
  publicKey: 'public key',
  signature: 'signature',
  success: function(data) {
    console.log(`### crypto.verify success:`, data)
  },
  fail: function(data, code) {
    console.log(`### crypto.verify fail ### ${code}: ${data}`)
  }
})

# crypto.encrypt(OBJECT)

Encrypt data.

# Parameters:

Parameter Type Required Description
data String | Uint8Array Yes Data to be encrypted
string: Plaintext to be encrypted, default 'utf-8' encoded
Uint8Array: Use hexadecimal or base64 encoded formats must convert to this type
algo String No Encryption algorithm. Default: RSA
Options: RSA, AES
key String | Uint8Array Yes Key used for encryption
AES encryption mode supports 128-bit (Uint8Array 16 bytes) / 192-bit (Uint8Array 24 bytes) / 256-bit (Uint8Array 32 bytes) keys
String: Base64 encoded string; RSA encryption requires PEM format string
Uint8Array: Hexadecimal or other encoded formats can convert to this type
options Object No Encryption parameters
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# RSA Parameter options:

Parameter Type Required Description
transformation String No RSA algorithm encryption mode and padding. Supports "RSA/None/PKCS1Padding"

# AES Parameter options:

Parameter Type Required Description
transformation String No AES algorithm encryption mode and padding. Default: "AES/CBC/PKCS7Padding"
See transformation support details
iv String No AES initial vector, base64 encoded string
AES CBC/ECB mode:
Default value is key
Default value extraction method:
1. If key is a string, decode the string and use byte content as iv
2. If key is uint8array, use byte content as iv
AES-CCM mode:
No default value
ivOffset Number No AES initial vector offset, integer, default value is 0
ivLen Number No AES initial vector byte length, integer
AES CBC/ECB mode: Default value is keyLen 16
AES-CCM mode: No default value
aad String | Uint8Array No AES-CCM additional data for encryption, provides additional authentication information during encryption
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type
tagLen Number No AES-CCM authentication tag length
Must be even, valid range 4~16
Default value is 4

# transformation Support Details:

Padding Mode / Encryption Mode CBC ECB CCM
PKCS5Padding AES/CBC/PKCS5Padding AES/ECB/PKCS5Padding -
PKCS7Padding AES/CBC/PKCS7Padding AES/ECB/PKCS7Padding -
NoPadding AES/CBC/NoPadding - AES/CCM/NoPadding

# success Return Value Object:

Parameter Type Description
data String | Uint8Array Encrypted data
If input data is a string, returns base64 encoded string; otherwise, returns Uint8Array
tag String | Uint8Array AES-CCM encryption returns authentication tag
If input data is a string, returns base64 encoded string; otherwise, returns Uint8Array

# Example:

// AES-CBC Encryption
crypto.encrypt({
  // Text content to be encrypted
  data: 'hello',
  // Encryption public key after base64 encoding
  key: crypto.btoa('KEYKEYKEYKEYKEYK'),
  algo: 'AES',
  success: function(res) {
    console.log(`### crypto.encrypt success:`, res.data)
  },
  fail: function(data, code) {
    console.log(`### crypto.encrypt fail ### ${code}: ${data}`)
  }
})
// AES-CCM Encryption
crypto.encrypt({
  // Text content to be encrypted
  data: 'hello',
  // Encryption public key after base64 encoding
  key: crypto.btoa('KEYKEYKEYKEYKEYK'),
  algo: 'AES',
  options: {
    transformation: 'AES/CCM/NoPadding',
    iv: crypto.btoa('iv_info'),
    aad: crypto.btoa('Associated Data'),
    tagLen: 16
  },
  success: function(res) {
    console.log('### crypto.encrypt success:', res.data, res.tag)
  },
  fail: function(data, code) {
    console.log('### crypto.encrypt fail ### ', code , data)
  }
})

# crypto.decrypt(OBJECT)

Decrypt data.

# Parameters:

Parameter Type Required Description
data String | Uint8Array Yes Data to be decrypted
String: Data to be decrypted, default base64 encoded
Uint8Array: Hexadecimal or other encoded formats must convert to this type
algo String No Decryption algorithm. Default: RSA
Options: RSA, AES
key String | Uint8Array Yes Key used for encryption
AES encryption mode supports 128-bit (Uint8Array 16 bytes) / 192-bit (Uint8Array 24 bytes) / 256-bit (Uint8Array 32 bytes) keys
String: Base64 encoded string; RSA encryption requires PEM format string
Uint8Array: Hexadecimal or other encoded formats can convert to this type
options Object No Decryption parameters
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# RSA Parameter options:

Parameter Type Required Description
transformation String No RSA algorithm encryption mode and padding. Supports "RSA/None/PKCS1Padding"

# AES Parameter options:

Parameter Type Required Description
transformation String No AES algorithm encryption mode and padding. Default: "AES/CBC/PKCS7Padding"
See transformation support details
iv String No AES initial vector, base64 encoded string
AES CBC/ECB mode:
Default value is key
Default value extraction method:
1. If key is a string, decode the string and use byte content as iv
2. If key is uint8array, use byte content as iv
AES-CCM mode:
No default value
ivOffset Number No AES initial vector offset, integer, default value is 0
ivLen Number No AES initial vector byte length, integer
AES CBC/ECB mode: Default value is keyLen 16
AES-CCM mode: No default value
aad String | Uint8Array No AES-CCM additional data for encryption, provides additional authentication information during encryption
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type
tag String | Uint8Array No AES-CCM authentication tag, used to verify data integrity and authenticity
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type

# transformation Support Details:

Padding Mode / Encryption Mode CBC ECB CCM
PKCS5Padding AES/CBC/PKCS5Padding AES/ECB/PKCS5Padding -
PKCS7Padding AES/CBC/PKCS7Padding AES/ECB/PKCS7Padding -
NoPadding AES/CBC/NoPadding - AES/CCM/NoPadding

# success Return Value Object:

Parameter Type Description
data String | Uint8Array Decrypted data
If input data is a string, returns decrypted plaintext. If decrypted content cannot be converted to a utf-8 string, an error occurs (CODE: 200); otherwise, returns Uint8Array

# Example:

// AES-CBC Decryption
crypto.decrypt({
  // Content to be decrypted
  data: 'WB96uM08PfYIHu5G1p6YwA==',
  // Encryption public key after base64 encoding
  key: crypto.btoa('KEYKEYKEYKEYKEYK'),
  algo: 'AES',
  success: function(res) {
    console.log(`### crypto.decrypt success:`, res.data)
  },
  fail: function(data, code) {
    console.log(`### crypto.decrypt fail ### ${code}: ${data}`)
  }
})
// AES-CCM Decryption
crypto.decrypt({
  // Text content to be decrypted
  data: '9KFkqz8=',
  // Encryption public key after base64 encoding
  key: crypto.btoa('KEYKEYKEYKEYKEYK'),
  algo: 'AES',
  options: {
    transformation: 'AES/CCM/NoPadding',
    iv: crypto.btoa('iv_info'),
    aad: crypto.btoa('Associated Data'),
    tag: 'kHX6EGYOEvKwA0PS79TAUQ=='
  },
  success: function(res) {
    console.log('### crypto.decrypt success:', res.data)
  },
  fail: function(data, code) {
    console.log('### crypto.decrypt fail ### ', code , data)
  }
})

# crypto.btoa(STRING)

Create a base-64 encoded ASCII string from a String object, where each character in the string is treated as a binary data byte.

# Parameters:

Type Required Description
String Yes Text to be encoded

# Return Value String:

Type Description
String Encoded result

# Example:

const encodeData = crypto.btoa('hello')

# crypto.atob(STRING)

Decode a base-64 encoded string.

# Parameters:

Type Required Description
String Yes Text to be decoded

# Return Value String:

Type Description
String Decoded result

# Example:

const encodeString = crypto.btoa('hello')
const res = crypto.atob(encodeString)

# crypto.hkdf(OBJECT)

Create a derived key.

# Parameters:

Parameter Type Required Description
algo String No Digest algorithm to use. Default: SHA256
Options: SHA256, SHA512
key String | Uint8Array Yes Key material, source key
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type
salt String | Uint8Array Yes Salt value
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type
info String | Uint8Array Yes Additional information value. Length cannot exceed 1024 bytes
String: Base64 encoded string
Uint8Array: Hexadecimal or other encoded formats must convert to this type
keyLen Number Yes Length of the key to be generated, in bytes
Maximum allowed value is 255 times the number of bytes produced by the selected digest function (e.g., sha256 produces 32-byte hashes, maximum HKDF output is 8160 bytes; sha512 produces 64-byte hashes, maximum HKDF output is 16320 bytes)
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Return Value Object:

Parameter Type Description
data Uint8Array Derived key result

# Example:

Conversion utility functions:

// Convert hexadecimal string to uint8Array
function hexToUint8Array(hex) {
  // Remove possible prefix 0x
  hex = hex.replace(/^0x/, '');

  // If length is odd, prepend zero
  if (hex.length % 2 !== 0) {
    hex = '0' + hex;
  }

  // Check for invalid characters
  if (/[^0-9A-Fa-f]/.test(hex)) {
    throw new Error('Invalid hex string: contains non-hexadecimal characters.');
  }

  // Calculate length of byte array
  const byteLength = hex.length / 2;
  const bytes = new Uint8Array(byteLength);

  // Convert each pair of hexadecimal characters to a byte
  for (let i = 0; i < byteLength; i++) {
    const byteString = hex.substr(i * 2, 2);
    bytes[i] = parseInt(byteString, 16);
  }

  return bytes;
}

// Convert uint8Array to hexadecimal string
function uint8ArrayToHex(bytes) {
  if (!(bytes instanceof Uint8Array)) {
    throw new TypeError('Expected input to be an instance of Uint8Array.');
  }

  // Convert each byte to a two-digit hexadecimal string
  return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
}

/**
 * Create Base64 decoding table (outside function, created only once)
 */
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const base64Map = new Map();
for (let i = 0; i < base64Chars.length; i++) {
  base64Map.set(base64Chars[i], i);
}

// Convert Base64 encoded string to Uint8Array
function base64ToUint8Array(base64) {
  // Remove possible whitespace characters from Base64 string
  base64 = base64.trim();

  // Base64 length must be a multiple of 4
  if (base64.length % 4 !== 0) {
    throw new Error('Invalid Base64 string length');
  }

  // Calculate number of padding characters
  const padding = base64.endsWith('==') ? 2 : (base64.endsWith('=')) ? 1 : 0;

  // Calculate output array length
  const outputLength = (base64.length * 3) / 4 - padding;
  const output = new Uint8Array(outputLength);

  let buffer = 0;
  let bufferLength = 0;
  let outputIndex = 0;

  for (let i = 0; i < base64.length; i++) {
    const char = base64[i];
    if (char === '=') continue;

    const value = base64Map.get(char);
    if (value === undefined) {
      throw new Error('Invalid Base64 character');
    }

    buffer = (buffer << 6) | value;
    bufferLength += 6;

    if (bufferLength >= 8) {
      bufferLength -= 8;
      output[outputIndex++] = (buffer >> bufferLength) & 0xFF;
    }
  }

  return output;
}

utf8-String

// Define input parameters
const key = crypto.btoa('initialkeymaterial'); // Initial key material
const salt = crypto.btoa('salt'); // Salt
const info = crypto.btoa('info'); // Additional information
const keyLen = 32; // Length of the key to be generated

crypto.hkdf({
  key,
  salt,
  info,
  keyLen,
  success(res) {
  try {
      const derivedKey = res.data
      console.log('Derived Key (hex): ', uint8ArrayToHex(derivedKey)); 
      // Derived key (hex): 00e02dcb12e9ca343bc57a1441ccd76a845d9f80e716cfc5a61f8daea81e57ec
    } catch (e) {
      console.error('Decoding error:', e.message);
    }
  },
  fail(msg, code) {
    console.log('crypto.hkdf error:', msg, code);
  }
})

Hexadecimal

// Define input parameters
const key = hexToUint8Array('696e697469616c6b65796d6174657269616c'); // Initial key material initialkeymaterial hex
const salt = hexToUint8Array('73616c74'); // Salt salt hex
const info = hexToUint8Array('696e666f'); // Additional information info hex
const keyLen = 32; // Length of the key to be generated

crypto.hkdf({
  key,
  salt,
  info,
  keyLen,
  success(res) {
    try {
      const derivedKey = res.data
      console.log('Derived Key (hex): ', uint8ArrayToHex(derivedKey)); 
      // Derived key (hex): 00e02dcb12e9ca343bc57a1441ccd76a845d9f80e716cfc5a61f8daea81e57ec
    } catch (e) {
      console.error('Decoding error:', e.message);
    }
  },
  fail(msg, code) {
    console.log('crypto.hkdf error:', msg, code);
  }
})

base64

// Define input parameters
const key = 'aW5pdGlhbGtleW1hdGVyaWFs'; // Initial key material initialkeymaterial base64
const salt = 'c2FsdA=='; // Salt salt base64
const info = 'aW5mbw=='; // Additional information info base64
const keyLen = 32; // Length of the key to be generated

crypto.hkdf({
  key,
  salt,
  info,
  keyLen,
  success(res) {
    try {
      const derivedKey = res.data
      console.log('Derived Key (hex): ', uint8ArrayToHex(derivedKey)); 
      // Derived key (hex): 00e02dcb12e9ca343bc57a1441ccd76a845d9f80e716cfc5a61f8daea81e57ec
    } catch (e) {
      console.error('Decoding error:', e.message);
    }
  },
  fail(msg, code) {
    console.log('crypto.hkdf error:', msg, code);
  }
})

# crypto.createECDH(String)

ECDH (Elliptic-curve Diffie-Hellman) is a key exchange protocol based on elliptic curve cryptography, used to securely generate a shared key. Create an Elliptic Curve Diffie-Hellman (ECDH) key exchange object using the curve name.

# Parameters:

Type Required Description
String Yes Elliptic curve name
Options: secp256r1

# Return Value:

Type Description
<ECDH> Elliptic Curve Diffie-Hellman (ECDH) key exchange instance object

# Example:

// Create an ECDH object using the secp256r1 curve
const ecdh = crypto.createECDH('secp256r1');

# ECDH

Elliptic Curve Diffie-Hellman (ECDH) key exchange object.

# ecdh.generateKeys(Object)

Generate a key pair.

# Parameters:

Parameter Type Required Description
encoding String No Defines the encoding format of the returned public key. Default: buffer
Supports base64, hex, buffer
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Return Value Object:

Parameter Type Description
publicKey String | Uint8Array Public key from the generated key pair, returned in different types based on the set public key encoding format
base64, hex return String, buffer returns Uint8Array

# Example:

// Create an ECDH object using the secp256r1 curve
const ecdh = crypto.createECDH('secp256r1');

ecdh.generateKeys({
  encoding: 'base64',
  success: function(res) {
    console.log('### generateKeys success publicKey: ', res.publicKey)
  },
  fail: function(data, code) {
    console.log('### generateKeys fail ### ', code , data)
  }
})

# ecdh.getPrivateKey(encoding)

Get the private key from the generated key pair.

# Parameters:

Parameter Type Required Description
encoding String No Defines the encoding format of the returned private key, default value is buffer
Supports: base64, hex, buffer

# Return Value:

Type Description
String | Uint8Array Returns the corresponding type based on the encoding parameter:
base64, hex encoding return String
buffer encoding returns Uint8Array

# Example:

const privateKey = ecdh.getPrivateKey('hex');

console.log('privateKey hex: ', privateKey);

# ecdh.getPublicKey(encoding)

Get the public key from the generated key pair.

# Parameters:

Parameter Type Required Description
encoding String No Defines the encoding format of the returned public key, default value is buffer
Supports: base64, hex, buffer

# Return Value:

Type Description
String | Uint8Array Returns the corresponding type based on the encoding parameter:
base64, hex encoding return String
buffer encoding returns Uint8Array

# Example:

const publicKey = ecdh.getPublicKey('hex');

console.log('publicKey hex: ', publicKey);

# ecdh.setPrivateKey(Object)

Set the private key, will attempt to generate the associated public key with the set private key.

# Parameters:

Parameter Type Required Description
privateKey String | Uint8Array Yes Private key value to be set:
String type: Must be a PEM format string
Uint8Array type: Byte array converted from other formats (such as hexadecimal)
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# Example:

// Create an ECDH object using the secp256r1 curve
const ecdh = crypto.createECDH('secp256r1');
const key = '-----BEGIN EC PRIVATE KEY-----\n' +
  'MHcCAQEEIB6c3X+2K7f3B3XXo4KPbQv0YP5ddTx0/zh8S2C0AXfkoAoGCCqGSM49\n' +
  'AwEHoUQDQgAEp4K5yOimfJxvZ0fX0TQh4h2d7G2JDo5pujpJwZmLdrX7vF7Lp1HU\n' +
  'AoIttMRXxktBjdvY9m7hfRp/Uu9paU7X3Q==\n' +
  '-----END EC PRIVATE KEY-----'

ecdh.setPrivateKey({
  privateKey: key,
  success: () => {
    console.log('#### privateKey: ', ecdh.getPrivateKey('base64'))
    console.log('#### publicKey: ', ecdh.getPublicKey('base64'))
  },
  fail: (data, code) => {
    console.log('### setPrivateKey fail ### ', code , data)
  }
})

# ecdh.computeSecret(Object)

Compute the shared secret using the provided public key.

# Parameters:

Parameter Type Required Description
otherPublicKey String | Uint8Array Yes Other party's public key used to compute the shared secret
inputEncoding String No Encoding format of the provided public key, default buffer
Supports: base64, hex, buffer
outputEncoding String No Encoding format of the returned computed shared secret, default buffer
Supports: base64, hex, buffer
success Function No Success callback
fail Function No Failure callback
complete Function No Completion callback

# success Callback Return Object Value:

Parameter Type Description
shareKey String | Uint8Array Generated computed shared secret

# Example:

async function fn() {
  try {
    // Create Alice's ECDH object and generate key pair
    const alice = crypto.createECDH('secp256r1');
    // Create Bob's ECDH object and generate key pair
    const bob = crypto.createECDH('secp256r1');
    // Interface handling as promise
    const aliceGen = promisify(alice.generateKeys)
    const bobGen = promisify(bob.generateKeys)
    const aliceCompute = promisify(alice.computeSecret)
    const bobCompute = promisify(bob.computeSecret)

    await aliceGen()
    await bobGen()

    // Get Alice's and Bob's public keys
    const alicePublicKey = alice.getPublicKey('hex');
    const bobPublicKey = bob.getPublicKey('hex');

    // Alice computes the shared secret using Bob's public key
    const { shareKey: aliceSharedSecret } = await aliceCompute(bobPublicKey, 'hex', 'hex');

    // Bob computes the shared secret using Alice's public key
    const { shareKey: bobSharedSecret } = await bobCompute(alicePublicKey, 'hex', 'hex');

    // Print results
    console.log('Alice Public Key:', alicePublicKey);
    console.log('Bob Public Key:', bobPublicKey);
    console.log('Alice Shared Secret:', aliceSharedSecret);
    console.log('Bob Shared Secret:', bobSharedSecret);

    // Verify shared secrets match
    if (aliceSharedSecret === bobSharedSecret) {
      console.log('Shared secrets match!');
    } else {
      console.log('Shared secrets do not match!');
    }
  } catch (e) {
   console.log('error: ', e)
  }
}

fn()

# Support Details

Device Product Description
Xiaomi S1 Pro Sports Health Watch Not supported
Xiaomi Band 8 Pro Not supported
Xiaomi Band 9 / 9 Pro Not supported
Xiaomi Watch S3 Supported
Redmi Watch 4 Not supported
Xiaomi Wrist ECG Blood Pressure Monitor Not supported
Xiaomi Band 10 Not supported
Xiaomi Watch S4 Supported
REDMI Watch 5 Supported