Basic Definitions

The goal of this chapter is to define used functions related to cryptography and data manipulation. The definitions crafted in this chapter are then used in pseudo-codes in documentation. You can learn more about actual implementation of following functions in the “Implementation Notes” section.

Cryptographic Functions

The following basic cryptography algorithms and parameters are used in the PowerAuth cryptography description:

AES Symmetric Encryption

A symmetric key encryption algorithm. The AES algorithm is used with 256-bit keys.

  • CTR mode is used as the encryption primitive inside AEAD and for unauthenticated key wrapping (UKE).
  • GCM mode is used for local vault.

Encryption

Encrypt bytes using symmetric key with given initialization vector and AES/CTR/NoPadding transformation:

byte[] encrypted = AES.encrypt(byte[] original, byte[] iv, SecretKey key);

Encrypt bytes using symmetric key with given initialization vector and given cipher transformation:

byte[] encrypted = AES.encrypt(byte[] original, byte[] iv, SecretKey key, String transformation);

Decryption

Decrypt bytes using symmetric key with given initialization vector and AES/CTR/NoPadding transformation:

byte[] original = AES.decrypt(byte[] encrypted, byte[] iv, SecretKey key);

Decrypt bytes using symmetric key with given initialization vector and given cipher transformation:

byte[] original = AES.decrypt(byte[] encrypted, byte[] iv, SecretKey key, String transformation);

Generic KDF

Keys are derived from an original secret using hierarchical string labels to guarantee that derived keys are never reused for different purposes.

The following method is used to derive a key from original secret value:

SecretKey derivedKey = KDF.derive(SecretKey sourceKey, String label);

If raw bytes are required:

byte[] bytes = KDF.deriveBytes(byte[] secret, String label, int length);

Password KDF

An algorithm for key stretching, converts a short password into long key by performing KMAC-based derivation on the original data.

The following method will stretch the password using provided salt:

SecretKey expandedKey = KDF.derivePassword(byte[] password, byte[] salt);

ECDSA Signatures

An algorithm for elliptic curve based signatures, uses SHA-384 hash algorithm and P-384 elliptic curve. It defines the following operations:

Data Signing

Compute signature of given data with a private key.

byte[] signature = ECDSA.sign(byte[] data, PrivateKey privateKey);

Signature Verification

Verify the signature for given data using a given public key.

boolean isValid = ECDSA.verify(byte[] data, byte[] signature, PublicKey publicKey);

ML-DSA Signatures

Post-quantum signature algorithm MLDSA can be used for post-quantum signatures.

A ML-DSA key pair is generated with a specified variant of the algorithm:

KeyPair keyPair = MLDSA.generateKeyPair(String algorithm); // ML-DSA-65 or ML-DSA-87

A signature is created by signing the message raw bytes by private key from the ML-DSA keypair:

byte[] signature = MLDSA.sign(PrivateKey privateKey, byte[] message);

A signature is verified for message raw bytes using public key from the ML-DSA keypair:

boolean isValid = MLDSA.verify(PublicKey publicKey, byte[] message, byte[] signature);

KEM / ECDH Key Agreement

Generate KEM key pair:

KeyPair keyPair = KEM.generateKeyPair();

Encapsulation:

Pair<SecretKey, byte[]> result = KEM.encapsulate(PublicKey publicKeyB);

Decapsulation:

SecretKey secretKey = KEM.decapsulate(PrivateKey privateKeyA, byte[] ciphertext);

The resulting SecretKey represents the shared secret between parties.

SharedSecret Interface

KEM is wrapped into a SharedSecret abstraction used by protocol flows.

Client derives shared secret from server response:

SecretKey secretKey = SharedSecret.computeSharedSecret(
        SharedSecretClientContext context,
        SharedSecretResponse response
);

The interface internally uses SharedSecretRequest, SharedSecretResponse, and SharedSecretClientContext objects.

UKE (Unauthenticated Key Encapsulation)

Primitive used for protecting factor-related keys (for example knowledge or biometry).

UKE provides confidentiality without authentication and intentionally avoids authenticated encryption to prevent offline brute-force oracles on low-entropy secrets (such as PINs).

Wrap key using provided KEK:

byte[] wrapped = UKE.wrap(SecretKey key, SecretKey kek);

Unwrap key using provided KEK:

SecretKey key = UKE.unwrap(byte[] wrapped, SecretKey kek);

AEAD

Authenticated encryption used for End-To-End encryption and key protection.

A 12-byte unique nonce is used. For request/response flows, two independent nonces are used (one for request, one for response).

Seal

byte[] ciphertext = AEAD.seal(
        SecretKey key,
        byte[] keyContext,
        byte[] nonce,
        byte[] associatedData,
        byte[] plaintext
);

The nonce is prepended to the ciphertext during encryption.

Open

byte[] plaintext = AEAD.open(
        SecretKey key,
        byte[] keyContext,
        byte[] associatedData,
        byte[] ciphertext
);

The nonce is automatically extracted during decryption from ciphertext.

Extract Nonce

byte[] nonce = AEAD.extractNonce(byte[] ciphertext);

AEAD internally derives encryption and authentication keys via KDF and authenticates data with KMAC-256.

KDF

A key derivation function used to derive symmetric keys from a given master key.

A hierarchical KMAC-based derivation is used with string labels.

To obtain a key derived from a master key using a provided label:

SecretKey derivedKey = KDF.derive(SecretKey masterKey, String label);

Example:

SecretKey KDK_UTILITY = KDF.derive(masterKey, "util");
SecretKey KEY_STATUS = KDF.derive(KDK_UTILITY, "util/mac/status");

Helper Functions

These functions are used in the pseudo-codes:

Key Generators

Generate Random Key Pair

Generate a new EC key pair for the P-384 elliptic curve.

KeyPair keyPair = ECKeyGenerator.randomKeyPair("P-384");

Key Conversion Utilities

Convert Private Key to Bytes

Get bytes from the EC private key by encoding the D value (the number defining the EC private key).

byte[] privateKeyBytes = KeyConversion.getBytes(PrivateKey privKey);

Convert Bytes to Private Key

Get EC key pair private key by decoding the bytes into the original D value (the number defining the EC private key).

PrivateKey privateKey = KeyConversion.privateKeyFromBytes(byte[] privKeyBytes);

Convert Public Key to Bytes

Get bytes from the EC public key by encoding the Q value (the point defining the EC public key).

byte[] publicKeyBytes = KeyConversion.getBytes(PublicKey pubKey);

Convert Bytes to Public Key

Get EC public key by decoding the bytes into the original Q value (the point defining the EC public key).

PublicKey publicKey = KeyConversion.publicKeyFromBytes(byte[] pubKeyBytes);

Convert Secret Key to Bytes

Get bytes from the symmetric key (using the getEncoded method).

byte[] secretKeyBytes = KeyConversion.getBytes(SecretKey secretKey);

Convert Bytes to Secret Key

Create a symmetric key using provided bytes.

SecretKey secretKey = KeyConversion.secretKeyFromBytes(byte[] secretKeyBytes);

Random Data Generators

Generate Random Data

Generate N random bytes using a secure random generator.

byte[] randomBytes = Generator.randomBytes(int N);

Generate Random Base32 String

Generate string in Base32 encoding with N characters using a secure random generator.

String randomBase32 = Generator.randomBase32String(int N);

Generate Random UUID

Generate a new UUID level 4 and return it in string representation.

String uuid = Generator.randomUUID();

Generate Random Activation Code

Generate a new ACTIVATION_CODE. See Activation Code for more details.

String code = Generator.randomActivationCode();

Build Activation Code With Random Bytes

Function return an activation code from given random data.

String code = Generator.buildActivationCode(byte[10] randomBytes);

MAC Functions

KMAC-256

Compute KMAC-256 signature for given message using provided symmetric key.

byte[] signature = Mac.kmac256(SecretKey key, byte[] message, int outLength, String custom);

Hashing Functions

SHA3

Compute SHA3 hash of a given input.

byte[] hash = Hash.sha3_256(byte[] original);
byte[] hash = Hash.sha3_384(byte[] original);

Note: SHA-384 (SHA-2) is used internally by ECDSA signatures for compatibility reasons.

Password Hashing

Compute Password Hash

Compute Argon2 hash for given password. Hash is stored in Modular Crypt Format.

String hash = PasswordHash.hash(byte[] password);

Verify Password Hash

Verify password against Argon2 hash stored in Modular Crypt Format.

boolean matches = PasswordHash.verify(byte[] password, String hash);

Utility Functions

Obtain Zero Byte Array

Generate buffer with N zero bytes.

byte[] zeroBytes = ByteUtils.zeroBytes(int N);

Truncate Array

Get last N bytes of given byte array.

byte[] truncatedBytes = ByteUtils.truncate(byte[] bytes, int N);

Get Numbers From Byte Array

Get integer value from big endian encoded byte array.

int integer = ByteUtils.getInt(byte[4] bytes);

Get long value from big endian encoded byte array.

long value = ByteUtils.getLong(byte[8] bytes);

Encode Primitive Types To Byte Array

Encode short value into byte array in big endian order.

byte[] encoded = ByteUtils.encode(short n);

Encode int value into byte array in big endian order.

byte[] encoded = ByteUtils.encode(int n);

Encode long value into byte array in big endian order.

byte[] encoded = ByteUtils.encode(long n);

Encode string into sequence of bytes with UTF-8 encoding.

byte[] encoded = ByteUtils.encode(String s);

Concatenate Data

Concatenate two or more byte arrays.

byte[] ByteUtils.concat(byte[]... args) {
    byte[] result = new byte[0];
    for (byte[] component : args) {
        if (component != null && component.length > 0) {
          byte[] tmp = new byte[result.length + component.length];
          ByteUtils.copy(result, 0, tmp, 0, result.length);
          ByteUtils.copy(component, 0, tmp, result.length, component.length);
          result = tmp;
        }
    }
    return result;
}

Concatenate multiple byte array elements and prepend the length of each element.

byte[] ByteUtils.concatWithSizes(byte[]... args) {
    byte[] result = new byte[0];
    for (byte[] component : args) {
        if (component != null) {
            result = ByteUtils.concat(result, ByteUtils.encode(component.length), component);
        } else {
            result = ByteUtils.concat(result, ByteUtils.encode((int)0));
        }
    }
    return result;
}

Concatenate multiple string elements and prepend the length of each element.

byte[] ByteUtils.concatWithSizes(String... args) {
    byte[] result = new byte[0];
    for (String component : args) {
        if (component != null) {
            byte[] componentBytes = ByteUtils.encode(component);
            result = ByteUtils.concat(result, ByteUtils.encode(componentBytes.length), componentBytes);
        } else {
            result = ByteUtils.concat(result, ByteUtils.encode((int)0));
        }
    }
    return result;
}

Convert 32b Array to 16b

Converts 32b long byte array to 16b long array by xor-ing the first 16b with the second 16b, byte-by-byte.

byte[] result = ByteUtils.convert32Bto16B(byte[] bytes32);

Obtain Sub-Array

Obtain subarray of a byte array, starting with index startIndex with a given length.

byte[] result = ByteUtils.subarray(byte[] bytes, int startIndex, int length);

Copy Arrays

Copies length of bytes from the specified source array of bytes, beginning at the specified position, to the specified position of the destination array.

ByteUtils.copy(byte[] source, int sourcePosition, byte[] destination, int destinationPosition, int length);

Current Time

Get UNIX timestamp in milliseconds, since 1.1.1970.

long timestamp = Time.getTimestamp();
Last updated on Feb 02, 2026 (09:34) Edit on Github Send Feedback
Search

develop

PowerAuth Java Crypto